Selaa lähdekoodia

Cert CLI stuff, module bump.

Adam Ierymenko 5 vuotta sitten
vanhempi
commit
fe01352412
9 muutettua tiedostoa jossa 162 lisäystä ja 19 poistoa
  1. 28 7
      cmd/zerotier/cli/cert.go
  2. 4 3
      cmd/zerotier/cli/help.go
  3. 17 1
      core/zerotier.h
  4. 1 1
      go.mod
  5. 2 2
      go.sum
  6. 86 0
      pkg/zerotier/api.go
  7. 12 0
      pkg/zerotier/certificate.go
  8. 11 4
      pkg/zerotier/node.go
  9. 1 1
      vendor/modules.txt

+ 28 - 7
cmd/zerotier/cli/cert.go

@@ -28,8 +28,6 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
 
 	switch args[0] {
 
-	case "list":
-
 	case "newsid":
 		if len(args) > 2 {
 			Help()
@@ -86,17 +84,18 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
 			Help()
 			return 1
 		}
-		var csr zerotier.Certificate
+
 		csrBytes, err := ioutil.ReadFile(args[1])
 		if err != nil {
 			fmt.Printf("ERROR: unable to read CSR from %s: %s\n", args[1], err.Error())
 			return 1
 		}
-		c, err := zerotier.NewCertificateFromBytes(csrBytes, false)
+		csr, err := zerotier.NewCertificateFromBytes(csrBytes, false)
 		if err != nil {
 			fmt.Printf("ERROR: CSR in %s is invalid: %s\n", args[1], err.Error())
 			return 1
 		}
+
 		id := readIdentity(args[2])
 		if id == nil {
 			fmt.Printf("ERROR: unable to read identity from %s\n", args[2])
@@ -106,12 +105,13 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
 			fmt.Printf("ERROR: signing identity in %s lacks private key\n", args[2])
 			return 1
 		}
-		c, err = csr.Sign(id)
+
+		cert, err := csr.Sign(id)
 		if err != nil {
 			fmt.Printf("ERROR: error signing CSR or generating certificate: %s\n", err.Error())
 			return 1
 		}
-		cb, err := c.Marshal()
+		cb, err := cert.Marshal()
 		if err != nil {
 			fmt.Printf("ERROR: error marshaling signed certificate: %s\n", err.Error())
 			return 1
@@ -124,7 +124,28 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
 			return 1
 		}
 
-	case "verify":
+	case "verify", "dump":
+		if len(args) != 2 {
+			Help()
+			return 1
+		}
+		certBytes, err := ioutil.ReadFile(args[1])
+		if err != nil {
+			fmt.Printf("ERROR: unable to read certificate from %s: %s\n", args[1], err.Error())
+			return 1
+		}
+		cert, err := zerotier.NewCertificateFromBytes(certBytes, true)
+		if err != nil {
+			fmt.Printf("FAILED: certificate in %s invalid: %s\n", args[1], err.Error())
+			return 1
+		}
+		if args[0] == "dump" {
+			fmt.Println(cert.JSON())
+		} else {
+			fmt.Println("OK")
+		}
+
+	case "list":
 
 	case "show":
 		if len(args) != 1 {

+ 4 - 3
cmd/zerotier/cli/help.go

@@ -37,7 +37,7 @@ Common Operations:
   help                                   Show this help
   version                                Print version
 
-  status                                 Show node status and configuration
+· status                                 Show node status and configuration
 
 · set [option] [value]                 - Get or set node configuration
     port <port>                          Primary P2P port
@@ -95,11 +95,12 @@ Advanced Operations:
 
   cert <command> [args]                - Certificate management
 ·   list                                 List certificates in local node store
-·   show [serial]                        List or show details of a certificate
+·   show <serial>                        List or show details of a certificate
     newsid <secret out>                  Create a new subject unique ID
     newcsr <subject> <secret> <csr out>  Create a subject CSR
     sign <csr> <identity> <cert out>     Sign a CSR to create a certificate
-·   verify <cert>                        Verify a certificate
+    verify <cert>                        Verify certificate (not entire chain)
+    dump <cert>                          Verify and print certificate
 ·   import <cert> [trust,[trust]]        Import certificate into this node
       rootca                             Certificate is a root CA (trust flag)
       ztrootset                          ZeroTier root node set (trust flag)

+ 17 - 1
core/zerotier.h

@@ -2411,7 +2411,7 @@ ZT_SDK_API int ZT_Node_tryPeer(
  * @param cert Certificate object, or set to NULL if certData and certSize are to be used
  * @param certData Certificate binary data if 'cert' is NULL, NULL otherwise
  * @param certSize Size of certificate binary data, 0 if none
- * @return
+ * @return Certificate error or ZT_CERTIFICATE_ERROR_NONE on success
  */
 ZT_SDK_API enum ZT_CertificateError ZT_Node_addCertificate(
 	ZT_Node *node,
@@ -2422,6 +2422,22 @@ ZT_SDK_API enum ZT_CertificateError ZT_Node_addCertificate(
 	const void *certData,
 	unsigned int certSize);
 
+/**
+ * Delete a certificate from this node's certificate store
+ *
+ * Note that deleting CA certificates may also imply deletion of certificates
+ * that depend on them for full chain verification.
+ *
+ * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
+ * @param serialNo 48-byte / 384-bit serial number of certificate to delete
+ * @return OK (0) or error code
+ */
+ZT_SDK_API enum ZT_ResultCode ZT_Node_deleteCertificate(
+	ZT_Node *node,
+	void *tptr,
+	const void *serialNo);
+
 /**
  * List certificates installed in this node's trust store
  *

+ 1 - 1
go.mod

@@ -5,5 +5,5 @@ go 1.13
 require (
 	github.com/Microsoft/go-winio v0.4.14
 	github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
-	golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect
+	golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d // indirect
 )

+ 2 - 2
go.sum

@@ -13,5 +13,5 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190529164535-6a60838ec259 h1:so6Hr/LodwSZ5UQDu/7PmQiDeS112WwtLvU3lpSPZTU=
 golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d h1:QQrM/CCYEzTs91GZylDCQjGHudbPTxF/1fvXdVh5lMo=
+golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

+ 86 - 0
pkg/zerotier/api.go

@@ -16,6 +16,7 @@ package zerotier
 import (
 	"bytes"
 	secrand "crypto/rand"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
@@ -217,10 +218,12 @@ func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool
 	if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) {
 		return true
 	}
+
 	ah = req.Header.Get("X-ZT1-Auth")
 	if len(ah) > 0 && strings.TrimSpace(ah) == token {
 		return true
 	}
+
 	_ = apiSendObj(out, req, http.StatusUnauthorized, &APIErr{"authorization token not found or incorrect (checked X-ZT1-Auth and Authorization headers)"})
 	return false
 }
@@ -252,6 +255,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 
 	smux := http.NewServeMux()
 
+	// -----------------------------------------------------------------------------------------------------------------
+
 	smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) {
 		defer func() {
 			e := recover()
@@ -298,6 +303,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 		}
 	})
 
+	// -----------------------------------------------------------------------------------------------------------------
+
 	smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) {
 		defer func() {
 			e := recover()
@@ -330,6 +337,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 		}
 	})
 
+	// -----------------------------------------------------------------------------------------------------------------
+
 	smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) {
 		var err error
 
@@ -397,6 +406,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 		}
 	})
 
+	// -----------------------------------------------------------------------------------------------------------------
+
 	smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) {
 		defer func() {
 			e := recover()
@@ -421,6 +432,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 		}
 
 		if req.Method == http.MethodDelete {
+
 			if queriedID == 0 {
 				_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only specific networks can be deleted"})
 				return
@@ -434,7 +446,9 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 				}
 			}
 			_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"})
+
 		} else if req.Method == http.MethodPost || req.Method == http.MethodPut {
+
 			if queriedID == 0 {
 				_ = apiSendObj(out, req, http.StatusBadRequest, nil)
 				return
@@ -460,7 +474,9 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 					_ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n))
 				}
 			}
+
 		} else if req.Method == http.MethodGet || req.Method == http.MethodHead {
+
 			networks := node.Networks()
 			if queriedID == 0 { // no queried ID lists all networks
 				nws := make([]*APINetwork, 0, len(networks))
@@ -477,12 +493,82 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 				}
 			}
 			_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"})
+
+		} else {
+			out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
+			_ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method})
+		}
+	})
+
+	// -----------------------------------------------------------------------------------------------------------------
+
+	smux.HandleFunc("/cert/", func(out http.ResponseWriter, req *http.Request) {
+		defer func() {
+			e := recover()
+			if e != nil {
+				_ = apiSendObj(out, req, http.StatusInternalServerError, nil)
+			}
+		}()
+
+		if !apiCheckAuth(out, req, authToken) {
+			return
+		}
+		apiSetStandardHeaders(out)
+
+		var queriedSerialNo []byte
+		if len(req.URL.Path) > 6 {
+			b, err := base64.URLEncoding.DecodeString(req.URL.Path[6:])
+			if err != nil || len(b) != CertificateSerialNoSize {
+				_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid base64 serial number in certificate path"})
+				return
+			}
+			queriedSerialNo = b
+		}
+
+		if req.Method == http.MethodGet || req.Method == http.MethodHead {
+
+			certs, err := node.ListCertificates()
+			if err != nil {
+				_ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"unexpected error listing certificates"})
+				return
+			}
+
+			if len(queriedSerialNo) == CertificateSerialNoSize {
+				for _, c := range certs {
+					if bytes.Equal(c.Certificate.SerialNo, queriedSerialNo) {
+						_ = apiSendObj(out, req, http.StatusOK, c)
+						break
+					}
+				}
+			} else {
+				_ = apiSendObj(out, req, http.StatusOK, certs)
+			}
+
+		} else if req.Method == http.MethodPost || req.Method == http.MethodPut {
+
+			var lc LocalCertificate
+			if apiReadObj(out, req, &lc) == nil {
+				if lc.Certificate == nil {
+					_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"missing certificate"})
+					return
+				}
+			}
+
+		} else if req.Method == http.MethodDelete {
+
+			if len(queriedSerialNo) == CertificateSerialNoSize {
+			} else {
+				_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"certificate not found"})
+			}
+
 		} else {
 			out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
 			_ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method})
 		}
 	})
 
+	// -----------------------------------------------------------------------------------------------------------------
+
 	listener, err := createNamedSocketListener(basePath, APISocketName)
 	if err != nil {
 		return nil, nil, err

+ 12 - 0
pkg/zerotier/certificate.go

@@ -18,6 +18,7 @@ package zerotier
 import "C"
 
 import (
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"unsafe"
@@ -96,6 +97,12 @@ type CertificateSubjectUniqueIDSecret struct {
 	UniqueIDSecret []byte `json:"uniqueIdSecret,omitempty"`
 }
 
+// LocalCertificate combines a certificate with its local trust flags.
+type LocalCertificate struct {
+	Certificate *Certificate `json:"certificate,omitempty"`
+	LocalTrust  uint         `json:"localTrust"`
+}
+
 func certificateErrorToError(cerr int) error {
 	switch cerr {
 	case C.ZT_CERTIFICATE_ERROR_NONE:
@@ -488,6 +495,11 @@ func (c *Certificate) JSON() string {
 	return string(j)
 }
 
+// URLSerialNo returns the serial number encoded for use in /cert/### local API URLs.
+func (c *Certificate) URLSerialNo() string {
+	return base64.URLEncoding.EncodeToString(c.SerialNo)
+}
+
 // NewCertificateSubjectUniqueId creates a new certificate subject unique ID and corresponding private key.
 // Right now only one type is supported: CertificateUniqueIdTypeNistP384
 func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) {

+ 11 - 4
pkg/zerotier/node.go

@@ -519,7 +519,7 @@ func (n *Node) TryPeer(fpOrAddress interface{}, ep *Endpoint, retries int) bool
 }
 
 // ListCertificates lists certificates and their corresponding local trust flags.
-func (n *Node) ListCertificates() (certs []*Certificate, localTrust []uint, err error) {
+func (n *Node) ListCertificates() (certs []LocalCertificate, err error) {
 	cl := C.ZT_Node_listCertificates(n.zn)
 	if cl != nil {
 		defer C.ZT_freeQueryResult(unsafe.Pointer(cl))
@@ -527,15 +527,22 @@ func (n *Node) ListCertificates() (certs []*Certificate, localTrust []uint, err
 			c := newCertificateFromCCertificate(unsafe.Pointer(uintptr(unsafe.Pointer(cl.certs)) + (i * pointerSize)))
 			if c != nil {
 				lt := *((*C.uint)(unsafe.Pointer(uintptr(unsafe.Pointer(cl.localTrust)) + (i * C.sizeof_int))))
-				certs = append(certs, c)
-				localTrust = append(localTrust, uint(lt))
+				certs = append(certs, LocalCertificate{Certificate: c, LocalTrust: uint(lt)})
 			}
 		}
 	}
 	return
 }
 
-// --------------------------------------------------------------------------------------------------------------------
+// AddCertificate adds a certificate to this node's local certificate store (after verification).
+func (n *Node) AddCertificate(cert *Certificate) error {
+}
+
+// DeleteCertificate deletes a certificate from this node's local certificate store.
+func (n *Node) DeleteCertificate(serialNo []byte) error {
+}
+
+// -------------------------------------------------------------------------------------------------------------------
 
 func (n *Node) runMaintenance() {
 	n.localConfigLock.RLock()

+ 1 - 1
vendor/modules.txt

@@ -4,6 +4,6 @@ github.com/Microsoft/go-winio/pkg/guid
 # github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
 github.com/hectane/go-acl
 github.com/hectane/go-acl/api
-# golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
+# golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d
 golang.org/x/sys/internal/unsafeheader
 golang.org/x/sys/windows