From b71b594e74ccbbfc987a787c45158598c2f807c7 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Thu, 17 Sep 2015 20:44:21 +0300 Subject: [PATCH] JSON configuration Signed-off-by: Sergey Matveev --- doc/client.texi | 5 +- doc/cpr.texi | 2 +- doc/example.texi | 54 ++++---- doc/identity.texi | 4 +- doc/news.texi | 1 + doc/server.texi | 105 ++++++++-------- doc/user.texi | 10 +- doc/verifier.texi | 4 +- src/govpn/cmd/govpn-client/main.go | 3 +- src/govpn/cmd/govpn-client/tcp.go | 2 +- src/govpn/cmd/govpn-client/udp.go | 2 +- src/govpn/cmd/govpn-server/common.go | 4 +- src/govpn/cmd/govpn-server/conf.go | 105 ++++++++++++++++ src/govpn/cmd/govpn-server/main.go | 25 ++-- src/govpn/cmd/govpn-server/tcp.go | 4 +- src/govpn/cmd/govpn-server/udp.go | 4 +- src/govpn/identify.go | 182 ++++++--------------------- utils/newclient.sh | 54 +++++--- 18 files changed, 288 insertions(+), 282 deletions(-) create mode 100644 src/govpn/cmd/govpn-server/conf.go diff --git a/doc/client.texi b/doc/client.texi index 1ed749e..bab6a47 100644 --- a/doc/client.texi +++ b/doc/client.texi @@ -7,7 +7,8 @@ options client has the following ones: @table @code @item -proto -@ref{Network, network protocol} to use. Can be either @emph{udp} or @emph{tcp}. +@ref{Network, network protocol} to use. Can be either @emph{udp} +(default) or @emph{tcp}. @item -proxy Use specified @emph{host:port} @ref{Proxy} server for accessing remote @@ -37,7 +38,7 @@ how to enter passphrase from stdin silently and store it in the file. Enable @ref{Noise}. @item -cpr -Enable @ref{CPR} in KiB/sec. +Set @ref{CPR} in KiB/sec. @item -up Optional path to script that will be executed after connection is diff --git a/doc/cpr.texi b/doc/cpr.texi index cd7b48a..5256a81 100644 --- a/doc/cpr.texi +++ b/doc/cpr.texi @@ -7,4 +7,4 @@ delays other ones. This mode is turned by @code{-cpr} option, where you specify desired outgoing traffic rate in KiB/sec (kibibytes per second). This option also -forces using of the @ref{Noise}! It is turned off by default. +@strong{forces} using of the @ref{Noise}! It is turned off by default. diff --git a/doc/example.texi b/doc/example.texi index 9fb3ad1..c262846 100644 --- a/doc/example.texi +++ b/doc/example.texi @@ -15,44 +15,38 @@ is 1432. @end itemize @strong{Install}. At first you must @ref{Installation, install} this -software: download, check the signature, compile. +software: download, @ref{Integrity, check the signature}, compile. -@strong{Prepare the server}. Create the new client, named (for example) -"Alice": +@strong{Prepare the client}. Generate client's identity and verifier for +Alice as an example: +@verbatim +client% ./utils/newclient.sh Alice +Enter passphrase: +Your id is: 7012df29deee2170594119df5091d4a2 -@example -server% ./utils/newclient.sh Alice -Place verifier to peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier -@end example - -"6d4ac605ce8dc37c2f0bf21cb542a713" -- is the new client's identity. - -@strong{Prepare the client}. Generate @ref{Verifier} for known client -identity: +Place the following JSON configuration entry on the server's side: -@example -client% ./utils/storekey.sh /tmp/passphrase -Enter passphrase:[my secure passphrase is here] -client% govpn-verifier -id 6d4ac605ce8dc37c2f0bf21cb542a713 -key /tmp/passphrase -562556cc9ecf0019b4cf45bcdf42706944ae9b3ac7c73ad299d83f2d5a169c55 -client% rm /tmp/passphrase -@end example + "7012df29deee2170594119df5091d4a2": { + "name": "Alice", + "up": "/path/to/up.sh", + "verifier": "fb43255ca3fe5bd884e364e5eae0cd37ad14774930a027fd38d8938fd0b57425" + } -"562556cc9ecf0019b4cf45bcdf42706944ae9b3ac7c73ad299d83f2d5a169c55" -- -this is verifier itself. +Verifier was generated with: -@strong{Save verifier on server}. + ./utils/storekey.sh /tmp/passphrase + govpn-verifier -id 7012df29deee2170594119df5091d4a2 -key /tmp/passphrase +@end verbatim -@example -server% cat > peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier <> peers/6d4ac605ce8dc37c2f0bf21cb542a713/up.sh +server% umask 077 +server% echo "#!/bin/sh" > /path/to/up.sh +server% echo "echo tap10" >> /path/to/up.sh server% ip addr add 192.168.0.1/24 dev wlan0 server% tunctl -t tap10 server% ip link set mtu 1432 dev tap10 @@ -83,7 +77,7 @@ client% ip route add default via 172.16.0.1 @example client% govpn-client \ -key key.txt \ - -id 6d4ac605ce8dc37c2f0bf21cb542a713 \ + -id 906e34b98750c4f686d6c5489508763c \ -iface tap10 \ -remote 192.168.0.1:1194 \ -mtu 1472 @@ -103,7 +97,7 @@ client% ifconfig tap10 inet6 fc00::2/96 mtu 1412 up client% route -6 add default fc00::1 client% govpn-client \ -key key.txt \ - -id 6d4ac605ce8dc37c2f0bf21cb542a713 \ + -id 906e34b98750c4f686d6c5489508763c \ -iface tap10 \ -remote "[fe80::1%me0]":1194 @end example diff --git a/doc/identity.texi b/doc/identity.texi index cb3061f..46a6889 100644 --- a/doc/identity.texi +++ b/doc/identity.texi @@ -7,7 +7,7 @@ to make DPI and deanonymization much harder to success. @example % ./utils/newclient.sh doesnotmatter -Place verifier to peers/6d4ac605ce8dc37c2f0bf21cb542a713/verifier +Your id is: 7012df29deee2170594119df5091d4a2 @end example -"6d4ac605ce8dc37c2f0bf21cb542a713" is client's identity. +@code{7012df29deee2170594119df5091d4a2} is client's identity. diff --git a/doc/news.texi b/doc/news.texi index e6f6b62..bdb178e 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -9,6 +9,7 @@ hidden. Now they are indistinguishable from transport messages. @item Parallelized clients processing on the server side. @item Much higher overall performance. +@item Single JSON file server configuration. @end itemize @item Release 3.5 diff --git a/doc/server.texi b/doc/server.texi index a9ce065..49a09fb 100644 --- a/doc/server.texi +++ b/doc/server.texi @@ -7,76 +7,69 @@ has the following ones: @table @code @item -proto -@ref{Network, network protocol} to use. Can be @emph{udp}, +@ref{Network, network protocol} to use. Can be @emph{udp} (default), @emph{tcp} or @emph{all}. @item -bind Address (@code{host:port} format) we must bind to. -@item -peers -Path to the directory containing peers information, database. +@item -conf +Path to JSON file with the configuration. @item -proxy Start trivial HTTP @ref{Proxy} server on specified @emph{host:port}. @end table -Peers directory must contain subdirectories with the names of client's -identities in hexadecimal notation. Each subdirectory has the following -files: - -@table @code - -@item verifier -@strong{Required}. Contains corresponding verifier used to authenticate -the client in hexadecimal notation. See @ref{Verifier} for how -to create it. - -@item up.sh -@strong{Required}. up-script executes each time connection with the -client is established. It's @emph{stdout} output must contain TAP -interface name on the first string. This script can be simple -@code{echo tap10}, or maybe more advanced like this: - @example - #!/bin/sh - $tap=$(ifconfig tap create) - ifconfig $tap inet6 fc00::1/96 mtu 1412 up - echo $tap - @end example - -@item down.sh -Optional. Same as @code{up.sh} above, but executes when connection is -lost. - -@item name -Optional. Contains human readable username. Used to beauty output of -@ref{Stats}. - -@item timeout -Optional. Contains @ref{Timeout} setting (decimal notation) in seconds. -Otherwise default minute timeout will be used. - -@item noise -Optional. Contains either "1" (enable @ref{Noise} adding), or "0". - -@item cpr -Optional. Contains @ref{CPR} setting (decimal notation) in KiB/sec. - -@end table +Configuration file is JSON file with following example structure: + +@verbatim +{ + "9b40701bdaf522f2b291cb039490312": { <-- Peer identifier + "name": "stargrave", <-- OPTIONAL human readable name + "up": "./stargrave-up.sh", <-- up-script + "down": "./stargrave-down.sh", <-- OPTIONAL down-script + "timeout": 60, <-- OPTIONAL overriden timeout + "noise": true, <-- OPTIONAL noise enabler + (default: false) + "cpr": 64, <-- OPTIONAL constant packet + rate in KiB/sec + "verifier": "2c15bbdffc73193bea56db412bce1143c68ccbdaa9e2eade53a684497646a685" + }, + [...] +} +@end verbatim + +See @ref{Verifier} for its description. + +up-script executes each time connection with the client is established. +Its @emph{stdout} output must contain TAP interface name as the first +line. This script can be simple @code{echo tap10}, or maybe more +advanced like this: +@example +#!/bin/sh +$tap=$(ifconfig tap create) +ifconfig $tap inet6 fc00::1/96 mtu 1412 up +echo $tap +@end example -Each minute server refreshes peers directory contents and adds newly -appeared identities, deletes an obsolete ones. +Each minute server rereads and refreshes peers configuration and adds +newly appeared identities, deletes an obsolete ones. You can use convenient @code{utils/newclient.sh} script for new client creation: -@example +@verbatim % ./utils/newclient.sh Alice -Place verifier to peers/9b40701bdaf522f2b291cb039490312/verifier -@end example - -@code{9b40701bdaf522f2b291cb039490312} is client's identification. -@code{peers/9b40701bdaf522f2b291cb039490312/name} contains @emph{Alice}, -@code{peers/9b40701bdaf522f2b291cb039490312/verifier} contains dummy -verifier and @code{peers/9b40701bdaf522f2b291cb039490312/up.sh} contains -currently dummy empty up-script. +[...] +Your id is: 7012df29deee2170594119df5091d4a2 + +Place the following JSON configuration entry on the server's side: + + "906e34b98750c4f686d6c5489508763c": { + "name": "Alice", + "up": "/path/to/up.sh", + "verifier": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + } +[...] +@end verbatim diff --git a/doc/user.texi b/doc/user.texi index 6eae9a3..73214da 100644 --- a/doc/user.texi +++ b/doc/user.texi @@ -3,11 +3,11 @@ Announcements about updates and new releases can be found in @ref{Contacts}. -GoVPN is split into two pieces: client and server. Each of them work on -top of UDP/TCP and TAP virtual network interfaces. GoVPN is just a -tunnelling of Ethernet frames, nothing less, nothing more. All you -IP-related network management is not touched by VPN at all. You can -automate it using up and down shell scripts. +GoVPN is split into two pieces: @ref{Client} and @ref{Server}. Each of +them work on top of @ref{Network, UDP/TCP} and TAP virtual network +interfaces. GoVPN is just a tunnelling of Ethernet frames, nothing less, +nothing more. All you IP-related network management is not touched by +VPN at all. You can automate it using up and down shell scripts. What network performance can user expect? For example single @emph{Intel i5-2450M 2.5 GHz} core on @emph{FreeBSD 10.2 amd64} diff --git a/doc/verifier.texi b/doc/verifier.texi index 6e97b7e..61b1e44 100644 --- a/doc/verifier.texi +++ b/doc/verifier.texi @@ -13,8 +13,8 @@ Enter passphrase:[hello world] 210e3878542828901a3af9b4aa00b004de530410eef5c1ba2ffb6d04504371b2 @end example -Store "210...1b2" string on the server's side in corresponding -@code{verifier} file. +Store @code{210...1b2} string on the server's side in corresponding +@code{verifier} configuration file's field. You can check passphrase against verifier by specifying @code{-verifier} option with the path to verifier file: diff --git a/src/govpn/cmd/govpn-client/main.go b/src/govpn/cmd/govpn-client/main.go index 467bec6..6c611e7 100644 --- a/src/govpn/cmd/govpn-client/main.go +++ b/src/govpn/cmd/govpn-client/main.go @@ -52,6 +52,7 @@ var ( timeout int firstUpCall bool = true knownPeers govpn.KnownPeers + idsCache govpn.CipherCache ) func main() { @@ -81,7 +82,7 @@ func main() { DSAPub: pub, DSAPriv: priv, } - govpn.PeersInitDummy(id, conf) + idsCache = govpn.NewCipherCache([]govpn.PeerId{*id}) log.Println(govpn.VersionGet()) tap, err = govpn.TAPListen(*ifaceName) diff --git a/src/govpn/cmd/govpn-client/tcp.go b/src/govpn/cmd/govpn-client/tcp.go index c872400..6265eeb 100644 --- a/src/govpn/cmd/govpn-client/tcp.go +++ b/src/govpn/cmd/govpn-client/tcp.go @@ -72,7 +72,7 @@ HandshakeCycle: } prev += n - peerId := govpn.IDsCache.Find(buf[:prev]) + peerId := idsCache.Find(buf[:prev]) if peerId == nil { continue } diff --git a/src/govpn/cmd/govpn-client/udp.go b/src/govpn/cmd/govpn-client/udp.go index 3cb3eea..9f45f8b 100644 --- a/src/govpn/cmd/govpn-client/udp.go +++ b/src/govpn/cmd/govpn-client/udp.go @@ -77,7 +77,7 @@ MainCycle: } continue } - if govpn.IDsCache.Find(buf[:n]) == nil { + if idsCache.Find(buf[:n]) == nil { log.Println("Invalid identity in handshake packet") continue } diff --git a/src/govpn/cmd/govpn-server/common.go b/src/govpn/cmd/govpn-server/common.go index 06e82e6..4692e52 100644 --- a/src/govpn/cmd/govpn-server/common.go +++ b/src/govpn/cmd/govpn-server/common.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "path" "sync" "time" @@ -49,8 +48,7 @@ Processor: } func callUp(peerId *govpn.PeerId) (string, error) { - upPath := path.Join(govpn.PeersPath, peerId.String(), "up.sh") - result, err := govpn.ScriptCall(upPath, "") + result, err := govpn.ScriptCall(confs[*peerId].Up, "") if err != nil { return "", err } diff --git a/src/govpn/cmd/govpn-server/conf.go b/src/govpn/cmd/govpn-server/conf.go new file mode 100644 index 0000000..4136fab --- /dev/null +++ b/src/govpn/cmd/govpn-server/conf.go @@ -0,0 +1,105 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2015 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package main + +import ( + "encoding/hex" + "encoding/json" + "io/ioutil" + "log" + "time" + + "github.com/agl/ed25519" + + "govpn" +) + +const ( + RefreshRate = time.Minute +) + +var ( + confs map[govpn.PeerId]*govpn.PeerConf + idsCache govpn.CipherCache +) + +func confRead() map[govpn.PeerId]*govpn.PeerConf { + data, err := ioutil.ReadFile(*confPath) + if err != nil { + log.Fatalln("Unable to read configuration:", err) + } + confsRaw := new(map[string]govpn.PeerConf) + err = json.Unmarshal(data, confsRaw) + if err != nil { + log.Fatalln("Unable to parse configuration:", err) + } + + confs := make(map[govpn.PeerId]*govpn.PeerConf, len(*confsRaw)) + for peerIdRaw, pc := range *confsRaw { + peerId, err := govpn.IDDecode(peerIdRaw) + if err != nil { + log.Fatalln("Invalid peer ID:", peerIdRaw, err) + } + conf := govpn.PeerConf{ + Id: peerId, + Name: pc.Name, + Up: pc.Up, + Down: pc.Down, + Noise: pc.Noise, + CPR: pc.CPR, + } + if pc.TimeoutInt <= 0 { + pc.TimeoutInt = govpn.TimeoutDefault + } + conf.Timeout = time.Second * time.Duration(pc.TimeoutInt) + + if len(pc.Verifier) != ed25519.PublicKeySize*2 { + log.Fatalln("Verifier must be 64 hex characters long") + } + keyDecoded, err := hex.DecodeString(string(pc.Verifier)) + if err != nil { + log.Fatalln("Unable to decode the key:", err.Error(), pc.Verifier) + } + conf.DSAPub = new([ed25519.PublicKeySize]byte) + copy(conf.DSAPub[:], keyDecoded) + + confs[*peerId] = &conf + } + return confs +} + +func confRefresh() { + confs = confRead() + ids := make([]govpn.PeerId, 0, len(confs)) + for peerId, _ := range confs { + ids = append(ids, peerId) + } + idsCache.Update(ids) +} + +func confInit() { + idsCache = govpn.NewCipherCache(nil) + confRefresh() + go func() { + for { + time.Sleep(RefreshRate) + confRefresh() + } + }() +} diff --git a/src/govpn/cmd/govpn-server/main.go b/src/govpn/cmd/govpn-server/main.go index dd338a2..be4e7eb 100644 --- a/src/govpn/cmd/govpn-server/main.go +++ b/src/govpn/cmd/govpn-server/main.go @@ -25,20 +25,19 @@ import ( "net" "os" "os/signal" - "path" "time" "govpn" ) var ( - bindAddr = flag.String("bind", "[::]:1194", "Bind to address") - proto = flag.String("proto", "udp", "Protocol to use: udp, tcp or all") - peersPath = flag.String("peers", "peers", "Path to peers keys directory") - stats = flag.String("stats", "", "Enable stats retrieving on host:port") - proxy = flag.String("proxy", "", "Enable HTTP proxy on host:port") - mtu = flag.Int("mtu", 1452, "MTU for outgoing packets") - egdPath = flag.String("egd", "", "Optional path to EGD socket") + bindAddr = flag.String("bind", "[::]:1194", "Bind to address") + proto = flag.String("proto", "udp", "Protocol to use: udp, tcp or all") + confPath = flag.String("conf", "peers.json", "Path to configuration JSON") + stats = flag.String("stats", "", "Enable stats retrieving on host:port") + proxy = flag.String("proxy", "", "Enable HTTP proxy on host:port") + mtu = flag.Int("mtu", 1452, "MTU for outgoing packets") + egdPath = flag.String("egd", "", "Optional path to EGD socket") ) func main() { @@ -48,7 +47,7 @@ func main() { log.Println(govpn.VersionGet()) govpn.MTU = *mtu - govpn.PeersInit(*peersPath) + confInit() knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer)) if *egdPath != "" { @@ -116,12 +115,10 @@ MainCycle: delete(peers, addr) delete(knownPeers, addr) delete(peersById, *ps.peer.Id) - downPath := path.Join( - govpn.PeersPath, - ps.peer.Id.String(), - "down.sh", + go govpn.ScriptCall( + confs[*ps.peer.Id].Down, + ps.tap.Name, ) - go govpn.ScriptCall(downPath, ps.tap.Name) ps.terminator <- struct{}{} } } diff --git a/src/govpn/cmd/govpn-server/tcp.go b/src/govpn/cmd/govpn-server/tcp.go index c1969fa..59ee559 100644 --- a/src/govpn/cmd/govpn-server/tcp.go +++ b/src/govpn/cmd/govpn-server/tcp.go @@ -72,12 +72,12 @@ func handleTCP(conn net.Conn) { break } prev += n - peerId := govpn.IDsCache.Find(buf[:prev]) + peerId := idsCache.Find(buf[:prev]) if peerId == nil { continue } if hs == nil { - conf = peerId.Conf() + conf = confs[*peerId] if conf == nil { log.Println("Can not get peer configuration:", peerId.String()) break diff --git a/src/govpn/cmd/govpn-server/udp.go b/src/govpn/cmd/govpn-server/udp.go index 148ff68..0f5e555 100644 --- a/src/govpn/cmd/govpn-server/udp.go +++ b/src/govpn/cmd/govpn-server/udp.go @@ -168,12 +168,12 @@ func startUDP() { } goto Finished CheckID: - peerId = govpn.IDsCache.Find(buf[:n]) + peerId = idsCache.Find(buf[:n]) if peerId == nil { log.Println("Unknown identity from:", addr) goto Finished } - conf = peerId.Conf() + conf = confs[*peerId] if conf == nil { log.Println("Unable to get peer configuration:", peerId.String()) goto Finished diff --git a/src/govpn/identify.go b/src/govpn/identify.go index 3f506ec..a0b7d22 100644 --- a/src/govpn/identify.go +++ b/src/govpn/identify.go @@ -22,22 +22,14 @@ import ( "crypto/subtle" "encoding/hex" "errors" - "io/ioutil" "log" - "os" - "path" - "strconv" - "strings" "sync" - "time" - "github.com/agl/ed25519" "golang.org/x/crypto/xtea" ) const ( - IDSize = 128 / 8 - RefreshRate = 60 * time.Second + IDSize = 128 / 8 ) type PeerId [IDSize]byte @@ -46,176 +38,76 @@ func (id PeerId) String() string { return hex.EncodeToString(id[:]) } -// Return human readable name of the peer. -// It equals either to peers/PEER/name file contents or PEER's hex. -func (id PeerId) MarshalJSON() ([]byte, error) { - result := id.String() - if name, err := ioutil.ReadFile(path.Join(PeersPath, result, "name")); err == nil { - result = strings.TrimRight(string(name), "\n") +// Decode identification string. +// It must be 32 hexadecimal characters long. +func IDDecode(raw string) (*PeerId, error) { + if len(raw) != IDSize*2 { + return nil, errors.New("ID must be 32 characters long") + } + idDecoded, err := hex.DecodeString(raw) + if err != nil { + return nil, errors.New("ID must contain hexadecimal characters only") } - return []byte(`"` + result + `"`), nil + idP := new([IDSize]byte) + copy(idP[:], idDecoded) + id := PeerId(*idP) + return &id, nil } -type cipherCache map[PeerId]*xtea.Cipher - -var ( - PeersPath string - IDsCache cipherCache - cipherCacheLock sync.RWMutex - dummyConf *PeerConf -) - -// Initialize (pre-cache) available peers info. -func PeersInit(path string) { - PeersPath = path - IDsCache = make(map[PeerId]*xtea.Cipher) - go func() { - for { - IDsCache.refresh() - time.Sleep(RefreshRate) - } - }() +type CipherCache struct { + c map[PeerId]*xtea.Cipher + l sync.RWMutex } -// Initialize dummy cache for client-side usage. -func PeersInitDummy(id *PeerId, conf *PeerConf) { - IDsCache = make(map[PeerId]*xtea.Cipher) - cipher, err := xtea.NewCipher(id[:]) - if err != nil { - panic(err) - } - IDsCache[*id] = cipher - dummyConf = conf +func NewCipherCache(peerIds []PeerId) CipherCache { + cc := CipherCache{c: make(map[PeerId]*xtea.Cipher, len(peerIds))} + cc.Update(peerIds) + return cc } -// Refresh IDsCache: remove disappeared keys, add missing ones with -// initialized ciphers. -func (cc cipherCache) refresh() { - dir, err := os.Open(PeersPath) - if err != nil { - panic(err) - } - peerIds, err := dir.Readdirnames(0) - if err != nil { - panic(err) - } - available := make(map[PeerId]bool) +// Remove disappeared keys, add missing ones with initialized ciphers. +func (cc CipherCache) Update(peerIds []PeerId) { + available := make(map[PeerId]struct{}) for _, peerId := range peerIds { - id, err := IDDecode(peerId) - if err != nil { - continue - } - available[*id] = true + available[peerId] = struct{}{} } - - cipherCacheLock.Lock() - // Cleanup deleted ones from cache - for k, _ := range cc { + cc.l.Lock() + for k, _ := range cc.c { if _, exists := available[k]; !exists { - delete(cc, k) - log.Println("Cleaning key: ", k) + log.Println("Cleaning key:", k) + delete(cc.c, k) } } - // Add missing ones for peerId, _ := range available { - if _, exists := cc[peerId]; !exists { + if _, exists := cc.c[peerId]; !exists { log.Println("Adding key", peerId) cipher, err := xtea.NewCipher(peerId[:]) if err != nil { panic(err) } - cc[peerId] = cipher + cc.c[peerId] = cipher } } - cipherCacheLock.Unlock() + cc.l.Unlock() } // Try to find peer's identity (that equals to an encryption key) // by taking first blocksize sized bytes from data at the beginning // as plaintext and last bytes as cyphertext. -func (cc cipherCache) Find(data []byte) *PeerId { +func (cc CipherCache) Find(data []byte) *PeerId { if len(data) < xtea.BlockSize*2 { return nil } buf := make([]byte, xtea.BlockSize) - cipherCacheLock.RLock() - for pid, cipher := range cc { + cc.l.RLock() + for pid, cipher := range cc.c { cipher.Decrypt(buf, data[len(data)-xtea.BlockSize:]) if subtle.ConstantTimeCompare(buf, data[:xtea.BlockSize]) == 1 { ppid := PeerId(pid) - cipherCacheLock.RUnlock() + cc.l.RUnlock() return &ppid } } - cipherCacheLock.RUnlock() + cc.l.RUnlock() return nil } - -func readIntFromFile(path string) (int, error) { - data, err := ioutil.ReadFile(path) - if err != nil { - return 0, err - } - val, err := strconv.Atoi(strings.TrimRight(string(data), "\n")) - if err != nil { - return 0, err - } - return val, nil -} - -// Get peer related configuration. -func (id *PeerId) Conf() *PeerConf { - if dummyConf != nil { - return dummyConf - } - conf := PeerConf{Id: id, NoiseEnable: false, CPR: 0} - peerPath := path.Join(PeersPath, id.String()) - - verPath := path.Join(peerPath, "verifier") - keyData, err := ioutil.ReadFile(verPath) - if err != nil { - log.Println("Unable to read verifier:", verPath) - return nil - } - if len(keyData) < ed25519.PublicKeySize*2 { - log.Println("Verifier must be 64 hex characters long:", verPath) - return nil - } - keyDecoded, err := hex.DecodeString(string(keyData[:ed25519.PublicKeySize*2])) - if err != nil { - log.Println("Unable to decode the key:", err.Error(), verPath) - return nil - } - conf.DSAPub = new([ed25519.PublicKeySize]byte) - copy(conf.DSAPub[:], keyDecoded) - - timeout := TimeoutDefault - if val, err := readIntFromFile(path.Join(peerPath, "timeout")); err == nil { - timeout = val - } - conf.Timeout = time.Second * time.Duration(timeout) - - if val, err := readIntFromFile(path.Join(peerPath, "noise")); err == nil && val == 1 { - conf.NoiseEnable = true - } - if val, err := readIntFromFile(path.Join(peerPath, "cpr")); err == nil { - conf.CPR = val - } - return &conf -} - -// Decode identification string. -// It must be 32 hexadecimal characters long. -func IDDecode(raw string) (*PeerId, error) { - if len(raw) != IDSize*2 { - return nil, errors.New("ID must be 32 characters long") - } - idDecoded, err := hex.DecodeString(raw) - if err != nil { - return nil, errors.New("ID must contain hexadecimal characters only") - } - idP := new([IDSize]byte) - copy(idP[:], idDecoded) - id := PeerId(*idP) - return &id, nil -} diff --git a/utils/newclient.sh b/utils/newclient.sh index e741130..9e1cb35 100755 --- a/utils/newclient.sh +++ b/utils/newclient.sh @@ -1,16 +1,10 @@ #!/bin/sh -e -getrand() -{ - local size=$1 - dd if=/dev/urandom bs=$size count=1 2>/dev/null | hexdump -ve '"%02x"' -} - [ -n "$1" ] || { cat < EOF @@ -18,11 +12,41 @@ EOF } username=$1 -peerid=$(getrand 16) +peerid=$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | hexdump -ve '"%02x"') +[ $(echo -n $peerid | wc -c) = 32 ] || peerid=0"$peerid" umask 077 -mkdir -p peers/$peerid -echo '0000000000000000000000000000000000000000000000000000000000000000' > peers/$peerid/verifier -echo $username > peers/$peerid/name -echo '#!/bin/sh' > peers/$peerid/up.sh -chmod 700 peers/$peerid/up.sh -echo Place verifier to peers/$peerid/verifier +passphrase=$(mktemp) +$(dirname $0)/storekey.sh $passphrase +verifier=$(govpn-verifier -id $peerid -key $passphrase) +rm -f $passphrase +echo + +cat <