diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dd2d8034..d9f8e01e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,7 +222,7 @@ if(NOT PACKAGE_STATIC) CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" ) message("++ Adding flags for processor ${CMAKE_SYSTEM_PROCESSOR}") - add_compile_options(-maes -mrdrnd -mpclmul -msse -msse2 -mssse3) + add_compile_options(-maes -mrdrnd -mpclmul -msse -msse2 -mssse3 -msse4) endif() set(GO_BUILD_TAGS) diff --git a/cmd/zerotier/cli/cert.go b/cmd/zerotier/cli/cert.go index 64243312a..84c80b96a 100644 --- a/cmd/zerotier/cli/cert.go +++ b/cmd/zerotier/cli/cert.go @@ -13,5 +13,101 @@ package cli -func Cert(basePath, authToken string, args []string, jsonOutput bool) { +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "zerotier/pkg/zerotier" +) + + +func Cert(basePath, authToken string, args []string, jsonOutput bool) int { + if len(args) < 1 { + Help() + return 1 + } + + switch args[0] { + + case "newsid": + if len(args) > 2 { + Help() + return 1 + } + uniqueId, uniqueIdPrivate, err := zerotier.NewCertificateSubjectUniqueId(zerotier.CertificateUniqueIdTypeNistP384) + if err != nil { + fmt.Printf("ERROR: unable to create unique ID and private key: %s\n", err.Error()) + return 1 + } + sec, err := json.MarshalIndent(&zerotier.CertificateSubjectUniqueIDSecret{UniqueID: uniqueId, UniqueIDSecret: uniqueIdPrivate}, "", " ") + if err != nil { + fmt.Printf("ERROR: unable to create unique ID and private key: %s\n", err.Error()) + return 1 + } + if len(args) == 1 { + fmt.Println(string(sec)) + } else { + _ = ioutil.WriteFile(args[1], sec, 0600) + } + + case "newcsr": + if len(args) < 3 { + Help() + return 1 + } + var cs zerotier.CertificateSubject + csb, err := ioutil.ReadFile(args[1]) + if err != nil { + fmt.Printf("ERROR: unable to read subject from %s: %s\n", args[1], err.Error()) + return 1 + } + err = json.Unmarshal(csb, &cs) + if err != nil { + fmt.Printf("ERROR: unable to read subject from %s: %s\n", args[1], err.Error()) + return 1 + } + var subj zerotier.CertificateSubjectUniqueIDSecret + subjb, err := ioutil.ReadFile(args[2]) + if err != nil { + fmt.Printf("ERROR: unable to read unique ID secret from %s: %s\n", args[2], err.Error()) + return 1 + } + err = json.Unmarshal(subjb, &subj) + if err != nil { + fmt.Printf("ERROR: unable to read unique ID secret from %s: %s\n", args[2], err.Error()) + return 1 + } + csr, err := zerotier.NewCertificateCSR(&cs, subj.UniqueID, subj.UniqueIDSecret) + if err != nil { + fmt.Printf("ERROR: problem creating CSR: %s\n", err.Error()) + return 1 + } + if len(args) == 3 { + _, _ = os.Stdout.Write(csr) + } else { + _ = ioutil.WriteFile(args[3], csr, 0644) + } + + case "sign": + + case "verify": + + case "show": + if len(args) != 1 { + Help() + return 1 + } + + case "import": + + case "restore": + + case "export": + + case "delete": + + } + + return 0 } diff --git a/cmd/zerotier/cli/help.go b/cmd/zerotier/cli/help.go index b38fc97f6..b54b39eb5 100644 --- a/cmd/zerotier/cli/help.go +++ b/cmd/zerotier/cli/help.go @@ -76,14 +76,14 @@ Commands: sign Sign a file with an identity's key verify Verify a signature cert [args] - Certificate commands - newsubject Create a new subject and secret - newcsr Create a subject CSR - sign Sign a CSR to create a certificate + newsid [secret] Create a new subject unique ID + newcsr [csr] Create a subject CSR + sign [certificate] Sign a CSR to create a certificate verify Verify a certificate - show List certificate for current node import [] Import certificate into this node - export Export a certificate from this node - delete Delete certificate from this node + restore Re-import default certificates + export [path] Export a certificate from this node + delete Delete certificate from this node An
may be specified as a 10-digit short ZeroTier address, a fingerprint containing both an address and a SHA384 hash, or an identity. diff --git a/cmd/zerotier/cli/identity.go b/cmd/zerotier/cli/identity.go index 21536fc2c..ab6129e1c 100644 --- a/cmd/zerotier/cli/identity.go +++ b/cmd/zerotier/cli/identity.go @@ -17,13 +17,12 @@ import ( "encoding/hex" "fmt" "io/ioutil" - "os" "strings" "zerotier/pkg/zerotier" ) -func Identity(args []string) { +func Identity(args []string) int { if len(args) > 0 { switch args[0] { @@ -32,7 +31,7 @@ func Identity(args []string) { if len(args) > 1 { if len(args) > 2 { Help() - os.Exit(1) + return 1 } switch args[1] { case "c25519", "C25519", "0": @@ -41,37 +40,37 @@ func Identity(args []string) { idType = zerotier.IdentityTypeP384 default: Help() - os.Exit(1) + return 1 } } id, err := zerotier.NewIdentity(idType) if err != nil { fmt.Printf("ERROR: internal error generating identity: %s\n", err.Error()) - os.Exit(1) + return 1 } fmt.Println(id.PrivateKeyString()) - os.Exit(0) + return 0 case "getpublic": if len(args) == 2 { fmt.Println(readIdentity(args[1]).String()) - os.Exit(0) + return 0 } case "fingerprint": if len(args) == 2 { fmt.Println(readIdentity(args[1]).Fingerprint().String()) - os.Exit(0) + return 0 } case "validate": if len(args) == 2 { if readIdentity(args[1]).LocallyValidate() { fmt.Println("OK") - os.Exit(0) + return 0 } fmt.Println("FAILED") - os.Exit(1) + return 1 } case "sign", "verify": @@ -80,7 +79,7 @@ func Identity(args []string) { msg, err := ioutil.ReadFile(args[2]) if err != nil { fmt.Printf("ERROR: unable to read input file: %s\n", err.Error()) - os.Exit(1) + return 1 } if args[0] == "verify" { @@ -88,28 +87,29 @@ func Identity(args []string) { sig, err := hex.DecodeString(strings.TrimSpace(args[3])) if err != nil { fmt.Println("FAILED") - os.Exit(1) + return 1 } if id.Verify(msg, sig) { fmt.Println("OK") - os.Exit(0) + return 0 } } fmt.Println("FAILED") - os.Exit(1) + return 1 } else { sig, err := id.Sign(msg) if err != nil { fmt.Printf("ERROR: internal error signing message: %s\n", err.Error()) - os.Exit(1) + return 1 } fmt.Println(hex.EncodeToString(sig)) - os.Exit(0) + return 0 } } } } + Help() - os.Exit(1) + return 1 } diff --git a/cmd/zerotier/cli/join.go b/cmd/zerotier/cli/join.go index 54b4c7bbb..61b07656f 100644 --- a/cmd/zerotier/cli/join.go +++ b/cmd/zerotier/cli/join.go @@ -23,25 +23,23 @@ import ( "zerotier/pkg/zerotier" ) -func Join(basePath, authToken string, args []string) { +func Join(basePath, authToken string, args []string) int { joinOpts := flag.NewFlagSet("join", flag.ContinueOnError) controllerAuthToken := joinOpts.String("a", "", "") controllerFingerprint := joinOpts.String("c", "", "") err := joinOpts.Parse(os.Args[1:]) if err != nil { Help() - os.Exit(1) - return + return 1 } args = joinOpts.Args() if len(args) < 1 { Help() - os.Exit(1) - return + return 1 } if len(args[0]) != zerotier.NetworkIDStringLength { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - os.Exit(1) + return 1 } _ = *controllerAuthToken // TODO: not implemented yet @@ -52,13 +50,13 @@ func Join(basePath, authToken string, args []string) { fp, err = zerotier.NewFingerprintFromString(*controllerFingerprint) if err != nil { fmt.Printf("ERROR: invalid network controller fingerprint: %s\n", *controllerFingerprint) - os.Exit(1) + return 1 } } else { id, err := zerotier.NewIdentityFromString(*controllerFingerprint) if err != nil { fmt.Printf("ERROR: invalid network controller identity: %s\n", *controllerFingerprint) - os.Exit(1) + return 1 } fp = id.Fingerprint() } @@ -67,7 +65,7 @@ func Join(basePath, authToken string, args []string) { nwid, err := strconv.ParseUint(args[0], 16, 64) if err != nil { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - os.Exit(1) + return 1 } nwids := fmt.Sprintf("%.16x", nwid) @@ -85,5 +83,5 @@ func Join(basePath, authToken string, args []string) { } } - os.Exit(0) + return 0 } diff --git a/cmd/zerotier/cli/leave.go b/cmd/zerotier/cli/leave.go index 0cabb34da..b80cacd59 100644 --- a/cmd/zerotier/cli/leave.go +++ b/cmd/zerotier/cli/leave.go @@ -15,29 +15,29 @@ package cli import ( "fmt" - "os" "strconv" "zerotier/pkg/zerotier" ) -func Leave(basePath, authToken string, args []string) { +func Leave(basePath, authToken string, args []string) int { if len(args) != 1 { Help() - os.Exit(1) + return 1 } if len(args[0]) != zerotier.NetworkIDStringLength { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - os.Exit(1) + return 1 } nwid, err := strconv.ParseUint(args[0], 16, 64) if err != nil { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - os.Exit(1) + return 1 } nwids := fmt.Sprintf("%.16x", nwid) apiDelete(basePath, authToken, "/network/"+nwids, nil) fmt.Printf("OK %s", nwids) - os.Exit(0) + + return 0 } diff --git a/cmd/zerotier/cli/network.go b/cmd/zerotier/cli/network.go index 298496895..d602dece4 100644 --- a/cmd/zerotier/cli/network.go +++ b/cmd/zerotier/cli/network.go @@ -15,7 +15,6 @@ package cli import ( "fmt" - "os" "strconv" "strings" @@ -85,20 +84,20 @@ func showNetwork(nwids string, network *zerotier.APINetwork, jsonOutput bool) { } } -func Network(basePath, authToken string, args []string, jsonOutput bool) { +func Network(basePath, authToken string, args []string, jsonOutput bool) int { if len(args) < 1 { Help() - os.Exit(1) + return 1 } if len(args[0]) != zerotier.NetworkIDStringLength { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - os.Exit(1) + return 1 } nwid, err := strconv.ParseUint(args[0], 16, 64) if err != nil { fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - os.Exit(1) + return 1 } nwids := fmt.Sprintf("%.16x", nwid) @@ -114,6 +113,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) { case "set": if len(args) > 3 { Help() + return 1 } else if len(args) > 2 { fieldName := strings.ToLower(strings.TrimSpace(args[2])) var field *bool @@ -130,7 +130,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) { field = &network.Settings.AllowDefaultRouteOverride default: Help() - os.Exit(1) + return 1 } if len(args) == 3 { @@ -148,5 +148,5 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) { } } - os.Exit(0) + return 0 } diff --git a/cmd/zerotier/cli/networks.go b/cmd/zerotier/cli/networks.go index ed2d29233..f11355477 100644 --- a/cmd/zerotier/cli/networks.go +++ b/cmd/zerotier/cli/networks.go @@ -15,12 +15,10 @@ package cli import ( "fmt" - "os" - "zerotier/pkg/zerotier" ) -func Networks(basePath, authToken string, args []string, jsonOutput bool) { +func Networks(basePath, authToken string, args []string, jsonOutput bool) int { var networks []zerotier.APINetwork apiGet(basePath, authToken, "/network", &networks) @@ -44,5 +42,5 @@ func Networks(basePath, authToken string, args []string, jsonOutput bool) { } } - os.Exit(0) + return 0 } diff --git a/cmd/zerotier/cli/peers.go b/cmd/zerotier/cli/peers.go index 7d8d8071e..b665c286d 100644 --- a/cmd/zerotier/cli/peers.go +++ b/cmd/zerotier/cli/peers.go @@ -15,13 +15,12 @@ package cli import ( "fmt" - "os" "strings" "zerotier/pkg/zerotier" ) -func Peers(basePath, authToken string, args []string, jsonOutput bool, rootsOnly bool) { +func Peers(basePath, authToken string, args []string, jsonOutput bool, rootsOnly bool) int { var peers []zerotier.Peer apiGet(basePath, authToken, "/peer", &peers) @@ -64,5 +63,5 @@ func Peers(basePath, authToken string, args []string, jsonOutput bool, rootsOnly } } - os.Exit(0) + return 0 } diff --git a/cmd/zerotier/cli/service.go b/cmd/zerotier/cli/service.go index 14d58aae6..3467f57dd 100644 --- a/cmd/zerotier/cli/service.go +++ b/cmd/zerotier/cli/service.go @@ -25,10 +25,10 @@ import ( "zerotier/pkg/zerotier" ) -func Service(basePath string, args []string) { +func Service(basePath string, args []string) int { if len(args) > 0 { Help() - os.Exit(1) + return 1 } pidPath := path.Join(basePath, "zerotier.pid") @@ -45,5 +45,5 @@ func Service(basePath string, args []string) { } _ = os.Remove(pidPath) - os.Exit(1) + return 0 } diff --git a/cmd/zerotier/cli/set.go b/cmd/zerotier/cli/set.go index 245ce5705..cf1b8f7ac 100644 --- a/cmd/zerotier/cli/set.go +++ b/cmd/zerotier/cli/set.go @@ -13,5 +13,6 @@ package cli -func Set(basePath, authToken string, args []string) { +func Set(basePath, authToken string, args []string) int { + return 0 } diff --git a/cmd/zerotier/cli/status.go b/cmd/zerotier/cli/status.go index bf098e2cd..286a1d7c9 100644 --- a/cmd/zerotier/cli/status.go +++ b/cmd/zerotier/cli/status.go @@ -15,12 +15,10 @@ package cli import ( "fmt" - "os" - "zerotier/pkg/zerotier" ) -func Status(basePath, authToken string, args []string, jsonOutput bool) { +func Status(basePath, authToken string, args []string, jsonOutput bool) int { var status zerotier.APIStatus apiGet(basePath, authToken, "/status", &status) @@ -70,5 +68,5 @@ func Status(basePath, authToken string, args []string, jsonOutput bool) { fmt.Printf("\n") } - os.Exit(0) + return 0 } diff --git a/cmd/zerotier/zerotier.go b/cmd/zerotier/zerotier.go index 3fb064e75..f41992038 100644 --- a/cmd/zerotier/zerotier.go +++ b/cmd/zerotier/zerotier.go @@ -27,33 +27,30 @@ import ( "zerotier/pkg/zerotier" ) -func getAuthTokenPaths(basePath string) (p []string) { - p = append(p, path.Join(basePath, "authtoken.secret")) - userHome, _ := os.UserHomeDir() - if len(userHome) > 0 { - if runtime.GOOS == "darwin" { - p = append(p, path.Join(userHome, "Library", "Application Support", "ZeroTier", "authtoken.secret")) - p = append(p, path.Join(userHome, "Library", "Application Support", "ZeroTier", "One", "authtoken.secret")) - } - p = append(p, path.Join(userHome, ".zerotierauth")) - p = append(p, path.Join(userHome, ".zeroTierOneAuthToken")) - } - return p -} - -func authTokenRequired(basePath, tflag, tTflag string) string { - authTokenPaths := getAuthTokenPaths(basePath) +func authToken(basePath, tflag, tTflag string) string { var authToken string if len(tflag) > 0 { at, err := ioutil.ReadFile(tflag) if err != nil || len(at) == 0 { fmt.Println("FATAL: unable to read local service API authorization token from " + tflag) - os.Exit(1) + return "" } authToken = string(at) } else if len(tTflag) > 0 { authToken = tTflag } else { + var authTokenPaths []string + authTokenPaths = append(authTokenPaths, path.Join(basePath, "authtoken.secret")) + userHome, _ := os.UserHomeDir() + if len(userHome) > 0 { + if runtime.GOOS == "darwin" { + authTokenPaths = append(authTokenPaths, path.Join(userHome, "Library", "Application Support", "ZeroTier", "authtoken.secret")) + authTokenPaths = append(authTokenPaths, path.Join(userHome, "Library", "Application Support", "ZeroTier", "One", "authtoken.secret")) + } + authTokenPaths = append(authTokenPaths, path.Join(userHome, ".zerotierauth")) + authTokenPaths = append(authTokenPaths, path.Join(userHome, ".zeroTierOneAuthToken")) + } + for _, p := range authTokenPaths { tmp, _ := ioutil.ReadFile(p) if len(tmp) > 0 { @@ -61,19 +58,22 @@ func authTokenRequired(basePath, tflag, tTflag string) string { break } } + if len(authToken) == 0 { fmt.Println("FATAL: unable to read local service API authorization token from any of:") for _, p := range authTokenPaths { fmt.Println(" " + p) } - os.Exit(1) + return "" } } + authToken = strings.TrimSpace(authToken) if len(authToken) == 0 { fmt.Println("FATAL: unable to read API authorization token from command line or any filesystem location.") - os.Exit(1) + return "" } + return authToken } @@ -111,37 +111,39 @@ func main() { basePath = *pflag } + exitCode := 0 switch args[0] { default: - //case "help": + cli.Help() + exitCode = 1 + case "help": cli.Help() case "version": fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) case "service": - cli.Service(basePath, cmdArgs) + exitCode = cli.Service(basePath, cmdArgs) case "status", "info": - cli.Status(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) + exitCode = cli.Status(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) case "join": - cli.Join(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs) + exitCode = cli.Join(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs) case "leave": - cli.Leave(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs) + exitCode = cli.Leave(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs) case "networks", "listnetworks": - cli.Networks(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) + exitCode = cli.Networks(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) case "network": - cli.Network(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) + exitCode = cli.Network(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) case "peers", "listpeers", "lspeers": - cli.Peers(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag, false) + exitCode = cli.Peers(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag, false) case "peer": - authTokenRequired(basePath, *tflag, *tTflag) case "roots": - cli.Peers(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag, true) + exitCode = cli.Peers(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag, true) case "controller": case "set": - cli.Set(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs) + exitCode = cli.Set(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs) case "identity": - cli.Identity(cmdArgs) + exitCode = cli.Identity(cmdArgs) case "cert": - cli.Cert(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) + exitCode = cli.Cert(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) } - os.Exit(0) + os.Exit(exitCode) } diff --git a/core/Protocol.hpp b/core/Protocol.hpp index b8c9c826b..9f45fd858 100644 --- a/core/Protocol.hpp +++ b/core/Protocol.hpp @@ -106,7 +106,9 @@ * 9 - 1.2.0 ... 1.2.14 * 10 - 1.4.0 ... 1.4.6 * + Contained early pre-alpha versions of multipath, which are deprecated - * 11 - 2.0.0 ... CURRENT + * 11 - 1.4.8 ... CURRENT 1.4 + * + AES-GMAC-SIV backported for faster peer-to-peer crypto + * 20 - 2.0.0 ... CURRENT * + New more WAN-efficient P2P-assisted multicast algorithm * + HELLO and OK(HELLO) include an extra HMAC to harden authentication * + HELLO and OK(HELLO) carry meta-data in a dictionary that's encrypted @@ -117,7 +119,7 @@ * + Short probe packets to reduce probe bandwidth * + More aggressive NAT traversal techniques for IPv4 symmetric NATs */ -#define ZT_PROTO_VERSION 11 +#define ZT_PROTO_VERSION 20 /** * Minimum supported protocol version diff --git a/core/Utils.hpp b/core/Utils.hpp index 8ffbedd05..3178dcd7b 100644 --- a/core/Utils.hpp +++ b/core/Utils.hpp @@ -16,11 +16,13 @@ #include "Constants.hpp" +#include +#include + #include #include #include -#include -#include +#include namespace ZeroTier { diff --git a/pkg/zerotier/certificate.go b/pkg/zerotier/certificate.go index 9d0cc557f..d2dc23a48 100644 --- a/pkg/zerotier/certificate.go +++ b/pkg/zerotier/certificate.go @@ -87,6 +87,12 @@ type Certificate struct { Signature []byte `json:"signature,omitempty"` } +// CertificateSubjectUniqueIDSecret bundles a certificate subject unique ID and its secret key. +type CertificateSubjectUniqueIDSecret struct { + UniqueID []byte `json:"uniqueId,omitempty"` + UniqueIDSecret []byte `json:"uniqueIdSecret,omitempty"` +} + func certificateErrorToError(cerr int) error { switch cerr { case C.ZT_CERTIFICATE_ERROR_NONE: