2
0
Эх сурвалжийг харах

Evil not very fun CGO glue for Certificate works! Also added a self-test project for Go code. Not using "go test" because of CMake build and CGO linking requirements that make it a pain in the derriere.

Adam Ierymenko 5 жил өмнө
parent
commit
451e6be180

+ 9 - 0
CMakeLists.txt

@@ -261,6 +261,15 @@ if(NOT PACKAGE_STATIC)
 		endif()
 	endif(WIN32)
 
+	add_custom_target(
+		zt_service_tests ALL
+		BYPRODUCTS ${CMAKE_BINARY_DIR}/zt_service_tests
+		SOURCES ${go_src}
+		COMMAND ${CMAKE_COMMAND} -E env ${GOARCH} ${GOARM} CGO_ENABLED=1 CGO_CFLAGS=\"-O3\" CGO_LDFLAGS=\"$<TARGET_FILE:zt_core> $<TARGET_FILE:zt_controller> $<TARGET_FILE:zt_service_io_core> $<TARGET_FILE:zt_osdep> ${GO_EXTRA_LIBRARIES}\" ${GO} build -mod=vendor ${GOFLAGS} -o ${CMAKE_BINARY_DIR}/zt_service_tests ${CMAKE_SOURCE_DIR}/cmd/zt_service_tests/*.go
+		COMMENT "Compiling zt_service_tests (Go/cgo self-tests)..."
+	)
+	add_dependencies(zt_service_tests zt_osdep zt_core zt_controller zt_service_io_core)
+
 	add_custom_target(
 		zerotier ALL
 		BYPRODUCTS ${CMAKE_BINARY_DIR}/zerotier

+ 111 - 0
cmd/zt_service_tests/certificate.go

@@ -0,0 +1,111 @@
+/*
+ * Copyright (C)2013-2020 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2024-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"zerotier/pkg/zerotier"
+)
+
+func TestCertificate() bool {
+	fmt.Println("Checking Certificate conversion to/from C ZT_Certificate structure...")
+
+	id, err := zerotier.NewIdentityFromString("8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e")
+	if err != nil {
+		fmt.Printf("  Error deserializing test identity: %s\n", err.Error())
+		return false
+	}
+
+	var c zerotier.Certificate
+
+	c.SerialNo = make([]byte, 48)
+	for i := 0; i < 48 ;i++ {
+		c.SerialNo[i] = byte(i)
+	}
+	c.Flags = 1234
+	c.Timestamp = 5678
+	c.Validity[0] = 1010
+	c.Validity[1] = 2020
+	c.Subject.Timestamp = 31337
+	c.Subject.Identities = append(c.Subject.Identities, zerotier.CertificateIdentity{
+		Identity: id,
+		Locator: nil,
+	})
+	c.Subject.Networks = append(c.Subject.Networks, zerotier.CertificateNetwork{
+		ID: 1111,
+		Controller: zerotier.Fingerprint{
+			Address: zerotier.Address(2222),
+			Hash: c.SerialNo,
+		},
+	})
+	c.Subject.Certificates = append(c.Subject.Certificates, c.SerialNo)
+	c.Subject.UpdateURLs = append(c.Subject.UpdateURLs, "https://www.zerotier.com/asdfasdf")
+	c.Subject.Name.SerialNo = "a"
+	c.Subject.Name.CommonName = "b"
+	c.Subject.Name.StreetAddress = "c"
+	c.Subject.Name.Locality = "d"
+	c.Subject.Name.Province = "e"
+	c.Subject.Name.PostalCode = "f"
+	c.Subject.Name.Country = "g"
+	c.Subject.Name.Organization = "h"
+	c.Subject.Name.Unit = "i"
+	c.Subject.Name.Email = "j"
+	c.Subject.Name.URL = "k"
+	c.Subject.Name.Host = "l"
+	c.Subject.UniqueID = []byte("asdf")
+	c.Subject.UniqueIDProofSignature = []byte("ghij")
+	c.Issuer = id
+	c.IssuerName.SerialNo = "m"
+	c.IssuerName.CommonName = "n"
+	c.IssuerName.StreetAddress = "o"
+	c.IssuerName.Locality = "p"
+	c.IssuerName.Province = "q"
+	c.IssuerName.PostalCode = "r"
+	c.IssuerName.Country = "s"
+	c.IssuerName.Organization = "t"
+	c.IssuerName.Unit = "u"
+	c.IssuerName.Email = "v"
+	c.IssuerName.URL = "w"
+	c.IssuerName.Host = "x"
+	c.ExtendedAttributes = c.SerialNo
+	c.MaxPathLength = 9999
+	c.Signature = []byte("qwerty")
+
+	cc := c.CCertificate()
+	if cc == nil {
+		fmt.Println("  Error converting Certificate to ZT_Certificate")
+		return false
+	}
+
+	c2 := zerotier.NewCertificateFromCCertificate(cc.C)
+	if c2 == nil {
+		fmt.Println("  Error converting ZT_Certificate to Certificate")
+		return false
+	}
+
+	j, _ := json.MarshalIndent(c, "", "  ")
+	j2, _ := json.MarshalIndent(c2, "", "  ")
+
+	if !bytes.Equal(j, j2) {
+		j, _ = json.MarshalIndent(c, "", "  ")
+		fmt.Print("  Deep equality test failed: certificates do not match! (see dumps below)\n\n")
+		fmt.Println(string(j))
+		fmt.Println(string(j2))
+		return false
+	}
+
+	return true
+}

+ 10 - 0
cmd/zt_service_tests/zt_service_tests.go

@@ -0,0 +1,10 @@
+package main
+
+import "os"
+
+func main() {
+	if !TestCertificate() {
+		os.Exit(1)
+	}
+}
+

+ 100 - 76
pkg/zerotier/certificate.go

@@ -49,20 +49,20 @@ type CertificateIdentity struct {
 
 // CertificateNetwork bundles a network ID with the fingerprint of its primary controller.
 type CertificateNetwork struct {
-	ID         uint64       `json:"id"`
-	Controller Fingerprint  `json:"controller"`
+	ID         uint64      `json:"id"`
+	Controller Fingerprint `json:"controller"`
 }
 
 // CertificateSubject contains information about the subject of a certificate.
 type CertificateSubject struct {
-	Timestamp              int64                           `json:"timestamp"`
-	Identities             []CertificateIdentity           `json:"identities,omitempty"`
-	Networks               []CertificateNetwork            `json:"networks,omitempty"`
-	Certificates           [][CertificateSerialNoSize]byte `json:"certificates,omitempty"`
-	UpdateURLs             []string                        `json:"updateURLs,omitempty"`
-	Name                   CertificateName                 `json:"name"`
-	UniqueID               []byte                          `json:"uniqueId,omitempty"`
-	UniqueIDProofSignature []byte                          `json:"uniqueIdProofSignature,omitempty"`
+	Timestamp              int64                 `json:"timestamp"`
+	Identities             []CertificateIdentity `json:"identities,omitempty"`
+	Networks               []CertificateNetwork  `json:"networks,omitempty"`
+	Certificates           [][]byte              `json:"certificates,omitempty"`
+	UpdateURLs             []string              `json:"updateURLs,omitempty"`
+	Name                   CertificateName       `json:"name"`
+	UniqueID               []byte                `json:"uniqueId,omitempty"`
+	UniqueIDProofSignature []byte                `json:"uniqueIdProofSignature,omitempty"`
 }
 
 // Certificate is a Go reflection of the C ZT_Certificate struct.
@@ -79,25 +79,30 @@ type Certificate struct {
 	Signature          []byte             `json:"signature,omitempty"`
 }
 
-type cCertificate struct {
-	C                             C.ZT_Certificate
+type CCertificate struct {
+	C                             unsafe.Pointer
+	internalCertificate           C.ZT_Certificate
 	internalSubjectIdentities     []C.ZT_Certificate_Identity
 	internalSubjectNetworks       []C.ZT_Certificate_Network
-	internalSubjectUpdateURLs     []*C.char
+	internalSubjectCertificates   []uintptr
+	internalSubjectUpdateURLs     []uintptr
 	internalSubjectUpdateURLsData [][]byte
 }
 
-// newCertificateFromCCertificate translates a C ZT_Certificate into a Go Certificate.
-func newCertificateFromCCertificate(cc *C.ZT_Certificate) *Certificate {
+// NewCertificateFromCCertificate translates a C ZT_Certificate into a Go Certificate.
+func NewCertificateFromCCertificate(ccptr unsafe.Pointer) *Certificate {
+	cc := (*C.ZT_Certificate)(ccptr)
 	c := new(Certificate)
 
 	if cc == nil {
 		return c
 	}
 
-	c.SerialNo = C.GoBytes(unsafe.Pointer(&cc.serialNo[0]), 48)
-	if allZero(c.SerialNo) {
-		c.SerialNo = nil
+	sn := (*[48]byte)(unsafe.Pointer(&cc.serialNo[0]))[:]
+	if !allZero(sn) {
+		var tmp [48]byte
+		copy(tmp[:], sn)
+		c.SerialNo = tmp[:]
 	}
 	c.Flags = uint64(cc.flags)
 	c.Timestamp = int64(cc.timestamp)
@@ -135,14 +140,16 @@ func newCertificateFromCCertificate(cc *C.ZT_Certificate) *Certificate {
 			return nil
 		}
 		c.Subject.Networks = append(c.Subject.Networks, CertificateNetwork{
-			ID: uint64(cn.id),
+			ID:         uint64(cn.id),
 			Controller: *fp,
 		})
 	}
 
 	for i := 0; i < int(cc.subject.certificateCount); i++ {
-		csn := (*[48]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.certificates)) + (uintptr(i) * pointerSize)))
-		c.Subject.Certificates = append(c.Subject.Certificates, *csn)
+		csn := *((**[48]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.certificates)) + (uintptr(i) * pointerSize))))
+		var tmp [48]byte
+		copy(tmp[:], csn[:])
+		c.Subject.Certificates = append(c.Subject.Certificates, tmp[:])
 	}
 
 	for i := 0; i < int(cc.subject.updateURLCount); i++ {
@@ -204,26 +211,36 @@ func newCertificateFromCCertificate(cc *C.ZT_Certificate) *Certificate {
 	return c
 }
 
-// cCertificate creates a C ZT_Certificate structure from the content of a Certificate.
+// CCertificate creates a C ZT_Certificate structure from the content of a Certificate.
+//
 // This will return nil if an error occurs, which would indicate an invalid C
 // structure or one with invalid values.
+//
 // The returned Go structure bundles this with some objects that have
 // to be created to set their pointers in ZT_Certificate. It's easier to
 // manage allocation of these in Go and bundle them so Go's GC will clean
-// them up automatically when cCertificate is released. Only the 'C' field
-// in cCertificate should be directly used.
-func (c *Certificate) cCertificate() *cCertificate {
-	var cc cCertificate
+// them up automatically when CCertificate is released. Only the 'C' field
+// in CCertificate should be directly used. The rest are internal and are
+// hidden outside the package.
+//
+// Ensure that Certificate is not modified until the generated C certificate
+// is no longer in use.
+func (c *Certificate) CCertificate() *CCertificate {
+	var cc CCertificate
+	var ccC *C.ZT_Certificate
+
+	cc.C = unsafe.Pointer(&cc.internalCertificate)
+	ccC = &cc.internalCertificate
 
 	if len(c.SerialNo) == 48 {
-		copy((*[48]byte)(unsafe.Pointer(&cc.C.serialNo[0]))[:], c.SerialNo[:])
+		copy((*[48]byte)(unsafe.Pointer(&ccC.serialNo[0]))[:], c.SerialNo)
 	}
-	cc.C.flags = C.uint64_t(c.Flags)
-	cc.C.timestamp = C.int64_t(c.Timestamp)
-	cc.C.validity[0] = C.int64_t(c.Validity[0])
-	cc.C.validity[1] = C.int64_t(c.Validity[1])
+	ccC.flags = C.uint64_t(c.Flags)
+	ccC.timestamp = C.int64_t(c.Timestamp)
+	ccC.validity[0] = C.int64_t(c.Validity[0])
+	ccC.validity[1] = C.int64_t(c.Validity[1])
 
-	cc.C.subject.timestamp = C.int64_t(c.Subject.Timestamp)
+	ccC.subject.timestamp = C.int64_t(c.Subject.Timestamp)
 
 	if len(c.Subject.Identities) > 0 {
 		cc.internalSubjectIdentities = make([]C.ZT_Certificate_Identity, len(c.Subject.Identities))
@@ -236,8 +253,8 @@ func (c *Certificate) cCertificate() *cCertificate {
 				cc.internalSubjectIdentities[i].locator = id.Locator.cl
 			}
 		}
-		cc.C.subject.identities = &cc.internalSubjectIdentities[0]
-		cc.C.subject.identityCount = C.uint(len(c.Subject.Identities))
+		ccC.subject.identities = &cc.internalSubjectIdentities[0]
+		ccC.subject.identityCount = C.uint(len(c.Subject.Identities))
 	}
 
 	if len(c.Subject.Networks) > 0 {
@@ -249,45 +266,52 @@ func (c *Certificate) cCertificate() *cCertificate {
 				copy((*[48]byte)(unsafe.Pointer(&cc.internalSubjectNetworks[i].controller.hash[0]))[:], n.Controller.Hash)
 			}
 		}
-		cc.C.subject.networks = &cc.internalSubjectNetworks[0]
-		cc.C.subject.networkCount = C.uint(len(c.Subject.Networks))
+		ccC.subject.networks = &cc.internalSubjectNetworks[0]
+		ccC.subject.networkCount = C.uint(len(c.Subject.Networks))
 	}
 
 	if len(c.Subject.Certificates) > 0 {
-		cc.C.subject.certificates = (**C.uint8_t)(unsafe.Pointer(&c.Subject.Certificates[0]))
-		cc.C.subject.certificateCount = C.uint(len(c.Subject.Certificates))
+		cc.internalSubjectCertificates = make([]uintptr, len(c.Subject.Certificates))
+		for i, cert := range c.Subject.Certificates {
+			if len(cert) != 48 {
+				return nil
+			}
+			cc.internalSubjectCertificates[i] = uintptr(unsafe.Pointer(&cert[0]))
+		}
+		ccC.subject.certificates = (**C.uint8_t)(unsafe.Pointer(&cc.internalSubjectCertificates[0]))
+		ccC.subject.certificateCount = C.uint(len(c.Subject.Certificates))
 	}
 
 	if len(c.Subject.UpdateURLs) > 0 {
-		cc.internalSubjectUpdateURLs = make([]*C.char, len(c.Subject.UpdateURLs))
+		cc.internalSubjectUpdateURLs = make([]uintptr, len(c.Subject.UpdateURLs))
 		cc.internalSubjectUpdateURLsData = make([][]byte, len(c.Subject.UpdateURLs))
 		for i, u := range c.Subject.UpdateURLs {
 			cc.internalSubjectUpdateURLsData[i] = stringAsZeroTerminatedBytes(u)
-			cc.internalSubjectUpdateURLs[i] = (*C.char)(unsafe.Pointer(&cc.internalSubjectUpdateURLsData[0]))
+			cc.internalSubjectUpdateURLs[i] = uintptr(unsafe.Pointer(&cc.internalSubjectUpdateURLsData[0][0]))
 		}
-		cc.C.subject.updateURLs = (**C.char)(unsafe.Pointer(&cc.internalSubjectUpdateURLs[0]))
-		cc.C.subject.updateURLCount = C.uint(len(c.Subject.UpdateURLs))
+		ccC.subject.updateURLs = (**C.char)(unsafe.Pointer(&cc.internalSubjectUpdateURLs[0]))
+		ccC.subject.updateURLCount = C.uint(len(c.Subject.UpdateURLs))
 	}
 
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.serialNo[0]), CertificateMaxStringLength+1, c.Subject.Name.SerialNo)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.commonName[0]), CertificateMaxStringLength+1, c.Subject.Name.CommonName)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.country[0]), CertificateMaxStringLength+1, c.Subject.Name.Country)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.organization[0]), CertificateMaxStringLength+1, c.Subject.Name.Organization)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.unit[0]), CertificateMaxStringLength+1, c.Subject.Name.Unit)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.locality[0]), CertificateMaxStringLength+1, c.Subject.Name.Locality)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.province[0]), CertificateMaxStringLength+1, c.Subject.Name.Province)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.streetAddress[0]), CertificateMaxStringLength+1, c.Subject.Name.StreetAddress)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.postalCode[0]), CertificateMaxStringLength+1, c.Subject.Name.PostalCode)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.email[0]), CertificateMaxStringLength+1, c.Subject.Name.Email)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.url[0]), CertificateMaxStringLength+1, c.Subject.Name.URL)
-	cStrCopy(unsafe.Pointer(&cc.C.subject.name.host[0]), CertificateMaxStringLength+1, c.Subject.Name.Host)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.serialNo[0]), CertificateMaxStringLength+1, c.Subject.Name.SerialNo)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.commonName[0]), CertificateMaxStringLength+1, c.Subject.Name.CommonName)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.country[0]), CertificateMaxStringLength+1, c.Subject.Name.Country)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.organization[0]), CertificateMaxStringLength+1, c.Subject.Name.Organization)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.unit[0]), CertificateMaxStringLength+1, c.Subject.Name.Unit)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.locality[0]), CertificateMaxStringLength+1, c.Subject.Name.Locality)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.province[0]), CertificateMaxStringLength+1, c.Subject.Name.Province)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.streetAddress[0]), CertificateMaxStringLength+1, c.Subject.Name.StreetAddress)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.postalCode[0]), CertificateMaxStringLength+1, c.Subject.Name.PostalCode)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.email[0]), CertificateMaxStringLength+1, c.Subject.Name.Email)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.url[0]), CertificateMaxStringLength+1, c.Subject.Name.URL)
+	cStrCopy(unsafe.Pointer(&ccC.subject.name.host[0]), CertificateMaxStringLength+1, c.Subject.Name.Host)
 
 	if len(c.Subject.UniqueID) > 0 {
-		cc.C.subject.uniqueId = (*C.uint8_t)(unsafe.Pointer(&c.Subject.UniqueID[0]))
-		cc.C.subject.uniqueIdSize = C.uint(len(c.Subject.UniqueID))
+		ccC.subject.uniqueId = (*C.uint8_t)(unsafe.Pointer(&c.Subject.UniqueID[0]))
+		ccC.subject.uniqueIdSize = C.uint(len(c.Subject.UniqueID))
 		if len(c.Subject.UniqueIDProofSignature) > 0 {
-			cc.C.subject.uniqueIdProofSignature = (*C.uint8_t)(unsafe.Pointer(&c.Subject.UniqueIDProofSignature[0]))
-			cc.C.subject.uniqueIdProofSignatureSize = C.uint(len(c.Subject.UniqueIDProofSignature))
+			ccC.subject.uniqueIdProofSignature = (*C.uint8_t)(unsafe.Pointer(&c.Subject.UniqueIDProofSignature[0]))
+			ccC.subject.uniqueIdProofSignatureSize = C.uint(len(c.Subject.UniqueIDProofSignature))
 		}
 	}
 
@@ -295,32 +319,32 @@ func (c *Certificate) cCertificate() *cCertificate {
 		if !c.Issuer.initCIdentityPtr() {
 			return nil
 		}
-		cc.C.issuer = c.Issuer.cid
+		ccC.issuer = c.Issuer.cid
 	}
 
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.serialNo[0]), CertificateMaxStringLength+1, c.IssuerName.SerialNo)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.commonName[0]), CertificateMaxStringLength+1, c.IssuerName.CommonName)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.country[0]), CertificateMaxStringLength+1, c.IssuerName.Country)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.organization[0]), CertificateMaxStringLength+1, c.IssuerName.Organization)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.unit[0]), CertificateMaxStringLength+1, c.IssuerName.Unit)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.locality[0]), CertificateMaxStringLength+1, c.IssuerName.Locality)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.province[0]), CertificateMaxStringLength+1, c.IssuerName.Province)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.streetAddress[0]), CertificateMaxStringLength+1, c.IssuerName.StreetAddress)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.postalCode[0]), CertificateMaxStringLength+1, c.IssuerName.PostalCode)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.email[0]), CertificateMaxStringLength+1, c.IssuerName.Email)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.url[0]), CertificateMaxStringLength+1, c.IssuerName.URL)
-	cStrCopy(unsafe.Pointer(&cc.C.issuerName.host[0]), CertificateMaxStringLength+1, c.IssuerName.Host)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.serialNo[0]), CertificateMaxStringLength+1, c.IssuerName.SerialNo)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.commonName[0]), CertificateMaxStringLength+1, c.IssuerName.CommonName)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.country[0]), CertificateMaxStringLength+1, c.IssuerName.Country)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.organization[0]), CertificateMaxStringLength+1, c.IssuerName.Organization)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.unit[0]), CertificateMaxStringLength+1, c.IssuerName.Unit)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.locality[0]), CertificateMaxStringLength+1, c.IssuerName.Locality)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.province[0]), CertificateMaxStringLength+1, c.IssuerName.Province)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.streetAddress[0]), CertificateMaxStringLength+1, c.IssuerName.StreetAddress)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.postalCode[0]), CertificateMaxStringLength+1, c.IssuerName.PostalCode)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.email[0]), CertificateMaxStringLength+1, c.IssuerName.Email)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.url[0]), CertificateMaxStringLength+1, c.IssuerName.URL)
+	cStrCopy(unsafe.Pointer(&ccC.issuerName.host[0]), CertificateMaxStringLength+1, c.IssuerName.Host)
 
 	if len(c.ExtendedAttributes) > 0 {
-		cc.C.extendedAttributes = (*C.uint8_t)(unsafe.Pointer(&c.ExtendedAttributes[0]))
-		cc.C.extendedAttributesSize = C.uint(len(c.ExtendedAttributes))
+		ccC.extendedAttributes = (*C.uint8_t)(unsafe.Pointer(&c.ExtendedAttributes[0]))
+		ccC.extendedAttributesSize = C.uint(len(c.ExtendedAttributes))
 	}
 
-	cc.C.maxPathLength = C.uint(c.MaxPathLength)
+	ccC.maxPathLength = C.uint(c.MaxPathLength)
 
 	if len(c.Signature) > 0 {
-		cc.C.signature = (*C.uint8_t)(unsafe.Pointer(&c.Signature[0]))
-		cc.C.signatureSize = C.uint(len(c.Signature))
+		ccC.signature = (*C.uint8_t)(unsafe.Pointer(&c.Signature[0]))
+		ccC.signatureSize = C.uint(len(c.Signature))
 	}
 
 	return &cc

+ 6 - 3
pkg/zerotier/misc.go

@@ -279,10 +279,13 @@ func stringAsZeroTerminatedBytes(s string) (b []byte) {
 // cStrCopy copies src into dest as a zero-terminated C string
 func cStrCopy(dest unsafe.Pointer, destSize int, src string) {
 	sb := []byte(src)
-	b := C.GoBytes(dest, C.int(destSize))
 	if len(sb) > (destSize - 1) {
 		sb = sb[0:destSize - 1]
 	}
-	copy(b[:], sb[:])
-	b[len(sb)] = 0
+	dp := dest
+	for _, c := range sb {
+		*((*byte)(dp)) = c
+		dp = unsafe.Pointer(uintptr(dp) + 1)
+	}
+	*((*byte)(dp)) = 0
 }