From f447608d6b545108bed6e55f74d94dfe91ce83d2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 13 Jun 2020 21:41:39 -0700 Subject: [PATCH] Certificates, etc... work in progress. --- Makefile | 4 +- cmd/zerotier/cli/help.go | 9 +- cmd/zerotier/cli/service.go | 21 +- core/Blob.hpp | 44 ++ core/CMakeLists.txt | 7 +- core/Dictionary.cpp | 135 ++-- core/Dictionary.hpp | 91 ++- core/IdentificationCertificate.cpp | 260 +++++++ core/IdentificationCertificate.hpp | 135 ++++ core/Identity.hpp | 75 +- core/Mutex.hpp | 308 ++++---- core/Node.cpp | 4 +- core/Protocol.hpp | 2 +- core/Salsa20.cpp | 2 + core/Topology.cpp | 15 +- core/Topology.hpp | 8 +- core/Utils.cpp | 38 +- core/Utils.hpp | 3 - core/zerotier.h | 191 ++++- installsupport/windows/ZeroTier One.aip | 968 ++++++++++++------------ main.cpp | 38 +- pkg/zerotier/node.go | 4 +- serviceiocore/GoGlue.cpp | 3 + serviceiocore/GoGlue.h | 2 + 24 files changed, 1502 insertions(+), 865 deletions(-) create mode 100644 core/Blob.hpp create mode 100644 core/IdentificationCertificate.cpp create mode 100644 core/IdentificationCertificate.hpp diff --git a/Makefile b/Makefile index 6acaa2fc5..2de28c53f 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ central-controller-docker: docker build -t registry.zerotier.com/zerotier-central/ztcentral-controller:${TIMESTAMP} -f controller/central-docker/Dockerfile . clean: - rm -rf ${BUILDDIR} cmake-build-* + rm -rf ${BUILDDIR} distclean: - rm -rf ${BUILDDIR} cmake-build-* + rm -rf ${BUILDDIR} diff --git a/cmd/zerotier/cli/help.go b/cmd/zerotier/cli/help.go index b483a1de5..a217c8952 100644 --- a/cmd/zerotier/cli/help.go +++ b/cmd/zerotier/cli/help.go @@ -34,7 +34,8 @@ Global Options: Commands: help Show this help version Print version - service Start node (see below) + service [-options] Start node (see below) + -d Fork into background (Unix only) status Show node status and configuration join [-options] Join a virtual network -a Token to submit to controller @@ -56,10 +57,8 @@ Commands: locator Explicitly update peer locator roots List root peers root [command] - Root management commands - add [endpoint] Designate a peer as a root - remove
Un-designate a peer as a root - subscribe [] Subscribe to a set of roots - unsubscribe Unsubscribe from a set of roots + add [endpoint] Add a root or a root set + remove
Remove a root or root set set [option] [value] - Get or set a core config option port Primary P2P port secondaryport Secondary P2P port (0 to disable) diff --git a/cmd/zerotier/cli/service.go b/cmd/zerotier/cli/service.go index 345c7f512..0318ecd04 100644 --- a/cmd/zerotier/cli/service.go +++ b/cmd/zerotier/cli/service.go @@ -15,8 +15,11 @@ package cli import ( "fmt" + "io/ioutil" "os" "os/signal" + "path" + "strconv" "syscall" "zerotier/pkg/zerotier" @@ -28,16 +31,20 @@ func Service(basePath, authToken string, args []string) { os.Exit(1) } + pidPath := path.Join(basePath, "zerotier.pid") + _ = ioutil.WriteFile(pidPath, []byte(strconv.FormatInt(int64(os.Getpid()), 10)), 0644) + node, err := zerotier.NewNode(basePath) if err != nil { fmt.Println("FATAL: error initializing node: " + err.Error()) - os.Exit(1) + } else { + osSignalChannel := make(chan os.Signal, 2) + signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGSTOP) + signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGPIPE, syscall.SIGHUP) + <-osSignalChannel + node.Close() } - osSignalChannel := make(chan os.Signal, 2) - signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS) - signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2) - <-osSignalChannel - node.Close() - os.Exit(0) + _ = os.Remove(pidPath) + os.Exit(1) } diff --git a/core/Blob.hpp b/core/Blob.hpp new file mode 100644 index 000000000..583fd4e5a --- /dev/null +++ b/core/Blob.hpp @@ -0,0 +1,44 @@ +/* + * 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. + */ +/****/ + +#ifndef ZT_BLOB_HPP +#define ZT_BLOB_HPP + +#include "Constants.hpp" +#include "Utils.hpp" + +namespace ZeroTier { + +/** + * Container for arbitrary bytes for use in collections + * + * @tparam S Size of container in bytes + */ +template +struct Blob +{ + uint8_t data[S]; + + ZT_INLINE Blob() { Utils::zero(data); } + + ZT_INLINE bool operator==(const Blob &b) const noexcept { return (memcmp(data,b.data,S) == 0); } + ZT_INLINE bool operator!=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) != 0); } + ZT_INLINE bool operator<(const Blob &b) const noexcept { return (memcmp(data,b.data,S) < 0); } + ZT_INLINE bool operator>(const Blob &b) const noexcept { return (memcmp(data,b.data,S) > 0); } + ZT_INLINE bool operator<=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) <= 0); } + ZT_INLINE bool operator>=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) >= 0); } +}; + +} // namespace ZeroTier + +#endif diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 5e033a469..2f8e7a0e7 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -2,13 +2,14 @@ cmake_minimum_required (VERSION 2.8) project(zt_core) configure_file( - version.h.in - version.h + version.h.in + version.h ) set(core_headers zerotier.h Address.hpp + Blob.hpp Buf.hpp C25519.hpp Capability.hpp @@ -24,6 +25,7 @@ set(core_headers Expect.hpp FCV.hpp Fingerprint.hpp + IdentificationCertificate.hpp Identity.hpp InetAddress.hpp Locator.hpp @@ -67,6 +69,7 @@ set(core_src Dictionary.cpp ECC384.cpp Endpoint.cpp + IdentificationCertificate.cpp Identity.cpp InetAddress.cpp Locator.cpp diff --git a/core/Dictionary.cpp b/core/Dictionary.cpp index 8ac5698c9..2240d061d 100644 --- a/core/Dictionary.cpp +++ b/core/Dictionary.cpp @@ -12,56 +12,64 @@ /****/ #include "Dictionary.hpp" -#include "SHA512.hpp" namespace ZeroTier { Dictionary::Dictionary() +{} + +Dictionary::~Dictionary() +{} + +Vector< uint8_t > &Dictionary::operator[](const char *k) { + if (k) + return m_entries[s_key(k)]; + else return m_entries[""]; } -Vector &Dictionary::operator[](const char *k) +const Vector< uint8_t > &Dictionary::operator[](const char *k) const { - return m_entries[s_key(k)]; -} - -const Vector &Dictionary::operator[](const char *k) const -{ - static const Vector s_emptyEntry; - SortedMap< String, Vector >::const_iterator e(m_entries.find(s_key(k))); - return (e == m_entries.end()) ? s_emptyEntry : e->second; + static const Vector< uint8_t > s_emptyEntry; + if (k) { + SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(s_key(k))); + return (e == m_entries.end()) ? s_emptyEntry : e->second; + } else { + SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find("")); + return (e == m_entries.end()) ? s_emptyEntry : e->second; + } } void Dictionary::add(const char *k, bool v) { - Vector &e = (*this)[k]; + Vector< uint8_t > &e = (*this)[k]; e.resize(2); - e[0] = (uint8_t) (v ? '1' : '0'); + e[0] = (uint8_t)(v ? '1' : '0'); e[1] = 0; } void Dictionary::add(const char *k, const Address &v) { - Vector &e = (*this)[k]; + Vector< uint8_t > &e = (*this)[k]; e.resize(ZT_ADDRESS_STRING_SIZE_MAX); - v.toString((char *) e.data()); + v.toString((char *)e.data()); } void Dictionary::add(const char *k, const char *v) { if ((v) && (*v)) { - Vector &e = (*this)[k]; + Vector< uint8_t > &e = (*this)[k]; e.clear(); while (*v) - e.push_back((uint8_t) *(v++)); + e.push_back((uint8_t)*(v++)); } } void Dictionary::add(const char *k, const void *data, unsigned int len) { - Vector &e = (*this)[k]; + Vector< uint8_t > &e = (*this)[k]; if (len != 0) { - e.assign((const uint8_t *) data, (const uint8_t *) data + len); + e.assign((const uint8_t *)data, (const uint8_t *)data + len); } else { e.clear(); } @@ -69,9 +77,9 @@ void Dictionary::add(const char *k, const void *data, unsigned int len) bool Dictionary::getB(const char *k, bool dfl) const { - const Vector &e = (*this)[k]; + const Vector< uint8_t > &e = (*this)[k]; if (!e.empty()) { - switch ((char) e[0]) { + switch ((char)e[0]) { case '1': case 't': case 'T': @@ -89,15 +97,15 @@ uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const { uint8_t tmp[18]; uint64_t v = dfl; - const Vector &e = (*this)[k]; + const Vector< uint8_t > &e = (*this)[k]; if (!e.empty()) { if (e.back() != 0) { const unsigned long sl = e.size(); Utils::copy(tmp, e.data(), (sl > 17) ? 17 : sl); tmp[17] = 0; - return Utils::unhex((const char *) tmp); + return Utils::unhex((const char *)tmp); } - return Utils::unhex((const char *) e.data()); + return Utils::unhex((const char *)e.data()); } return v; } @@ -106,13 +114,13 @@ char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const { if (cap == 0) // sanity check return v; - const Vector &e = (*this)[k]; + const Vector< uint8_t > &e = (*this)[k]; unsigned int i = 0; const unsigned int last = cap - 1; for (;;) { if ((i == last) || (i >= (unsigned int)e.size())) break; - v[i] = (char) e[i]; + v[i] = (char)e[i]; ++i; } v[i] = 0; @@ -124,16 +132,14 @@ void Dictionary::clear() m_entries.clear(); } -void Dictionary::encode(Vector &out, const bool omitSignatureFields) const +void Dictionary::encode(Vector< uint8_t > &out) const { out.clear(); - for (SortedMap< String, Vector >::const_iterator ti(m_entries.begin());ti != m_entries.end();++ti) { - if ((!omitSignatureFields) || ((ti->first != ZT_DICTIONARY_SIGNATURE_KEY))) { - s_appendKey(out, ti->first.data()); - for (Vector::const_iterator i(ti->second.begin());i != ti->second.end();++i) - s_appendValueByte(out, *i); - out.push_back((uint8_t) '\n'); - } + for (SortedMap< String, Vector< uint8_t > >::const_iterator ti(m_entries.begin()); ti != m_entries.end(); ++ti) { + s_appendKey(out, ti->first.data()); + for (Vector< uint8_t >::const_iterator i(ti->second.begin()); i != ti->second.end(); ++i) + s_appendValueByte(out, *i); + out.push_back((uint8_t)'\n'); } out.push_back(0); } @@ -142,9 +148,9 @@ bool Dictionary::decode(const void *data, unsigned int len) { clear(); String k; - Vector *v = nullptr; + Vector< uint8_t > *v = nullptr; bool escape = false; - for (unsigned int di = 0;di < len;++di) { + for (unsigned int di = 0; di < len; ++di) { uint8_t c = reinterpret_cast(data)[di]; if (!c) break; if (v) { @@ -168,7 +174,7 @@ bool Dictionary::decode(const void *data, unsigned int len) break; } } else { - if (c == (uint8_t) '\n') { + if (c == (uint8_t)'\n') { k.clear(); v = nullptr; } else if (c == 92) { // backslash @@ -180,7 +186,7 @@ bool Dictionary::decode(const void *data, unsigned int len) } else { if ((c < 33) || (c > 126) || (c == 92)) { return false; - } else if (c == (uint8_t) '=') { + } else if (c == (uint8_t)'=') { k.push_back(0); v = &m_entries[k]; } else if (k.size() < 7) { @@ -193,59 +199,4 @@ bool Dictionary::decode(const void *data, unsigned int len) return true; } -void Dictionary::sign( - const uint8_t c25519PrivateKey[ZT_C25519_COMBINED_PRIVATE_KEY_SIZE], - const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE], - const uint8_t p384PrivateKey[ZT_ECC384_PRIVATE_KEY_SIZE], - const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) -{ - Vector buf; - encode(buf, true); - - uint8_t c25519Signature[ZT_C25519_SIGNATURE_LEN]; - C25519::sign(c25519PrivateKey, c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature); - - uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE]; - static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE,"size mismatch"); - SHA384(hbuf, buf.data(), (unsigned int)buf.size()); - uint8_t p384Signature[ZT_ECC384_SIGNATURE_SIZE]; - ECC384ECDSASign(p384PrivateKey, hbuf, p384Signature); - - SHA384(hbuf, c25519PublicKey, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, p384PublicKey, ZT_ECC384_PUBLIC_KEY_SIZE); - - Dictionary signature; - signature["kh"].assign(hbuf, hbuf + ZT_SHA384_DIGEST_SIZE); - signature["ed25519"].assign(c25519Signature, c25519Signature + ZT_C25519_SIGNATURE_LEN); - signature["p384"].assign(p384Signature, p384Signature + ZT_ECC384_SIGNATURE_SIZE); - signature.encode((*this)[ZT_DICTIONARY_SIGNATURE_KEY], true); -} - -bool Dictionary::verify( - const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE], - const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) const -{ - try { - const Vector< uint8_t > &data = (*this)[ZT_DICTIONARY_SIGNATURE_KEY]; - if (data.empty()) - return false; - Dictionary signature; - if (!signature.decode(data.data(), (unsigned int)data.size())) - return false; - const Vector< uint8_t > &p384Signature = signature["p384"]; - const Vector< uint8_t > &c25519Signature = signature["ed25519"]; - if ((p384Signature.size() != ZT_ECC384_SIGNATURE_SIZE) || (c25519Signature.size() != ZT_C25519_SIGNATURE_LEN)) - return false; - - Vector< uint8_t > buf; - encode(buf, true); - - if (C25519::verify(c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature.data(), (unsigned int)c25519Signature.size())) { - uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE]; - SHA384(hbuf, buf.data(), (unsigned int)buf.size()); - return ECC384ECDSAVerify(p384PublicKey, hbuf, p384Signature.data()); - } - } catch ( ... ) {} - return false; -} - } // namespace ZeroTier diff --git a/core/Dictionary.hpp b/core/Dictionary.hpp index 2d5a40b98..2ec2ce15d 100644 --- a/core/Dictionary.hpp +++ b/core/Dictionary.hpp @@ -19,10 +19,6 @@ #include "Address.hpp" #include "Buf.hpp" #include "Containers.hpp" -#include "C25519.hpp" -#include "ECC384.hpp" - -#define ZT_DICTIONARY_SIGNATURE_KEY "@S" namespace ZeroTier { @@ -46,10 +42,10 @@ class Identity; class Dictionary { public: - typedef SortedMap< String, Vector < uint8_t > > - ::const_iterator const_iterator; + typedef SortedMap< String, Vector < uint8_t > >::const_iterator const_iterator; Dictionary(); + ~Dictionary(); /** * Get a reference to a value @@ -90,12 +86,17 @@ public: * @param k Key to set * @param v Integer to set, will be cast to uint64_t and stored as hex */ - template< typename I > - ZT_INLINE void add(const char *const k, I v) - { - char buf[17]; - add(k, Utils::hex((uint64_t)(v), buf)); - } + ZT_INLINE void add(const char *const k, const uint64_t v) + { char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); } + + /** + * Add an integer as a hexadecimal string value + * + * @param k Key to set + * @param v Integer to set, will be cast to uint64_t and stored as hex + */ + ZT_INLINE void add(const char *const k, const int64_t v) + { char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); } /** * Add an address in 10-digit hex string format @@ -145,6 +146,7 @@ public: /** * Get an object supporting the marshal/unmarshal interface pattern * + * @tparam T Object type (inferred) * @param k Key to look up * @param obj Object to unmarshal() into * @return True if unmarshal was successful @@ -158,6 +160,27 @@ public: return (obj.unmarshal(d.data(), (unsigned int)d.size()) > 0); } + /** + * Add an object supporting the marshal/unmarshal interface pattern + * + * @tparam T Object type (inferred) + * @param k Key to add + * @param obj Object to marshal() into vector + * @return True if successful + */ + template< typename T > + ZT_INLINE bool addO(const char *k, T &obj) + { + uint8_t tmp[4096]; + static_assert(sizeof(tmp) >= T::marshalSizeMax(),"buffer too small"); + int l = obj.marshal(tmp); + if (l > 0) { + (*this)[k].assign(tmp, tmp + l); + return true; + } + return false; + } + /** * Erase all entries in dictionary */ @@ -179,9 +202,8 @@ public: * Encode to a string in the supplied vector * * @param out String encoded dictionary - * @param omitSignatureFields If true omit the signature fields from encoding (default: false) */ - void encode(Vector &out, bool omitSignatureFields = false) const; + void encode(Vector &out) const; /** * Decode a string encoded dictionary @@ -195,34 +217,6 @@ public: */ bool decode(const void *data, unsigned int len); - /** - * Sign this dictionary with both an Ed25519 key and a NIST P-384 key. - * - * This is currently used just for signing root sets for the root subscribe - * feature. It uses both key types for more crypto cowbell. - * - * @param c25519PrivateKey Curve25519 combined key (C25519 and ed25519), though only ed25519 part is used - * @param c25519PublicKey Public part of Curve25519 combined key - * @param p384PrivateKey NIST P-384 private key - * @param p384PublicKey NIST P-384 public key - */ - void sign( - const uint8_t c25519PrivateKey[ZT_C25519_COMBINED_PRIVATE_KEY_SIZE], - const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE], - const uint8_t p384PrivateKey[ZT_ECC384_PRIVATE_KEY_SIZE], - const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]); - - /** - * Verify this dictionary's signature - * - * @param c25519PublicKey Curve25519 public key - * @param p384PublicKey P-384 public key - * @return True if signatures are valid - */ - bool verify( - const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE], - const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) const; - /** * Append a key=value pair to a buffer (vector or FCV) * @@ -376,6 +370,19 @@ public: return mlen; } + static ZT_INLINE char *arraySubscript(char buf[256],const char *name,const unsigned long sub) noexcept + { + for(unsigned int i=0;i<(256 - 17);++i) { + if ((buf[i] = name[i]) == 0) { + buf[i++] = '#'; + Utils::hex(sub, buf + i); + return buf; + } + } + buf[0] = 0; + return buf; + } + private: template< typename V > ZT_INLINE static void s_appendValueByte(V &out, const uint8_t c) diff --git a/core/IdentificationCertificate.cpp b/core/IdentificationCertificate.cpp new file mode 100644 index 000000000..5e72a624c --- /dev/null +++ b/core/IdentificationCertificate.cpp @@ -0,0 +1,260 @@ +/* + * 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. + */ +/****/ + +#include "IdentificationCertificate.hpp" +#include "SHA512.hpp" + +namespace ZeroTier { + +IdentificationCertificate &IdentificationCertificate::operator=(const ZT_IdentificationCertificate &apiCert) +{ + Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, &apiCert); + m_identities.clear(); + m_locators.clear(); + m_nodes.clear(); + m_networks.clear(); + return *this; +} + +IdentificationCertificate &IdentificationCertificate::operator=(const IdentificationCertificate &cert) +{ + Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, (const ZT_IdentificationCertificate *)(&cert)); + + m_identities.clear(); + m_locators.clear(); + m_nodes.clear(); + m_networks.clear(); + + this->subject.nodeCount = 0; + this->subject.networkCount = 0; + + if (cert.issuer) { + m_identities.push_back(*reinterpret_cast(cert.issuer)); + this->issuer = reinterpret_cast(&(m_identities.back())); + } + + for (unsigned int i = 0; i < cert.subject.nodeCount; ++i) { + if (cert.subject.nodes[i].locator) + addSubjectNode(*reinterpret_cast(cert.subject.nodes[i].identity), *reinterpret_cast(cert.subject.nodes[i].locator)); + else if (cert.subject.nodes[i].identity) + addSubjectNode(*reinterpret_cast(cert.subject.nodes[i].identity)); + } + + for (unsigned int i = 0; i < cert.subject.networkCount; ++i) + addSubjectNetwork(cert.subject.networks[i].id, cert.subject.networks[i].controller); + + return *this; +} + +ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id) +{ + m_nodes.resize(++this->subject.nodeCount); + this->subject.nodes = m_nodes.data(); + m_identities.push_back(id); + m_nodes.back().identity = reinterpret_cast(&(m_identities.back())); + m_nodes.back().locator = nullptr; + return &(m_nodes.back()); +} + +ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id, const Locator &loc) +{ + ZT_IdentificationCertificate_Node *n = addSubjectNode(id); + m_locators.push_back(loc); + n->locator = reinterpret_cast(&(m_locators.back())); + return n; +} + +ZT_IdentificationCertificate_Network *IdentificationCertificate::addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller) +{ + m_networks.resize(++this->subject.networkCount); + this->subject.networks = m_networks.data(); + m_networks.back().id = id; + Utils::copy< sizeof(ZT_Fingerprint) >(&(m_networks.back().controller), &controller); + return &(m_networks.back()); +} + +Vector< uint8_t > IdentificationCertificate::encode(const bool omitSignature) const +{ + char tmp[256]; + Vector< uint8_t > enc; + Dictionary d; + + d.add("v", (uint64_t)this->version); + d.add("mP", (uint64_t)this->maxPathLength); + d.add("f", this->flags); + d.add("v0", this->validity[0]); + d.add("v1", this->validity[1]); + + d.add("s.n[]", (uint64_t)this->subject.nodeCount); + for (unsigned int i = 0; i < this->subject.nodeCount; ++i) { + d.addO(Dictionary::arraySubscript(tmp, "s.n[].i", i), *reinterpret_cast(this->subject.nodes[i].identity)); + if (this->subject.nodes[i].locator) + d.addO(Dictionary::arraySubscript(tmp, "s.n[].l", i), *reinterpret_cast(this->subject.nodes[i].locator)); + } + + d.add("s.nw[]", (uint64_t)this->subject.networkCount); + for (unsigned int i = 0; i < this->subject.networkCount; ++i) { + d.add(Dictionary::arraySubscript(tmp, "s.nw[].i", i), this->subject.networks[i].id); + Fingerprint fp(this->subject.networks[i].controller); + d.addO(Dictionary::arraySubscript(tmp, "s.nw[].c", i), fp); + } + + d.add("s.n.c", this->subject.name.country); + d.add("s.n.o", this->subject.name.organization); + d.add("s.n.u", this->subject.name.unit); + d.add("s.n.l", this->subject.name.locality); + d.add("s.n.p", this->subject.name.province); + d.add("s.n.sA", this->subject.name.streetAddress); + d.add("s.n.pC", this->subject.name.postalCode); + d.add("s.n.cN", this->subject.name.commonName); + d.add("s.n.sN", this->subject.name.serialNo); + d.add("s.n.e", this->subject.name.email); + d.add("s.n.ur", this->subject.name.url); + + if (this->issuer) + d.addO("i", *reinterpret_cast(this->issuer)); + + d.add("iN.c", this->issuerName.country); + d.add("iN.o", this->issuerName.organization); + d.add("iN.u", this->issuerName.unit); + d.add("iN.l", this->issuerName.locality); + d.add("iN.p", this->issuerName.province); + d.add("iN.sA", this->issuerName.streetAddress); + d.add("iN.pC", this->issuerName.postalCode); + d.add("iN.cN", this->issuerName.commonName); + d.add("iN.sN", this->issuerName.serialNo); + d.add("iN.e", this->issuerName.email); + d.add("iN.ur", this->issuerName.url); + + if ((!omitSignature) && (this->signatureSize > 0) && (this->signatureSize <= sizeof(this->signature))) + d["si"].assign(this->signature, this->signature + this->signatureSize); + + d.encode(enc); + return enc; +} + +bool IdentificationCertificate::decode(const Vector< uint8_t > &data) +{ + char tmp[256]; + + Utils::zero< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this); + m_identities.clear(); + m_locators.clear(); + m_nodes.clear(); + m_networks.clear(); + + Dictionary d; + if (!d.decode(data.data(), (unsigned int)data.size())) + return false; + + this->version = (unsigned int)d.getUI("v"); + this->maxPathLength = (unsigned int)d.getUI("mP"); + this->flags = d.getUI("f"); + this->validity[0] = (int64_t)d.getUI("v0"); + this->validity[1] = (int64_t)d.getUI("v1"); + + unsigned int cnt = (unsigned int)d.getUI("s.n[]"); + for (unsigned int i = 0; i < cnt; ++i) { + const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, "s.n[].i", i)]; + if (identityData.empty()) + return false; + Identity id; + if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0) + return false; + const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, "s.n[].l", i)]; + if (!locatorData.empty()) { + Locator loc; + if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0) + return false; + this->addSubjectNode(id, loc); + } else { + this->addSubjectNode(id); + } + } + + cnt = (unsigned int)d.getUI("s.nw[]"); + for (unsigned int i = 0; i < cnt; ++i) { + const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, "s.nw[].i", i)); + if (nwid == 0) + return false; + const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, "s.nw[].c", i)]; + if (fingerprintData.empty()) + return false; + Fingerprint fp; + if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0) + return false; + this->addSubjectNetwork(nwid, fp); + } + + d.getS("s.n.c", this->subject.name.country, sizeof(this->subject.name.country)); + d.getS("s.n.o", this->subject.name.organization, sizeof(this->subject.name.organization)); + d.getS("s.n.u", this->subject.name.unit, sizeof(this->subject.name.unit)); + d.getS("s.n.l", this->subject.name.locality, sizeof(this->subject.name.locality)); + d.getS("s.n.p", this->subject.name.province, sizeof(this->subject.name.province)); + d.getS("s.n.sA", this->subject.name.streetAddress, sizeof(this->subject.name.streetAddress)); + d.getS("s.n.pC", this->subject.name.postalCode, sizeof(this->subject.name.postalCode)); + d.getS("s.n.cN", this->subject.name.commonName, sizeof(this->subject.name.commonName)); + d.getS("s.n.sN", this->subject.name.serialNo, sizeof(this->subject.name.serialNo)); + d.getS("s.n.e", this->subject.name.email, sizeof(this->subject.name.email)); + d.getS("s.n.ur", this->subject.name.url, sizeof(this->subject.name.url)); + + const Vector< uint8_t > &issuerData = d["i"]; + if (!issuerData.empty()) { + Identity id; + if (id.unmarshal(issuerData.data(), (int)issuerData.size()) > 0) { + m_identities.push_back(id); + this->issuer = reinterpret_cast(&(m_identities.back())); + } + } + + d.getS("iN.c", this->issuerName.country, sizeof(this->issuerName.country)); + d.getS("iN.o", this->issuerName.organization, sizeof(this->issuerName.organization)); + d.getS("iN.u", this->issuerName.unit, sizeof(this->issuerName.unit)); + d.getS("iN.l", this->issuerName.locality, sizeof(this->issuerName.locality)); + d.getS("iN.p", this->issuerName.province, sizeof(this->issuerName.province)); + d.getS("iN.sA", this->issuerName.streetAddress, sizeof(this->issuerName.streetAddress)); + d.getS("iN.pC", this->issuerName.postalCode, sizeof(this->issuerName.postalCode)); + d.getS("iN.cN", this->issuerName.commonName, sizeof(this->issuerName.commonName)); + d.getS("iN.sN", this->issuerName.serialNo, sizeof(this->issuerName.serialNo)); + d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email)); + d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url)); + + const Vector< uint8_t > &sig = d["si"]; + if (sig.size() > sizeof(this->signature)) + return false; + Utils::copy(this->signature, sig.data(), (unsigned int)sig.size()); + this->signatureSize = (unsigned int)sig.size(); + + Vector< uint8_t > enc(encode(true)); + SHA384(this->serialNo, enc.data(), (unsigned int)enc.size()); + + return true; +} + +bool IdentificationCertificate::sign(const Identity &issuer) +{ + Vector< uint8_t > enc(encode(true)); + SHA384(this->serialNo, enc.data(), (unsigned int)enc.size()); + return (this->signatureSize = issuer.sign(enc.data(), (unsigned int)enc.size(), this->signature, sizeof(this->signature))) > 0; +} + +bool IdentificationCertificate::verify() const +{ + if (this->issuer) { + Vector< uint8_t > enc(encode(true)); + return reinterpret_cast(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize); + } + return false; +} + +} // namespace ZeroTier diff --git a/core/IdentificationCertificate.hpp b/core/IdentificationCertificate.hpp new file mode 100644 index 000000000..d45726cdc --- /dev/null +++ b/core/IdentificationCertificate.hpp @@ -0,0 +1,135 @@ +/* + * 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. + */ +/****/ + +#ifndef ZT_IDENTIFICATIONCERTIFICATE_HPP +#define ZT_IDENTIFICATIONCERTIFICATE_HPP + +#include "Constants.hpp" +#include "SHA512.hpp" +#include "C25519.hpp" +#include "ECC384.hpp" +#include "SharedPtr.hpp" +#include "Identity.hpp" +#include "Locator.hpp" +#include "Dictionary.hpp" +#include "Utils.hpp" +#include "Containers.hpp" + +namespace ZeroTier { + +/** + * Certificate identifying the real world owner of an identity or network. + * + * This is a wrapper around the straight C ZT_IdentificationCertificate and + * handles allocating memory for objects and disposing of it on GC. If filling + * out a ZT_IdentificationCertificate structure, identities and other objects + * should be attached via the addXXX() methods rather than by directly setting + * the pointers in the C structure. + * + * If identities and similar objects are NOT added via the addXXX() methods, + * this will not take care of de-allocating them when destroyed. + * + * The serialNo field is filled in automatically by sign() and decode(), so + * it can be left undefined when building certificates. + */ +class IdentificationCertificate : public ZT_IdentificationCertificate +{ +public: + ZT_INLINE IdentificationCertificate() noexcept + { Utils::zero< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this); } + + ZT_INLINE IdentificationCertificate(const ZT_IdentificationCertificate &apiCert) + { Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, &apiCert); } + + ZT_INLINE IdentificationCertificate(const IdentificationCertificate &cert) + { *this = cert; } + + IdentificationCertificate &operator=(const ZT_IdentificationCertificate &apiCert); + + IdentificationCertificate &operator=(const IdentificationCertificate &cert); + + /** + * Add a subject node/identity without a locator + * + * @param id Identity + * @return Pointer to C struct + */ + ZT_IdentificationCertificate_Node *addSubjectNode(const Identity &id); + + /** + * Add a subject node/identity with a locator + * + * @param id Identity + * @param loc Locator signed by identity (signature is NOT checked here) + * @return Pointer to C struct + */ + ZT_IdentificationCertificate_Node *addSubjectNode(const Identity &id, const Locator &loc); + + /** + * Add a subject network + * + * @param id Network ID + * @param controller Network controller's full fingerprint + * @return Pointer to C struct + */ + ZT_IdentificationCertificate_Network *addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller); + + /** + * Marshal this certificate in binary form + * + * The internal encoding used here is Dictionary to permit easy + * extensibility. + * + * @param omitSignature If true omit the signature field (for signing and verification, default is false) + * @return Marshaled certificate + */ + Vector< uint8_t > encode(bool omitSignature = false) const; + + /** + * Decode this certificate from marshaled bytes. + * + * @param data Marshalled certificate + * @return True if input is valid and was unmarshalled (signature is NOT checked) + */ + bool decode(const Vector< uint8_t > &data); + + /** + * Sign this certificate (and also fill in serialNo). + * + * @param issuer Issuer identity (must have secret key) + * @return True on success + */ + bool sign(const Identity &issuer); + + /** + * Verify certificate signature against the issuer contained therein + * + * @return True if certificate is signed and signature is valid + */ + bool verify() const; + +private: + // These hold any identity or locator objects that are owned by and should + // be deleted with this certificate. Lists are used so the pointers never + // change. + List< Identity > m_identities; + List< Locator > m_locators; + + // These are stored in a vector because the memory needs to be contiguous. + Vector< ZT_IdentificationCertificate_Node > m_nodes; + Vector< ZT_IdentificationCertificate_Network > m_networks; +}; + +} // namespace ZeroTier + +#endif diff --git a/core/Identity.hpp b/core/Identity.hpp index 8e5778b04..4b9d8ff71 100644 --- a/core/Identity.hpp +++ b/core/Identity.hpp @@ -64,7 +64,7 @@ public: ZT_INLINE Identity() noexcept { - Utils::memoryLock(this,sizeof(Identity)); + Utils::memoryLock(this, sizeof(Identity)); memoryZero(this); } @@ -78,25 +78,27 @@ public: */ explicit ZT_INLINE Identity(const char *str) { - Utils::memoryLock(this,sizeof(Identity)); + Utils::memoryLock(this, sizeof(Identity)); fromString(str); } ZT_INLINE ~Identity() { - Utils::memoryUnlock(this,sizeof(Identity)); + Utils::memoryUnlock(this, sizeof(Identity)); Utils::burn(reinterpret_cast(&this->m_priv), sizeof(this->m_priv)); } /** * Set identity to NIL value (all zero) */ - ZT_INLINE void zero() noexcept { memoryZero(this); } + ZT_INLINE void zero() noexcept + { memoryZero(this); } /** * @return Identity type (undefined if identity is null or invalid) */ - ZT_INLINE Type type() const noexcept { return m_type; } + ZT_INLINE Type type() const noexcept + { return m_type; } /** * Generate a new identity (address, key pair) @@ -122,17 +124,20 @@ public: /** * @return True if this identity contains a private key */ - ZT_INLINE bool hasPrivate() const noexcept { return m_hasPrivate; } + ZT_INLINE bool hasPrivate() const noexcept + { return m_hasPrivate; } /** * @return This identity's address */ - ZT_INLINE Address address() const noexcept { return Address(m_fp.address); } + ZT_INLINE Address address() const noexcept + { return Address(m_fp.address); } /** * @return Full fingerprint of this identity (address plus SHA384 of keys) */ - ZT_INLINE const Fingerprint &fingerprint() const noexcept { return m_fp; } + ZT_INLINE const Fingerprint &fingerprint() const noexcept + { return m_fp; } /** * Compute a hash of this identity's public and private keys. @@ -155,7 +160,7 @@ public: * @param siglen Length of buffer * @return Number of bytes actually written to sig or 0 on error */ - unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const; + unsigned int sign(const void *data, unsigned int len, void *sig, unsigned int siglen) const; /** * Verify a message signature against this identity @@ -166,7 +171,7 @@ public: * @param siglen Length of signature in bytes * @return True if signature validates and data integrity checks */ - bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const; + bool verify(const void *data, unsigned int len, const void *sig, unsigned int siglen) const; /** * Shortcut method to perform key agreement with another identity @@ -177,7 +182,7 @@ public: * @param key Result parameter to fill with key bytes * @return Was agreement successful? */ - bool agree(const Identity &id,uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const; + bool agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const; /** * Serialize to a more human-friendly string @@ -186,8 +191,14 @@ public: * @param buf Buffer to store string * @return ASCII string representation of identity (pointer to buf) */ - char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const; - ZT_INLINE String toString(const bool includePrivate = false) const { char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]; toString(includePrivate,buf); return String(buf); } + char *toString(bool includePrivate, char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const; + + ZT_INLINE String toString(const bool includePrivate = false) const + { + char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]; + toString(includePrivate, buf); + return String(buf); + } /** * Deserialize a human-friendly string @@ -203,20 +214,36 @@ public: /** * @return True if this identity contains something */ - explicit ZT_INLINE operator bool() const noexcept { return (m_fp); } + explicit ZT_INLINE operator bool() const noexcept + { return (m_fp); } - ZT_INLINE unsigned long hashCode() const noexcept { return m_fp.hashCode(); } + ZT_INLINE unsigned long hashCode() const noexcept + { return m_fp.hashCode(); } - ZT_INLINE bool operator==(const Identity &id) const noexcept { return (m_fp == id.m_fp); } - ZT_INLINE bool operator!=(const Identity &id) const noexcept { return !(*this == id); } - ZT_INLINE bool operator<(const Identity &id) const noexcept { return (m_fp < id.m_fp); } - ZT_INLINE bool operator>(const Identity &id) const noexcept { return (id < *this); } - ZT_INLINE bool operator<=(const Identity &id) const noexcept { return !(id < *this); } - ZT_INLINE bool operator>=(const Identity &id) const noexcept { return !(*this < id); } + ZT_INLINE bool operator==(const Identity &id) const noexcept + { return (m_fp == id.m_fp); } - static constexpr int marshalSizeMax() noexcept { return ZT_IDENTITY_MARSHAL_SIZE_MAX; } - int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],bool includePrivate = false) const noexcept; - int unmarshal(const uint8_t *data,int len) noexcept; + ZT_INLINE bool operator!=(const Identity &id) const noexcept + { return !(*this == id); } + + ZT_INLINE bool operator<(const Identity &id) const noexcept + { return (m_fp < id.m_fp); } + + ZT_INLINE bool operator>(const Identity &id) const noexcept + { return (id < *this); } + + ZT_INLINE bool operator<=(const Identity &id) const noexcept + { return !(id < *this); } + + ZT_INLINE bool operator>=(const Identity &id) const noexcept + { return !(*this < id); } + + static constexpr int marshalSizeMax() noexcept + { return ZT_IDENTITY_MARSHAL_SIZE_MAX; } + + int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], bool includePrivate = false) const noexcept; + + int unmarshal(const uint8_t *data, int len) noexcept; private: void m_computeHash(); diff --git a/core/Mutex.hpp b/core/Mutex.hpp index 4b8ede85a..9751fd8ef 100644 --- a/core/Mutex.hpp +++ b/core/Mutex.hpp @@ -1,154 +1,154 @@ -/* - * 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. - */ -/****/ - -#ifndef ZT_MUTEX_HPP -#define ZT_MUTEX_HPP - -#include "Constants.hpp" - -#include -#include - -#ifndef __WINDOWS__ -#include -#endif - -namespace ZeroTier { - -class Mutex -{ -public: - ZT_INLINE Mutex() noexcept { pthread_mutex_init(&_mh,nullptr); } - ZT_INLINE ~Mutex() noexcept { pthread_mutex_destroy(&_mh); } - - ZT_INLINE void lock() const noexcept { pthread_mutex_lock(&((const_cast (this))->_mh)); } - ZT_INLINE void unlock() const noexcept { pthread_mutex_unlock(&((const_cast (this))->_mh)); } - - class Lock - { - public: - explicit ZT_INLINE Lock(Mutex &m) noexcept : _m(&m) { m.lock(); } - explicit ZT_INLINE Lock(const Mutex &m) noexcept : _m(const_cast(&m)) { _m->lock(); } - ZT_INLINE ~Lock() { _m->unlock(); } - private: - Mutex *const _m; - }; - -private: - ZT_INLINE Mutex(const Mutex &) noexcept {} - ZT_INLINE const Mutex &operator=(const Mutex &) noexcept { return *this; } - - pthread_mutex_t _mh; -}; - -class RWMutex -{ -public: - ZT_INLINE RWMutex() noexcept { pthread_rwlock_init(&_mh,nullptr); } - ZT_INLINE ~RWMutex() noexcept { pthread_rwlock_destroy(&_mh); } - - ZT_INLINE void lock() const noexcept { pthread_rwlock_wrlock(&((const_cast (this))->_mh)); } - ZT_INLINE void rlock() const noexcept { pthread_rwlock_rdlock(&((const_cast (this))->_mh)); } - ZT_INLINE void unlock() const noexcept { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } - ZT_INLINE void runlock() const noexcept { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } - - /** - * RAAI locker that acquires only the read lock (shared read) - */ - class RLock - { - public: - explicit ZT_INLINE RLock(RWMutex &m) noexcept : _m(&m) { m.rlock(); } - explicit ZT_INLINE RLock(const RWMutex &m) noexcept : _m(const_cast(&m)) { _m->rlock(); } - ZT_INLINE ~RLock() { _m->runlock(); } - private: - RWMutex *const _m; - }; - - /** - * RAAI locker that acquires the write lock (exclusive write, no readers) - */ - class Lock - { - public: - explicit ZT_INLINE Lock(RWMutex &m) noexcept : _m(&m) { m.lock(); } - explicit ZT_INLINE Lock(const RWMutex &m) noexcept : _m(const_cast(&m)) { _m->lock(); } - ZT_INLINE ~Lock() { _m->unlock(); } - private: - RWMutex *const _m; - }; - - /** - * RAAI locker that acquires the read lock first and can switch modes - * - * Use writing() to acquire the write lock if not already acquired. Use reading() to - * let go of the write lock and go back to only holding the read lock. - */ - class RMaybeWLock - { - public: - explicit ZT_INLINE RMaybeWLock(RWMutex &m) noexcept : _m(&m),_w(false) { m.rlock(); } - explicit ZT_INLINE RMaybeWLock(const RWMutex &m) noexcept : _m(const_cast(&m)),_w(false) { _m->rlock(); } - ZT_INLINE void writing() noexcept { if (!_w) { _w = true; _m->runlock(); _m->lock(); } } - ZT_INLINE void reading() noexcept { if (_w) { _w = false; _m->unlock(); _m->rlock(); } } - ZT_INLINE ~RMaybeWLock() { if (_w) _m->unlock(); else _m->runlock(); } - private: - RWMutex *const _m; - bool _w; - }; - -private: - ZT_INLINE RWMutex(const RWMutex &) noexcept {} - ZT_INLINE const RWMutex &operator=(const RWMutex &) noexcept { return *this; } - - pthread_rwlock_t _mh; -}; - -} // namespace ZeroTier - -#if 0 -#include - -namespace ZeroTier { - -class Mutex -{ -public: - ZT_INLINE Mutex() { InitializeCriticalSection(&_cs); } - ZT_INLINE ~Mutex() { DeleteCriticalSection(&_cs); } - ZT_INLINE void lock() { EnterCriticalSection(&_cs); } - ZT_INLINE void unlock() { LeaveCriticalSection(&_cs); } - ZT_INLINE void lock() const { (const_cast (this))->lock(); } - ZT_INLINE void unlock() const { (const_cast (this))->unlock(); } - - class Lock - { - public: - ZT_INLINE Lock(Mutex &m) : _m(&m) { m.lock(); } - ZT_INLINE Lock(const Mutex &m) : _m(const_cast(&m)) { _m->lock(); } - ZT_INLINE ~Lock() { _m->unlock(); } - private: - Mutex *const _m; - }; - -private: - ZT_INLINE Mutex(const Mutex &) {} - ZT_INLINE const Mutex &operator=(const Mutex &) { return *this; } - - CRITICAL_SECTION _cs; -}; - -} // namespace ZeroTier -#endif - -#endif +/* + * 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. + */ +/****/ + +#ifndef ZT_MUTEX_HPP +#define ZT_MUTEX_HPP + +#include "Constants.hpp" + +#include +#include + +#ifndef __WINDOWS__ +#include +#endif + +namespace ZeroTier { + +class Mutex +{ +public: + ZT_INLINE Mutex() noexcept { pthread_mutex_init(&_mh,nullptr); } + ZT_INLINE ~Mutex() noexcept { pthread_mutex_destroy(&_mh); } + + ZT_INLINE void lock() const noexcept { pthread_mutex_lock(&((const_cast (this))->_mh)); } + ZT_INLINE void unlock() const noexcept { pthread_mutex_unlock(&((const_cast (this))->_mh)); } + + class Lock + { + public: + explicit ZT_INLINE Lock(Mutex &m) noexcept : _m(&m) { m.lock(); } + explicit ZT_INLINE Lock(const Mutex &m) noexcept : _m(const_cast(&m)) { _m->lock(); } + ZT_INLINE ~Lock() { _m->unlock(); } + private: + Mutex *const _m; + }; + +private: + ZT_INLINE Mutex(const Mutex &) noexcept {} + ZT_INLINE const Mutex &operator=(const Mutex &) noexcept { return *this; } + + pthread_mutex_t _mh; +}; + +class RWMutex +{ +public: + ZT_INLINE RWMutex() noexcept { pthread_rwlock_init(&_mh,nullptr); } + ZT_INLINE ~RWMutex() noexcept { pthread_rwlock_destroy(&_mh); } + + ZT_INLINE void lock() const noexcept { pthread_rwlock_wrlock(&((const_cast (this))->_mh)); } + ZT_INLINE void rlock() const noexcept { pthread_rwlock_rdlock(&((const_cast (this))->_mh)); } + ZT_INLINE void unlock() const noexcept { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } + ZT_INLINE void runlock() const noexcept { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } + + /** + * RAAI locker that acquires only the read lock (shared read) + */ + class RLock + { + public: + explicit ZT_INLINE RLock(RWMutex &m) noexcept : _m(&m) { m.rlock(); } + explicit ZT_INLINE RLock(const RWMutex &m) noexcept : _m(const_cast(&m)) { _m->rlock(); } + ZT_INLINE ~RLock() { _m->runlock(); } + private: + RWMutex *const _m; + }; + + /** + * RAAI locker that acquires the write lock (exclusive write, no readers) + */ + class Lock + { + public: + explicit ZT_INLINE Lock(RWMutex &m) noexcept : _m(&m) { m.lock(); } + explicit ZT_INLINE Lock(const RWMutex &m) noexcept : _m(const_cast(&m)) { _m->lock(); } + ZT_INLINE ~Lock() { _m->unlock(); } + private: + RWMutex *const _m; + }; + + /** + * RAAI locker that acquires the read lock first and can switch modes + * + * Use writing() to acquire the write lock if not already acquired. Use reading() to + * let go of the write lock and go back to only holding the read lock. + */ + class RMaybeWLock + { + public: + explicit ZT_INLINE RMaybeWLock(RWMutex &m) noexcept : _m(&m),_w(false) { m.rlock(); } + explicit ZT_INLINE RMaybeWLock(const RWMutex &m) noexcept : _m(const_cast(&m)),_w(false) { _m->rlock(); } + ZT_INLINE void writing() noexcept { if (!_w) { _w = true; _m->runlock(); _m->lock(); } } + ZT_INLINE void reading() noexcept { if (_w) { _w = false; _m->unlock(); _m->rlock(); } } + ZT_INLINE ~RMaybeWLock() { if (_w) _m->unlock(); else _m->runlock(); } + private: + RWMutex *const _m; + bool _w; + }; + +private: + ZT_INLINE RWMutex(const RWMutex &) noexcept {} + ZT_INLINE const RWMutex &operator=(const RWMutex &) noexcept { return *this; } + + pthread_rwlock_t _mh; +}; + +} // namespace ZeroTier + +#if 0 +#include + +namespace ZeroTier { + +class Mutex +{ +public: + ZT_INLINE Mutex() { InitializeCriticalSection(&_cs); } + ZT_INLINE ~Mutex() { DeleteCriticalSection(&_cs); } + ZT_INLINE void lock() { EnterCriticalSection(&_cs); } + ZT_INLINE void unlock() { LeaveCriticalSection(&_cs); } + ZT_INLINE void lock() const { (const_cast (this))->lock(); } + ZT_INLINE void unlock() const { (const_cast (this))->unlock(); } + + class Lock + { + public: + ZT_INLINE Lock(Mutex &m) : _m(&m) { m.lock(); } + ZT_INLINE Lock(const Mutex &m) : _m(const_cast(&m)) { _m->lock(); } + ZT_INLINE ~Lock() { _m->unlock(); } + private: + Mutex *const _m; + }; + +private: + ZT_INLINE Mutex(const Mutex &) {} + ZT_INLINE const Mutex &operator=(const Mutex &) { return *this; } + + CRITICAL_SECTION _cs; +}; + +} // namespace ZeroTier +#endif + +#endif diff --git a/core/Node.cpp b/core/Node.cpp index 124c7387d..26e46825a 100644 --- a/core/Node.cpp +++ b/core/Node.cpp @@ -119,9 +119,7 @@ Node::Node( stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, RR->publicIdentityStr, (unsigned int)strlen(RR->publicIdentityStr)); } - // 2X hash our identity private key(s) to obtain a symmetric key for encrypting - // locally cached data at rest (as a defense in depth measure). This is not used - // for any network level encryption or authentication. + // Create a secret key for encrypting local data at rest. uint8_t tmph[ZT_SHA384_DIGEST_SIZE]; RR->identity.hashWithPrivate(tmph); SHA384(tmph, tmph, ZT_SHA384_DIGEST_SIZE); diff --git a/core/Protocol.hpp b/core/Protocol.hpp index aa1d11c44..206af42d4 100644 --- a/core/Protocol.hpp +++ b/core/Protocol.hpp @@ -194,7 +194,7 @@ #define ZT_PROTO_PACKET_FRAGMENT_PAYLOAD_START_AT ZT_PROTO_MIN_FRAGMENT_LENGTH /** - * Header flag indicating that a packet is fragmented and more fragments should be expected + * Outer flag indicating that a packet is fragmented and this is just the head. */ #define ZT_PROTO_FLAG_FRAGMENTED 0x40U diff --git a/core/Salsa20.cpp b/core/Salsa20.cpp index c4c7e3c05..4bc819dcc 100644 --- a/core/Salsa20.cpp +++ b/core/Salsa20.cpp @@ -145,7 +145,9 @@ static ZT_INLINE void p_salsaCrypt(p_SalsaState *const state, const uint8_t *m, for (;;) { if (likely(bytes >= 64)) { +#ifdef ZT_SALSA20_SSE _mm_prefetch(m + 128, _MM_HINT_T0); +#endif } else { for (unsigned int i = 0;i < bytes;++i) tmp[i] = m[i]; diff --git a/core/Topology.cpp b/core/Topology.cpp index 5b094721a..7877c56c9 100644 --- a/core/Topology.cpp +++ b/core/Topology.cpp @@ -29,7 +29,6 @@ Topology::Topology(const RuntimeEnvironment *renv, void *tPtr) : Identity id; int l = id.unmarshal(dptr, drem); if ((l > 0) && (id)) { - m_roots.insert(id); ZT_SPEW("restored root %s", id.address().toString().c_str()); if ((drem -= l) <= 0) break; @@ -189,25 +188,25 @@ void Topology::m_updateRootPeers(void *tPtr) { // assumes m_peers_l is locked for write Vector< SharedPtr< Peer > > rp; - for (Set< Identity >::iterator r(m_roots.begin()); r != m_roots.end(); ++r) { - Map< Address, SharedPtr< Peer > >::iterator pp(m_peers.find(r->address())); + for (Map< Identity, Set< SubscriptionKeyHash > >::iterator r(m_roots.begin()); r != m_roots.end(); ++r) { + Map< Address, SharedPtr< Peer > >::iterator pp(m_peers.find(r->first.address())); SharedPtr< Peer > p; if (pp != m_peers.end()) p = pp->second; if (!p) - m_loadCached(tPtr, r->address(), p); + m_loadCached(tPtr, r->first.address(), p); - if ((!p) || (p->identity() != *r)) { + if ((!p) || (p->identity() != r->first)) { p.set(new Peer(RR)); - p->init(*r); - m_peers[r->address()] = p; + p->init(r->first); + m_peers[r->first.address()] = p; } rp.push_back(p); } + std::sort(rp.begin(), rp.end(), p_RootSortComparisonOperator()); m_rootPeers.swap(rp); - std::sort(m_rootPeers.begin(), m_rootPeers.end(), p_RootSortComparisonOperator()); } } // namespace ZeroTier diff --git a/core/Topology.hpp b/core/Topology.hpp index ddda370ad..8becd1f92 100644 --- a/core/Topology.hpp +++ b/core/Topology.hpp @@ -25,6 +25,7 @@ #include "ScopedPtr.hpp" #include "Fingerprint.hpp" #include "Containers.hpp" +#include "Blob.hpp" namespace ZeroTier { @@ -36,6 +37,11 @@ class RuntimeEnvironment; class Topology { public: + /** + * Hash of public keys for signing a root set definition + */ + typedef Blob RootSetId; + Topology(const RuntimeEnvironment *renv, void *tPtr); /** @@ -237,7 +243,7 @@ private: RWMutex m_peers_l; // locks m_peers, m_roots, and m_rootPeers Map< uint64_t, SharedPtr< Path > > m_paths; Map< Address, SharedPtr< Peer > > m_peers; - Set< Identity > m_roots; + Map< Identity, Set< SubscriptionKeyHash > > m_roots; Vector< SharedPtr< Peer > > m_rootPeers; }; diff --git a/core/Utils.cpp b/core/Utils.cpp index d62fcbcdc..869462488 100644 --- a/core/Utils.cpp +++ b/core/Utils.cpp @@ -132,14 +132,19 @@ char *decimal(unsigned long n, char s[24]) noexcept char *hex(uint64_t i, char buf[17]) noexcept { - if (i) { - char *p = buf + 16; - *p = 0; - while (i) { - *(--p) = HEXCHARS[i & 0xfU]; - i >>= 4; + if (i != 0) { + char *p = nullptr; + for (int b = 60; b >= 0; b -= 4) { + const unsigned int nyb = (unsigned int)(i >> (unsigned int)b) & 0xfU; + if (p) { + *(p++) = HEXCHARS[nyb]; + } else if (nyb != 0) { + p = buf; + *(p++) = HEXCHARS[nyb]; + } } - return p; + *p = 0; + return buf; } else { buf[0] = '0'; buf[1] = 0; @@ -267,20 +272,18 @@ void getSecureRandom(void *const buf, unsigned int bytes) noexcept close(devURandomFd); #endif - // Mix in additional entropy from time, the address of 'buf', CPU RDRAND if present, etc. - randomState[0] += (uint64_t)time(nullptr); - randomState[1] += (uint64_t)((uintptr_t)buf); #ifdef __UNIX_LIKE__ - randomState[2] += (uint64_t)getpid(); - randomState[3] += (uint64_t)getppid(); + randomState[0] += (uint64_t)getpid(); + randomState[1] += (uint64_t)getppid(); #endif #ifdef ZT_ARCH_X64 if (CPUID.rdrand) { + // RDRAND is very slow on some chips, so only sample it a little bit for extra entropy. uint64_t tmp = 0; - for (int k = 0; k < ZT_GETSECURERANDOM_STATE_SIZE; ++k) { - _rdrand64_step((unsigned long long *)&tmp); - randomState[k] ^= tmp; - } + _rdrand64_step((unsigned long long *)&tmp); + randomState[2] ^= tmp; + _rdrand64_step((unsigned long long *)&tmp); + randomState[3] ^= tmp; } #endif } @@ -289,6 +292,9 @@ void getSecureRandom(void *const buf, unsigned int bytes) noexcept // replacing the first 64 bytes with this hash, and then re-initializing // AES with the first 32 bytes. randomByteCounter = 0; + randomState[4] += (uint64_t)((uintptr_t)buf); + randomState[5] += (uint64_t)bytes; + randomState[6] += (uint64_t)time(nullptr); SHA512(randomState, randomState, sizeof(randomState)); randomGen.init(randomState); } diff --git a/core/Utils.hpp b/core/Utils.hpp index e4293267c..57021d624 100644 --- a/core/Utils.hpp +++ b/core/Utils.hpp @@ -144,9 +144,6 @@ char *decimal(unsigned long n, char s[24]) noexcept; /** * Convert an unsigned integer into hex * - * The returned pointer won't point to the start of 'buf', since - * hex writing is done in reverse order. - * * @param i Any unsigned integer * @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur. * @return Pointer to s containing hex string with trailing zero byte diff --git a/core/zerotier.h b/core/zerotier.h index 3ba9780ce..66275b093 100644 --- a/core/zerotier.h +++ b/core/zerotier.h @@ -276,6 +276,181 @@ typedef void ZT_Identity; */ typedef void ZT_Locator; +/** + * Full identity fingerprint with address and 384-bit hash of public key(s) + */ +typedef struct +{ + /** + * Short address (only least significant 40 bits are used) + */ + uint64_t address; + + /** + * 384-bit hash of identity public key(s) + */ + uint8_t hash[48]; +} ZT_Fingerprint; + +/** + * Maximum length of string fields in identification certificates + */ +#define ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH 127 + +/** + * Maximum length of a signature + */ +#define ZT_IDENTIFICATION_CERTIFICATE_MAX_SIGNATURE_SIZE 256 + +/** + * Information about a real world entity. + */ +typedef struct +{ + char country[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char organization[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char unit[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char locality[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char province[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char streetAddress[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char postalCode[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char commonName[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char serialNo[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char email[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; + char url[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1]; +} ZT_IdentificationCertificate_Name; + +/** + * Identity and optional locator for a node + */ +typedef struct +{ + /** + * Identity (never NULL) + */ + const ZT_Identity *identity; + + /** + * Locator or NULL if not specified + */ + const ZT_Locator *locator; +} ZT_IdentificationCertificate_Node; + +/** + * ID and primary controller for a network + */ +typedef struct +{ + /** + * Network ID + */ + uint64_t id; + + /** + * Full fingerprint of primary controller + */ + ZT_Fingerprint controller; +} ZT_IdentificationCertificate_Network; + +/** + * Identification certificate subject + */ +typedef struct +{ + /** + * Identities and optional locators of nodes + */ + ZT_IdentificationCertificate_Node *nodes; + + /** + * Number of nodes + */ + unsigned int nodeCount; + + /** + * Networks owned by this entity + */ + ZT_IdentificationCertificate_Network *networks; + + /** + * Number of networks + */ + unsigned int networkCount; + + /** + * Information about owner of items. + */ + ZT_IdentificationCertificate_Name name; +} ZT_IdentificationCertificate_Subject; + +/** + * Identification certificate + * + * This is designed so it could be converted to/from an X509 format + * for interoperability with X509 systems. OCSP could be implemented + * too, though it would probably require the development of an OCSP + * proxy server that queried the issuer via the ZeroTier protocol. + */ +typedef struct +{ + /** + * Serial number, a SHA384 hash of this certificate. + */ + uint8_t serialNo[48]; + + /** + * Certificate version + */ + unsigned int version; + + /** + * Maximum path length from this certificate toward further certificates. + * + * Subjects may sign other certificates whose path lengths are less than + * this value. A value of zero indicates that no identification certificates + * may be signed (not a CA). + */ + unsigned int maxPathLength; + + /** + * Flags (for future use, currently zero). + * + * This could be used to implement key usage flags similar to X509 if + * these are needed. + */ + uint64_t flags; + + /** + * Valid time range: not before, not after. + */ + int64_t validity[2]; + + /** + * Subject of certificate + */ + ZT_IdentificationCertificate_Subject subject; + + /** + * Issuer node identity and public key(s). + */ + const ZT_Identity *issuer; + + /** + * Issuer information + */ + ZT_IdentificationCertificate_Name issuerName; + + /** + * Signature by issuer (algorithm determined by identity type). + */ + uint8_t signature[ZT_IDENTIFICATION_CERTIFICATE_MAX_SIGNATURE_SIZE]; + + /** + * Size of signature in bytes. + */ + unsigned int signatureSize; +} ZT_IdentificationCertificate; + /** * Credential type IDs */ @@ -308,22 +483,6 @@ enum ZT_EndpointType ZT_ENDPOINT_TYPE_IP_HTTP = 8 // IP/HTTP encapsulation }; -/** - * Full identity fingerprint with address and 384-bit hash of public key(s) - */ -typedef struct -{ - /** - * Short address (only least significant 40 bits are used) - */ - uint64_t address; - - /** - * 384-bit hash of identity public key(s) - */ - uint8_t hash[48]; -} ZT_Fingerprint; - /** * Flag indicating that VL1 tracing should be generated */ diff --git a/installsupport/windows/ZeroTier One.aip b/installsupport/windows/ZeroTier One.aip index 53ec2cf5d..61b6ec331 100644 --- a/installsupport/windows/ZeroTier One.aip +++ b/installsupport/windows/ZeroTier One.aip @@ -1,484 +1,484 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main.cpp b/main.cpp index 55e95fc28..18856e290 100644 --- a/main.cpp +++ b/main.cpp @@ -11,9 +11,41 @@ */ /****/ +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#endif + #include "zerotier_cgo.h" -int main() { - ZeroTierMain(); - return 0; +int main(int argc,char **argv) +{ + // Fork into background if run with 'service -d'. This is best done prior + // to launching the Go code, since Go likes to start thread pools and stuff + // that don't play nice with fork. This is obviously unsupported on Windows. +#if !defined(_WIN32) && !defined(_WIN64) + for(int i=1;i 0) { + return 0; + } + break; + } + } + break; + } + } +#endif + + ZeroTierMain(); + + return 0; } diff --git a/pkg/zerotier/node.go b/pkg/zerotier/node.go index d5d4349dd..68d03c72f 100644 --- a/pkg/zerotier/node.go +++ b/pkg/zerotier/node.go @@ -47,7 +47,7 @@ var nullLogger = log.New(ioutil.Discard, "", 0) const ( NetworkIDStringLength = 16 - NEtworkIDLength = 8 + NetworkIDLength = 8 AddressStringLength = 10 AddressLength = 5 @@ -883,7 +883,7 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Poi *((*uintptr)(dataP)) = 0 tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id))) if found && len(tmp) > 0 { - cData := C.malloc(C.ulong(len(tmp))) // GoGlue sends free() to the core as the free function + cData := C.ZT_malloc(C.ulong(len(tmp))) // GoGlue sends free() to the core as the free function if uintptr(cData) == 0 { return -1 } diff --git a/serviceiocore/GoGlue.cpp b/serviceiocore/GoGlue.cpp index cf4c3e0b7..ef34989c1 100644 --- a/serviceiocore/GoGlue.cpp +++ b/serviceiocore/GoGlue.cpp @@ -724,3 +724,6 @@ extern "C" int ZT_isTemporaryV6Address(const char *ifname, const struct sockaddr return 0; #endif } + +extern "C" void *ZT_malloc(unsigned long s) +{ return (void *)malloc((size_t)s); } diff --git a/serviceiocore/GoGlue.h b/serviceiocore/GoGlue.h index 622b0998e..1884f33bc 100644 --- a/serviceiocore/GoGlue.h +++ b/serviceiocore/GoGlue.h @@ -50,6 +50,8 @@ void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu); int ZT_isTemporaryV6Address(const char *ifname,const struct sockaddr_storage *a); +void *ZT_malloc(unsigned long s); + #ifdef __cplusplus } #endif