Ver código fonte

Certificate plumbing in Go.

Adam Ierymenko 5 anos atrás
pai
commit
81530e5990

+ 1 - 1
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)

+ 97 - 1
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
 }

+ 6 - 6
cmd/zerotier/cli/help.go

@@ -76,14 +76,14 @@ Commands:
     sign <identity> <file>               Sign a file with an identity's key
     verify <identity> <file> <sig>       Verify a signature
   cert <command> [args]                - Certificate commands
-    newsubject <subject> <secret>        Create a new subject and secret
-    newcsr <subject> <secret>            Create a subject CSR
-    sign <csr> <identity> <certificate>  Sign a CSR to create a certificate
+    newsid [secret]                      Create a new subject unique ID
+    newcsr <subject> <secret> [csr]      Create a subject CSR
+    sign <csr> <identity> [certificate]  Sign a CSR to create a certificate
     verify <certificate>                 Verify a certificate
-    show                                 List certificate for current node
     import <certificate> [<trust>]       Import certificate into this node
-    export <serial>                      Export a certificate from this node
-    delete <serial>                      Delete certificate from this node
+    restore                              Re-import default certificates
+    export <serial> [path]               Export a certificate from this node
+    delete <serial|ALL>                  Delete certificate from this node
 
 An <address> may be specified as a 10-digit short ZeroTier address, a
 fingerprint containing both an address and a SHA384 hash, or an identity.

+ 17 - 17
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
 }

+ 8 - 10
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
 }

+ 6 - 6
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
 }

+ 7 - 7
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
 }

+ 2 - 4
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
 }

+ 2 - 3
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
 }

+ 3 - 3
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
 }

+ 2 - 1
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
 }

+ 2 - 4
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
 }

+ 35 - 33
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)
 }

+ 4 - 2
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

+ 4 - 2
core/Utils.hpp

@@ -16,11 +16,13 @@
 
 #include "Constants.hpp"
 
+#include <stddef.h>
+#include <stdarg.h>
+
 #include <utility>
 #include <algorithm>
 #include <memory>
-#include <stdint.h>
-#include <stddef.h>
+#include <stdexcept>
 
 namespace ZeroTier {
 

+ 6 - 0
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: