pax_global_header00006660000000000000000000000064143574521370014525gustar00rootroot0000000000000052 comment=8e98d8c18711c6587dce8946985c609a6e2a641b golang-github-crowdsecurity-machineid-1.0.3/000077500000000000000000000000001435745213700211005ustar00rootroot00000000000000golang-github-crowdsecurity-machineid-1.0.3/.gitignore000066400000000000000000000004571435745213700230760ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof .DS_Store .vscode result /machineid golang-github-crowdsecurity-machineid-1.0.3/LICENSE.md000066400000000000000000000020721435745213700225050ustar00rootroot00000000000000 The MIT License (MIT) Copyright (c) 2017 Denis Brodbeck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-crowdsecurity-machineid-1.0.3/README.md000066400000000000000000000120561435745213700223630ustar00rootroot00000000000000# machineid provides support for reading the unique machine id of most host OS's (without admin privileges) ![Image of Gopher 47](logo.png) … because sometimes you just need to reliably identify your machines. ## Main Features * Cross-Platform (tested on Win7+, Debian 8+, Ubuntu 14.04+, OS X 10.6+, FreeBSD 11+) * No admin privileges required * Hardware independent (no usage of MAC, BIOS or CPU — those are too unreliable, especially in a VM environment) * IDs are unique[1](#unique-key-reliability) to the installed OS ## Installation Get the library with ```bash go get github.com/crowdsecurity/machineid ``` You can also add the cli app directly to your `$GOPATH/bin` with ## Usage ```golang package main import ( "fmt" "log" "github.com/crowdsecurity/machineid" ) func main() { id, err := machineid.ID() if err != nil { log.Fatal(err) } fmt.Println(id) } ``` Or even better, use securely hashed machine IDs: ```golang package main import ( "fmt" "log" "github.com/crowdsecurity/machineid" ) func main() { id, err := machineid.ProtectedID("myAppName") if err != nil { log.Fatal(err) } fmt.Println(id) } ``` ### Function: ID() (string, error) Returns original machine id as a `string`. ### Function: ProtectedID(appID string) (string, error) Returns hashed version of the machine ID as a `string`. The hash is generated in a cryptographically secure way, using a fixed, application-specific key (calculates HMAC-SHA256 of the app ID, keyed by the machine ID). ## What you get This package returns the OS native machine UUID/GUID, which the OS uses for internal needs. All machine IDs are usually generated during system installation and stay constant for all subsequent boots. The following sources are used: * **BSD** uses `/etc/hostid` and `smbios.system.uuid` as a fallback * **Linux** uses `/var/lib/dbus/machine-id` ([man](http://man7.org/linux/man-pages/man5/machine-id.5.html)) * **OS X** uses `IOPlatformUUID` * **Windows** uses the `MachineGuid` from `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` ## Unique Key Reliability Do note, that `machine-id` and `MachineGuid` can be changed by root/admin, although that may not come without cost (broken system services and more). Most IDs won't be regenerated by the OS, when you clone/image/restore a particular OS installation. This is a well known issue with cloned windows installs (not using the official sysprep tools). **Linux** users can generate a new id with `dbus-uuidgen` and put the id into `/var/lib/dbus/machine-id` and `/etc/machine-id`. **Windows** users can use the `sysprep` [toolchain](https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/sysprep--generalize--a-windows-installation) to create images, which produce valid images ready for distribution. Such images produce a new unique machine ID on each deployment. ## Security Considerations A machine ID uniquely identifies the host. Therefore it should be considered "confidential", and must not be exposed in untrusted environments. If you need a stable unique identifier for your app, do not use the machine ID directly. > A reliable solution is to hash the machine ID in a cryptographically secure way, using a fixed, application-specific key. That way the ID will be properly unique, and derived in a constant way from the machine ID but there will be no way to retrieve the original machine ID from the application-specific one. Do something along these lines: ```golang package main import ( "crypto/hmac" "crypto/sha256" "fmt" "github.com/crowdsecurity/machineid" ) const appKey = "WowSuchNiceApp" func main() { id, _ := machineid.ID() fmt.Println(protect(appKey, id)) // Output: dbabdb7baa54845f9bec96e2e8a87be2d01794c66fdebac3df7edd857f3d9f97 } func protect(appID, id string) string { mac := hmac.New(sha256.New, []byte(id)) mac.Write([]byte(appID)) return fmt.Sprintf("%x", mac.Sum(nil)) } ``` Or simply use the convenience API call: ```golang hashedID, err := machineid.ProtectedID("myAppName") ``` ## Snippets Don't want to download code, and just need a way to get the data by yourself? BSD: ```bash cat /etc/hostid # or (might be empty) kenv -q smbios.system.uuid ``` Linux: ```bash cat /var/lib/dbus/machine-id # or when not found (e.g. Fedora 20) cat /etc/machine-id ``` OS X: ```bash ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID ``` Windows: ```batch reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography /v MachineGuid ``` or * Open Windows Registry via `regedit` * Navigate to `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` * Take value of key `MachineGuid` ## Credits The Go gopher was created by [Denis Brodbeck](https://github.com/denisbrodbeck) with [gopherize.me](https://gopherize.me/), based on original artwork from [Renee French](http://reneefrench.blogspot.com/). This work is derived from Denis Brodbeck's [original repository](https://github.com/denisbrodbeck/machineid) ## License The MIT License (MIT) — [Denis Brodbeck](https://github.com/denisbrodbeck). Please have a look at the [LICENSE.md](LICENSE.md) for more details. golang-github-crowdsecurity-machineid-1.0.3/example_test.go000066400000000000000000000005231435745213700241210ustar00rootroot00000000000000package machineid_test import ( "fmt" "log" "github.com/crowdsecurity/machineid" ) func Example() { id, err := machineid.ID() if err != nil { log.Fatal(err) } fmt.Println(id) } func Example_protected() { appID := "Corp.SomeApp" id, err := machineid.ProtectedID(appID) if err != nil { log.Fatal(err) } fmt.Println(id) } golang-github-crowdsecurity-machineid-1.0.3/go.mod000066400000000000000000000001601435745213700222030ustar00rootroot00000000000000module github.com/crowdsecurity/machineid go 1.17 require golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 golang-github-crowdsecurity-machineid-1.0.3/go.sum000066400000000000000000000003171435745213700222340ustar00rootroot00000000000000golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-crowdsecurity-machineid-1.0.3/helper.go000066400000000000000000000014461435745213700227130ustar00rootroot00000000000000package machineid import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "io" "io/ioutil" "os" "os/exec" "strings" ) // run wraps `exec.Command` with easy access to stdout and stderr. func run(stdout, stderr io.Writer, cmd string, args ...string) error { c := exec.Command(cmd, args...) c.Stdin = os.Stdin c.Stdout = stdout c.Stderr = stderr return c.Run() } // protect calculates HMAC-SHA256 of the application ID, keyed by the machine ID and returns a hex-encoded string. func protect(appID, id string) string { mac := hmac.New(sha256.New, []byte(id)) mac.Write([]byte(appID)) return hex.EncodeToString(mac.Sum(nil)) } func readFile(filename string) ([]byte, error) { return ioutil.ReadFile(filename) } func trim(s string) string { return strings.TrimSpace(strings.Trim(s, "\n")) } golang-github-crowdsecurity-machineid-1.0.3/helper_test.go000066400000000000000000000043261435745213700237520ustar00rootroot00000000000000package machineid import ( "bytes" "crypto/hmac" "crypto/sha256" "encoding/hex" "strings" "testing" ) func Test_protect(t *testing.T) { appID := "ms.azur.appX" machineID := "1a1238d601ad430cbea7efb0d1f3d92d" hash := protect(appID, machineID) if hash == "" { t.Error("hash is empty") } rawHash, err := hex.DecodeString(hash) if err != nil { t.Error(err) } eq := checkMAC([]byte(appID), rawHash, []byte(machineID)) if eq == false { t.Error("hashes do not match") } // modify rawHash --> should not match rawHash[0] = 0 eq = checkMAC([]byte(appID), rawHash, []byte(machineID)) if eq == true { t.Error("hashes do match, but shouldn't") } } func checkMAC(message, messageMAC, key []byte) bool { mac := hmac.New(sha256.New, key) mac.Write(message) expectedMAC := mac.Sum(nil) return hmac.Equal(messageMAC, expectedMAC) } func Test_run(t *testing.T) { stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} wantStdout := "hello" wantStderr := "" if err := run(stdout, stderr, "echo", "hello"); err != nil { t.Error(err) } gotStdout := strings.TrimRight(stdout.String(), "\r\n") if gotStdout != wantStdout { t.Errorf("run() = %v, want %v", gotStdout, wantStdout) } if gotStderr := stderr.String(); gotStderr != wantStderr { t.Errorf("run() = %v, want %v", gotStderr, wantStderr) } } func Test_run_unknown(t *testing.T) { stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} err := run(stdout, stderr, "echolo", "hello") if err == nil { t.Error("expected error, got none") } if strings.Contains(err.Error(), "executable file not found") == false { t.Error("unexpected error, expected exec not found") } } func Test_trim(t *testing.T) { type args struct { s string } tests := []struct { name string args args want string }{ { name: "nil", args: args{s: ""}, want: "", }, { name: "space", args: args{s: " space "}, want: "space", }, { name: "nl", args: args{s: "data\n"}, want: "data", }, { name: "combined", args: args{s: " some data \n"}, want: "some data", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := trim(tt.args.s); got != tt.want { t.Errorf("trim() = %v, want %v", got, tt.want) } }) } } golang-github-crowdsecurity-machineid-1.0.3/id.go000066400000000000000000000033601435745213700220250ustar00rootroot00000000000000// Package machineid provides support for reading the unique machine id of most OSs (without admin privileges). // // https://github.com/denisbrodbeck/machineid // // https://godoc.org/github.com/denisbrodbeck/machineid/cmd/machineid // // This package is Cross-Platform (tested on Win7+, Debian 8+, Ubuntu 14.04+, OS X 10.6+, FreeBSD 11+) // and does not use any internal hardware IDs (no MAC, BIOS, or CPU). // // Returned machine IDs are generally stable for the OS installation // and usually stay the same after updates or hardware changes. // // This package allows sharing of machine IDs in a secure way by // calculating HMAC-SHA256 over a user provided app ID, which is keyed by the machine id. // // Caveat: Image-based environments have usually the same machine-id (perfect clone). // Linux users can generate a new id with `dbus-uuidgen` and put the id into // `/var/lib/dbus/machine-id` and `/etc/machine-id`. // Windows users can use the `sysprep` toolchain to create images, which produce valid images ready for distribution. package machineid import ( "fmt" ) // ID returns the platform specific machine id of the current host OS. // Regard the returned id as "confidential" and consider using ProtectedID() instead. func ID() (string, error) { id, err := machineID() if err != nil { return "", fmt.Errorf("machineid: %v", err) } return id, nil } // ProtectedID returns a hashed version of the machine ID in a cryptographically secure way, // using a fixed, application-specific key. // Internally, this function calculates HMAC-SHA256 of the application ID, keyed by the machine ID. func ProtectedID(appID string) (string, error) { id, err := ID() if err != nil { return "", fmt.Errorf("machineid: %v", err) } return protect(appID, id), nil } golang-github-crowdsecurity-machineid-1.0.3/id_bsd.go000066400000000000000000000015271435745213700226600ustar00rootroot00000000000000// +build freebsd netbsd openbsd dragonfly solaris package machineid import ( "bytes" "os" ) const hostidPath = "/etc/hostid" // machineID returns the uuid specified at `/etc/hostid`. // If the returned value is empty, the uuid from a call to `kenv -q smbios.system.uuid` is returned. // If there is an error an empty string is returned. func machineID() (string, error) { id, err := readHostid() if err != nil { // try fallback id, err = readKenv() } if err != nil { return "", err } return id, nil } func readHostid() (string, error) { buf, err := readFile(hostidPath) if err != nil { return "", err } return trim(string(buf)), nil } func readKenv() (string, error) { buf := &bytes.Buffer{} err := run(buf, os.Stderr, "kenv", "-q", "smbios.system.uuid") if err != nil { return "", err } return trim(buf.String()), nil } golang-github-crowdsecurity-machineid-1.0.3/id_darwin.go000066400000000000000000000015651435745213700233760ustar00rootroot00000000000000// +build darwin package machineid import ( "bytes" "fmt" "os" "strings" ) // machineID returns the uuid returned by `ioreg -rd1 -c IOPlatformExpertDevice`. // If there is an error running the commad an empty string is returned. func machineID() (string, error) { buf := &bytes.Buffer{} err := run(buf, os.Stderr, "ioreg", "-rd1", "-c", "IOPlatformExpertDevice") if err != nil { return "", err } id, err := extractID(buf.String()) if err != nil { return "", err } return trim(id), nil } func extractID(lines string) (string, error) { for _, line := range strings.Split(lines, "\n") { if strings.Contains(line, "IOPlatformUUID") { parts := strings.SplitAfter(line, `" = "`) if len(parts) == 2 { return strings.TrimRight(parts[1], `"`), nil } } } return "", fmt.Errorf("Failed to extract 'IOPlatformUUID' value from `ioreg` output.\n%s", lines) } golang-github-crowdsecurity-machineid-1.0.3/id_darwin_test.go000066400000000000000000000035321435745213700244310ustar00rootroot00000000000000// +build darwin package machineid import ( "strings" "testing" ) const sampleOutput = `+-o MacBookPro12,1 { "IOPlatformSystemSleepPolicy" = <534c505402001300841e120004000000001400000004000006000000000000000f2500000000000000004000000040000000100000001000070000$ "compatible" = <"MacBookPro12,1"> "version" = <"1.0"> "board-id" = <"Mac-EEECCCDDD8888AAA"> "IOInterruptSpecifiers" = (<0900000005000000>) "platform-feature" = <0200000000000000> "serial-number" = <1111111100000000000000000022222222222d3333333000000000000000000000000000000000000> "IOInterruptControllers" = ("io-apic-0") "IOPlatformUUID" = "A3344D1DD-1234-22A1-B123-11AB1C11D111" "target-type" = <"Mac"> "clock-frequency" = <00e1f505> "manufacturer" = <"Apple Inc."> "IOPolledInterface" = "SMCPolledInterface is not serializable" "IOPlatformSerialNumber" = "CCCCCCCCC" "system-type" = <02> "product-name" = <"MacBookPro12,1"> "model" = <"MacBookPro12,1"> "name" = <"/"> "IOBusyInterest" = "IOCommand is not serializable" } ` func Test_extractID(t *testing.T) { want := "A3344D1DD-1234-22A1-B123-11AB1C11D111" got, err := extractID(sampleOutput) if err != nil { t.Error(err) } if got != want { t.Errorf("extractID() = %v, want %v", got, want) } } func Test_extractID_invalidInput(t *testing.T) { got, err := extractID("invalid input") if err == nil { t.Error("expected error, got none") } if got != "" { t.Errorf("expected empty string, got some value %s", got) } if strings.Contains(err.Error(), "Failed to extract 'IOPlatformUUID'") == false { t.Errorf("Got unexpected error: %v", err) } } func Test_machineID(t *testing.T) { got, err := machineID() if err != nil { t.Error(err) } if got == "" { t.Error("Got empty machine id") } } golang-github-crowdsecurity-machineid-1.0.3/id_linux.go000066400000000000000000000016631435745213700232500ustar00rootroot00000000000000// +build linux package machineid import "errors" const ( // dbusPath is the default path for dbus machine id. dbusPath = "/var/lib/dbus/machine-id" // dbusPathEtc is the default path for dbus machine id located in /etc. // Some systems (like Fedora 20) only know this path. // Sometimes it's the other way round. dbusPathEtc = "/etc/machine-id" ) // machineID returns the uuid specified at `/var/lib/dbus/machine-id` or `/etc/machine-id`. // If there is an error reading the files an empty string is returned. // See https://unix.stackexchange.com/questions/144812/generate-consistent-machine-unique-id func machineID() (string, error) { id, err := readFile(dbusPath) if err != nil || trim(string(id)) == "" { // try fallback path id, err = readFile(dbusPathEtc) } if err != nil { return "", err } if trim(string(id)) == "" { return "", errors.New("All known machineid file are empty") } return trim(string(id)), nil } golang-github-crowdsecurity-machineid-1.0.3/id_test.go000066400000000000000000000007121435745213700230620ustar00rootroot00000000000000package machineid import "testing" func TestID(t *testing.T) { got, err := ID() if err != nil { t.Error(err) } if got == "" { t.Error("Got empty machine id") } } func TestProtectedID(t *testing.T) { id, err := ID() if err != nil { t.Error(err) } hash, err := ProtectedID("app.id") if err != nil { t.Error(err) } if hash == "" { t.Error("Got empty machine id hash") } if id == hash { t.Error("id and hashed id are the same") } } golang-github-crowdsecurity-machineid-1.0.3/id_windows.go000066400000000000000000000011121435745213700235700ustar00rootroot00000000000000// +build windows package machineid import ( "golang.org/x/sys/windows/registry" ) // machineID returns the key MachineGuid in registry `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`. // If there is an error running the commad an empty string is returned. func machineID() (string, error) { k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Cryptography`, registry.QUERY_VALUE|registry.WOW64_64KEY) if err != nil { return "", err } defer k.Close() s, _, err := k.GetStringValue("MachineGuid") if err != nil { return "", err } return s, nil } golang-github-crowdsecurity-machineid-1.0.3/logo.png000066400000000000000000000473641435745213700225640ustar00rootroot00000000000000PNG  IHDR^1W:PLTE@@@ܷe̽ !!DCC.//б()(&&%YWUƓ=>?##"ffi666nllpiil+,+233ҟqquNNPɆJIJ||yy}cce]]_SSUZZ[ttx``b:::DGJoorxQ˹™ġk靝wwzΥVVYۙLTYg좢FMRz6:@"#0tRNS jBvJ_8/(PWXttoT. M̽KsC#} ~Wfřᾡ9"b ύL {\ndpz: C//DŗpbX | \KƋ#w+/ݠ,"oTjFhJd " ajfsyu;80 M# OGúJ$B7^ ݓqr: }Ɠ{iz/{< >@gΏOtIaA] #CT؀2''*:wt^KB-!(#4Kusp쭪&v, t:Sw@PF!e'$1>s+< y}˞<jr\v0xÈ<if+P!M÷\=7,8ª1`2uBhG-xd)nXD^Jc7$l`52tzMtXAф~?*VT/0C^~`uOHd'Drj6F:Bs,ð$I&z-|A5v[FXڄ62nC>t!aN񆤳a_X=L=.s(5cxj|m ͍^^7dL]H,0y.FB!Ei}NQZ!3X6ؾۗ^vm%%.yv&ӨgͶ ؘ !1f @֞bFiN*4X_\(4h/m}dϏ*ǵaP -Q'"U8%`H/WXh9OWǃyy¶rZd_#Z 9M5]KK*i]7w2um0"vl߶s=Z$UVнD Zl ͉sj|2rظ个E܁_ndԄ/RnQ $|Ks\;N /)k { Yr3_Ɵ9 0x'j }/'5͂vIyqjKEaC1faZ.5s=6U*Z3r ŖhF>XyPcrvlO.W{mOp2^VTʠzo8EXQcwMRMq 2m=(xr |9V~oCCx$O Yv N$lN|8`{^^ dY*~R_ Ln/奶8`A=*~lֿA8c f2RMrƤSp7BsvHtrWB_>ۀo! Bd m]CEd+%o 7~ ٖd[|jJ2>ڥ #:7HD{]L[ϲw_d7BY'MXɪl@ed VB(zw$탘oU=-=umVBTO k6W-/60'fNe|̌UnxtZ/{*!{I:6w +1SIX#{B[e#V 9 Tf҂;,O{+-0ysW><xj&ืNK| .hMeiHtV$D_wڷ3A\EߝZ^ !mXbV:n ۮ^QkkP_^+:qޫnlvaX wz^a2ؘKy9Q[q\@e)b'{cu^k0*fE}n\K E.S[LX,|;@mL"KRiڷk!Z,/a՗&B8ͯ[MXlRu]G_7)1L|y:Ual-_^fЁ-=zӨVB{A: Nc.oS챕y%@}rs%)H^+C`Ս1B peeFMŗ=\Hr0O>꽭אn},~OrieVP<Bf߄&W=5|y:%d:HsܽY27aȰi.w›ԜhoCl[ "S 'o ,6 w_Qۮ$7R'=ƖH:1jhikMAA ƚ[#1 Fobp *$ˆ( 550 Oc%!o8);"r=pahẻGA'555 0r m}\D '9.]a3I.,qU&#}Gdi".]F,)؆"ʑ#of!g +{~kGFDƂV3 kX]L`XlBW|Q lb\rzK c2H `.<׿"Zs ۠ 8rm6}0 lLO2D]+oZlm FF;xǎa]CVnsUJf7tbIf֙o_ޔhG 9ofɪDvWCHA5樎 MҬlA?wť] J X-7pǍv$B{!Q bk_o?C91Ums.#dÓ2mARm&`PeE6kiL tϙoXԼr6Aݕ J!Yswu; BntCC.BXu'y\%5ˡt^Jl5ZfUhJҿS38{MbN[4"F0eCXjo":u4+C)ܵdi9-#X2[OSuDj2"4J,)WpJS1Qk}ѐ~|FV^I+oFRLTZ:ఴbv;Q#}s7]0p\6CNDoH<L]&R0js,M7䋉N30:Vg;˶kIr+5Ll.B\UFi+++:n!-DRjX-O{\!E N&Ia.Ξi8i9ҝJґ Pkw!Ch*\}۴]n8$X^.N6D7 5ї&Y:ܹkp4̘oE tV^obQCvǘ: ˫ir,VADA!D!;_?j@s B%%5T!.ቼL)U:B8^ӧy=2}^PXf:ug^Y/jbaeKXޝ82aqj FQmvv l,3S\KHYadsmaoU|9T;^%C,(2$3u.Aõ^﮳g80V)>Q׵ߞ4tSXrs U:E=*r4& .*K =ƺeRKYe2p[.6,-5)7^H)!A MW+nFmß~W?M`BxN]#[;e|,Y>}|VZ oɪ麩N9tҊNT}݊#vP2s3=4f>vfL|n{QfZ|8EltHS?蛹D#o^csނALi,.8e?/oy_+pb#MK}s.6 mH;.nbG 1\B\a[y'e/V_WQ4s)SDp*n },RWJ2dwڸ>=p=s-]V4Gv3yiPcT~ p(/?XWW'+-pnD|B\moDVA)hnM_`=|֎g.n*Qv'\Gt vZ.vb|nlrLq9J3  \ h+3w1ݭd;Bl u׋%s-U A@MZe vri333WӦup F{e; l!"T_LWEMymcYc?Ud?f|q8H WfUCKM1q+X\ C)wqzzf Ό.&H'A%ӧ'>飉#{^ nzcR^f  YW`z|1;.Ԙ k,. W91ݥ(;ؑ6Eݐ.̌.ߚLcUZU/ͱ-`e $LOb{%łFwfdZ8QMoqŜ%{Mrj+7VRPI+(x}P+F)ڄMTT+A9sDxIHc!nݺ :qx#YK',As R.& 0=@I' ?;"RQDMG {RZ ٫!ƫˈ s*DH ?#GH#`"{W{zf{/Ȣ*թb$M+Y,)B`a" ‘Jj?HEyWf欁pH% ә1FA#VqZe,}l$2&ʠ!XVqL** 0fQw6ot]N;8V^#HDpRuZP¿0ZKڜ&rS":Wkj4R 4țy!&ܽ@f#tjll$vVEdUNjJ~`cf~ZިTՅ *IulHb!Up 6i<'{eAxd11]C˘Gta[ >|qP_Cl8v4aaDslbQRf`i ( ' g; ߾O;G?R'JDaKF(#p}əCo=^y/dSoJn AMR{k?ٵxݳwoh' ^'Q)1I4` R %ZgHv1 z}gha]Zh|o^|Jl8֧5j\PӱF)YhԂ.k2zطA3,莍ze )dM2&)u,;l*1蝻M$b +>ɻɛyʎ؃CW5M g_!R|$Zۦ87BXl $l_ޥm4+BӬJT1EIX-}~C(U)(kh1Chx~E7p4냭I\8ssϽ=̹uEV 6DK '= ,8Bi7}%21B/ `PSg p7` Lղ L`ݳupm(ؽJWX;$|q{#!B]hԦ 7<`JilA*q=ІEj駡8H&$\sWxXk|W%hPF : ;p!gbw21/ث2ǡchSXK$5'%g//&IV7|=gD~&rLݽW @ӁȲ)9HEP{>z`}#@L-^=vt)CI>"NE@g\k&5e?e>S\X]㾌( I]%LMe j/ݣne~YgJ t!7=ˋq9:ހ*%fg_sng_Yw&:+Y|vbTM#EqS=4Zf,Е C`I,quM| &` -^𠼅zEv*%wp X4Uߋhh& C$/,L* K4>?=*{%4Rn@`00e䒗Ǐ$ Ք2CC3)ž7luI#㗓9y/^O#ZN9R=9qZT 'Q!N<T2&j9-//LIx ےTB٤tSq;BUIXa)VZUTS!|>DTI2=rp }bjvOQmf m!n +PJ2)g&ke{x:ǻ>vhcU ?iTh:[,HH| e&_JB҇Q'Y'lSvrDK@VkdpP_Kc#ư4F[db粤& \|YD\̸8`Bo#8 L˺{ wDdU60,t)Kb\GoZe+!n֍o;ML؅.oS{=vhVl a",]daP@e8ꅘm oTn,smy:gjW%x;mgeRZr"H@5R%[Q`4懽1ptVR#, i2wZЃ7XR7jhft4FڲOH4e k͕̰|kVvϽr%qرKjA6u̢;pAGDKMbU&Z=-fdc;ڟk9T;j7BW{+5nMǶvErS;Nu7o fZ"c+ѕwtܱ)Dy<+ t 0`Â!]cтo:'G CwRXqŹȮ#vuXPXl g ] =vpINDnD0,#jdwY9θ7Y&l頨A@WPh~hj[RjuW&@M7v8 ]{_F'H@K2 6P 5BwNa,@楸o^dI!Um.)>+lVt&幼B*=C_tS xՃW 9Sj3ؘ W9[S o$)xt\i[b%ݖiQۧNMM L,ٔWlMPT~Cҽ# LWyGSKP2c:Ren5MFjHCcD}M!>[![gzѽ'y^ւ7Զ >IpJIxFy> N"*lM=玱/z'|D}9ʃJGG\OBӐi]/kV4n}~ V' 5L_)ƱT89E+XnqbԑOpj໶vߞ!Śu1+`\2x^TX~bL&ӳE𖶾I:u-"8z|yv Fw1J Dɍ&(=я_㯓Vg6%$_BO̝.8U'j$OK:.L94#ׅ*^_SݙRGq==r- :-yJTDhk4˅zuF\?+bs v^9H8`0A#V]y XL0 :1}Zp@f1luT6ԹI%Ъj}hp B{.RTzIձ^ah?<0wI]{3zPmOﵯx=T?- mmssG\k5<'ULT'.&+ Da uiV3jC/ţL01_kT=܅iVne 6Z"&L洒(.v 54q.TZxs ^6 ?a(#q[IXW,_8y7tbWӆQvּݺ C=i ]݃o5;h,'̖|fX 0+qB5 O=#؟BǸ΄ 5\ SL\792 ezAySo]qU?q_ܧ5m:s_-گ0ycp'Mu*<crLiǗpU=S(ԝ0w4~],rXV !bNUf)&1fq~xìmdbۡ@&|c/)s z=ce9ǝ4Efͷxg' YރxQB1Vu:sNhnxx7F޳18Hf:f9Fqn"^RQ];=~eL Ƹ p]~j3< {؅n8+C6 Wx q- y;T~&WJUuT3Gv;;NfV,]qm+1{/k=ۯI\~uYTY+ɸy!&iѾn0lkgkNq0NKboX!#=13ɮ쬿9GÛnWg5rXUn43:O@nُ*7jqOa`F_!/ttq7s `B3q ?<4~NI#%\.͜u܁ۣfiߛ?,l6rf%L"z QV^tB|UŸXZ9(z^΀$};͹5 + x 8%w:щ9o,n ]rgu/IZg{?gWŻ5GojDq<܍9 h7+'P;Z:5M+{gxWP/O½JVWz᝭҆iS|/b pyCKGxo75 qvvݐ! H*B LqwJHUǕCLk{LWN CU$VNa=r'PxWvbѪ53{Cl":Dy!-7&1Wr XT҆/B&Uڃd9Rb iQ8qǓb/B|Zxzgq/޺[j~f=kpXXlZcכtҌ@>%3냿4i݈ef2tTpݠ*[Ob05:0܁l,Bc|Tx2{b ){tMz`_5`́ج(qC:nlEvx3dncSxQ|!^o Ɩݡ JML-m6671<06&8XWjBr x}azX7_IV;].~][I׵fDMXTk~;0QIMWDj=I9?u 18jQ΄c5vo`Fww,cO`äzzTGg=O @Wm*hbC=V7޼޴ixscxU)ɒ*sT1J d3}֘&c}rV6K}m4^bVV⠏{/{Uk+>*O1p{0چXqpU bЩmI^u%(/Y.;vV DF]vkfqyʁ 4fUKTGG>LIZX­?Ao9ME+>x{I`H=?hyaZ!*T$+Fu^andc}JmE3Ԇvb~ncӳ`lC# kMf/ԗ"OnAoǩ'n!x/zG/`TUK"`3KJ,l>rgծ,)@^(f-TfUΠ Y沓p$;ٴwmel.͟B#;YW-=٣$颽|ug=oyuI,,(DOS,,t48(lWZ\ގ#ޑ ]ɰh{ex?xݠt&0Ћ4=~? |SNA tus4`kS`@{+m_PSBa̪fB.!V<2Ff %:꼤o8.qިUgEXOI? (6*HEc-ΤTOi~tN3BMVy&~l'?zt&O˒A⾧i6sb N{t 0kS[dbwBbYDy~ugVqIvAp" x>}75݄Q$o}#Me /^>{ f;J6T*T]5}h2YV 5 ` Mb-#ҁA _Dơ5 zTwݰM>N<:s"?xH.P({⁝~J8ޠfqx{@8w7 #nM7h. V:REd_@eD?lqEN08D?x#ż s-H9IJ XcInAl?/fŖΐR:|MYOTldcҊVOJ !Жb䋁7xk ,}_r$IjZߔMY( \X$hruۗShSB30e Go9OOcpj¬'-.?ÍRS5Q9o+T.Hև #dgi∪%.龋u?zSq %bz٬5m/rWM d|"ݘ *XDeh*3))bP(P7'+m.UibK'o1^qJ= o[o~4^s.C&l;% %Px;V,k[@lSUf{.Mݕ@ehؗGoKM^pVaQfY+ᐁ!(v_>c'zg[F %mpotv TX!&S+Mٍ2dM府*BJ gTii~rBZ:Փ.9/cW *L|#hME"Pnf uTf K2kRF5܃V)]wa70_=W}]/ 6az 4s.ο+.ZB}3IڎUO]Tfr(:fU g';XN{/o `vC؋+1ze%jxކxcfyYȤd)޲횭~; 36}%VN6\O/o.k@^Z)~d?j7)x4FMiۻ=ҳH^aK.ޖ)J1=|CA;ID4W7bf>n Yh"rk{{ܠO; ]yeI#kxqoZ$He&7:yۉ -Vؑ0ު'yt:uVq|qO*OkkyeYkD zm~Nm< Bq58˳<ft e)o-ʗ$G3l'!Q]'/ dIxim~/*WE*]abyqئq(R\0WGK띀MdY|زo,}x\꛳P3HWNz[1&ݢZ%6؈Z&o̘ h7^ĜZ=f"Oԓ"լF@uXFFA\[:\V#o.iO<ÇZ_} ")z5L`KZ5Ïf φsvC}%-.omxobڶ lyE^-hMi/ K/-ou'>:,2b楴g`W]c~7$>KbN|,+Фu