From 3b375b55c2437a97942aba9fe7662e748a6909ff Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 15 Sep 2021 09:21:41 -0700 Subject: [PATCH 01/68] fix central docker launch without redis --- ext/central-controller-docker/main.sh | 2 +- service/OneService.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/central-controller-docker/main.sh b/ext/central-controller-docker/main.sh index 7a1086907..3a625ede5 100755 --- a/ext/central-controller-docker/main.sh +++ b/ext/central-controller-docker/main.sh @@ -50,7 +50,7 @@ if [ "$ZT_USE_REDIS" == "true" ]; then } " else - REDIS="\"redis\": {}" + REDIS="\"redis\": null" fi mkdir -p /var/lib/zerotier-one diff --git a/service/OneService.cpp b/service/OneService.cpp index 4421d1cb5..c0a3a422c 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1074,7 +1074,7 @@ public: if (redis.is_object() && _rc == NULL) { _rc = new RedisConfig; _rc->hostname = OSUtils::jsonString(redis["hostname"],""); - _rc->port = redis["port"]; + _rc->port = OSUtils::jsonInt(redis["port"],0); _rc->password = OSUtils::jsonString(redis["password"],""); _rc->clusterMode = OSUtils::jsonBool(redis["clusterMode"], false); } From 90025555969532c5ceaf668068345115820bf003 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 15 Sep 2021 09:45:10 -0700 Subject: [PATCH 02/68] ensure count > 0 --- controller/PostgreSQL.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 89f5cb9ba..50b128418 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -594,12 +594,14 @@ void PostgreSQL::initializeNetworks() auto dur = std::chrono::duration_cast(end - start);; total += dur.count(); ++count; - if (count % 10000 == 0) { + if (count > 0 && count % 10000 == 0) { fprintf(stderr, "Averaging %llu us per network\n", (total/count)); } } - fprintf(stderr, "Took %llu us per network to load\n", (total/count)); + if (count > 0) { + fprintf(stderr, "Took %llu us per network to load\n", (total/count)); + } stream.complete(); w.commit(); @@ -748,11 +750,13 @@ void PostgreSQL::initializeMembers() auto dur = std::chrono::duration_cast(end - start);; total += dur.count(); ++count; - if (count % 10000 == 0) { + if (count > 0 && count % 10000 == 0) { fprintf(stderr, "Averaging %llu us per member\n", (total/count)); } } - fprintf(stderr, "Took %llu us per member to load\n", (total/count)); + if (count > 0) { + fprintf(stderr, "Took %llu us per member to load\n", (total/count)); + } stream.complete(); From 46adc1f05997ebbc7fcb8250a197ed2d648e20b9 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 15 Sep 2021 15:27:19 -0700 Subject: [PATCH 03/68] ifdef this out --- controller/PostgreSQL.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 50b128418..5f71a3699 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -157,7 +157,9 @@ PostgreSQL::PostgreSQL(const Identity &myId, const char *path, int listenPort, R memset(_ssoPsk, 0, sizeof(_ssoPsk)); char *const ssoPskHex = getenv("ZT_SSO_PSK"); +#ifdef ZT_TRACE fprintf(stderr, "ZT_SSO_PSK: %s\n", ssoPskHex); +#endif if (ssoPskHex) { // SECURITY: note that ssoPskHex will always be null-terminated if libc acatually // returns something non-NULL. If the hex encodes something shorter than 48 bytes, From 9cfb807fcb1079a7e7016be67157ff9123b20ff6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 20 Sep 2021 16:15:59 -0400 Subject: [PATCH 04/68] Don't assume roots validated the identity, just in case they did not. --- node/IncomingPacket.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5a2a94642..ae6c1a849 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -511,7 +511,10 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP case Packet::VERB_WHOIS: if (RR->topology->isUpstream(peer->identity())) { const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY); - RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr(new Peer(RR,RR->identity,id)))); + // Good idea to locally validate here even if roots are doing so. In a truly distributed + // system there should not be single points of failure for global trust assertions. + if (id.locallyValidate()) + RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr(new Peer(RR,RR->identity,id)))); } break; From 134d33c2181e5a2b783a0503313b1a03dd89d863 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 20 Sep 2021 18:26:49 -0400 Subject: [PATCH 05/68] Add a bit of hardening in the network certificate of membership by incorporating a full hash of the identity to which it is issued. This means the recipient need not depend entirely on the root verifying identities properly to make sure impersonation is not occurring. --- controller/EmbeddedNetworkController.cpp | 2 +- node/CertificateOfMembership.cpp | 52 +++++++++++++----------- node/CertificateOfMembership.hpp | 22 ++++++++-- node/Identity.hpp | 12 ++++++ node/Membership.hpp | 7 ++-- node/Network.cpp | 9 ++-- selftest.cpp | 16 ++++---- 7 files changed, 78 insertions(+), 42 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 99d59aeef..ea70cb3ae 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1801,7 +1801,7 @@ void EmbeddedNetworkController::_request( nc->certificateOfOwnershipCount = 1; } - CertificateOfMembership com(now,credentialtmd,nwid,identity.address()); + CertificateOfMembership com(now,credentialtmd,nwid,identity); if (com.sign(_signingId)) { nc->com = com; } else { diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp index 10cb0863a..2580041c8 100644 --- a/node/CertificateOfMembership.cpp +++ b/node/CertificateOfMembership.cpp @@ -148,37 +148,43 @@ void CertificateOfMembership::fromString(const char *s) #endif // ZT_SUPPORT_OLD_STYLE_NETCONF -bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const +bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other, const Identity &otherIdentity) const { - unsigned int myidx = 0; - unsigned int otheridx = 0; - if ((_qualifierCount == 0)||(other._qualifierCount == 0)) return false; - while (myidx < _qualifierCount) { - // Fail if we're at the end of other, since this means the field is - // missing. - if (otheridx >= other._qualifierCount) - return false; + std::map< uint64_t, uint64_t > otherFields; + for(unsigned int i=0;i= other._qualifierCount) + bool fullIdentityVerification = false; + for(unsigned int i=0;i<_qualifierCount;++i) { + const uint64_t qid = _qualifiers[i].id; + if ((qid >= 3)&&(qid <= 6)) { + fullIdentityVerification = true; + } else { + std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find(qid)); + if (otherQ == otherFields.end()) + return false; + const uint64_t a = _qualifiers[i].value; + const uint64_t b = otherQ->second; + if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[i].maxDelta) return false; } + } - // Compare to determine if the absolute value of the difference - // between these two parameters is within our maxDelta. - const uint64_t a = _qualifiers[myidx].value; - const uint64_t b = other._qualifiers[myidx].value; - if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[myidx].maxDelta) - return false; - - ++myidx; + // If this COM has a full hash of its identity, assume the other must have this as well. + // Otherwise we are on a controller that does not incorporate these. + if (fullIdentityVerification) { + uint64_t idHash[6]; + otherIdentity.publicKeyHash(idHash); + for(unsigned long i=0;i<4;++i) { + std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find((uint64_t)(i + 3))); + if (otherQ == otherFields.end()) + return false; + if (otherQ->second != Utils::ntoh(idHash[i])) + return false; + } } return true; diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp index f8500628d..0e4e04748 100644 --- a/node/CertificateOfMembership.hpp +++ b/node/CertificateOfMembership.hpp @@ -94,6 +94,8 @@ public: * ZeroTier address to whom certificate was issued */ COM_RESERVED_ID_ISSUED_TO = 2 + + // IDs 3-6 reserved for full hash of identity to which this COM was issued. }; /** @@ -110,7 +112,7 @@ public: * @param nwid Network ID * @param issuedTo Certificate recipient */ - CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo) + CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo) { _qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP; _qualifiers[0].value = timestamp; @@ -119,9 +121,20 @@ public: _qualifiers[1].value = nwid; _qualifiers[1].maxDelta = 0; _qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO; - _qualifiers[2].value = issuedTo.toInt(); + _qualifiers[2].value = issuedTo.address().toInt(); _qualifiers[2].maxDelta = 0xffffffffffffffffULL; - _qualifierCount = 3; + + // Include hash of full identity public key in COM for hardening purposes. Pack it in + // using the original COM format. Format may be revised in the future to make this cleaner. + uint64_t idHash[6]; + issuedTo.publicKeyHash(idHash); + for(unsigned long i=0;i<4;++i) { + _qualifiers[i + 3].id = (uint64_t)(i + 3); + _qualifiers[i + 3].value = Utils::ntoh(idHash[i]); + _qualifiers[i + 3].maxDelta = 0xffffffffffffffffULL; + } + + _qualifierCount = 7; memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN); } @@ -224,9 +237,10 @@ public: * tuples present in this cert but not in other result in 'false'. * * @param other Cert to compare with + * @param otherIdentity Identity of other node * @return True if certs agree and 'other' may be communicated with */ - bool agreesWith(const CertificateOfMembership &other) const; + bool agreesWith(const CertificateOfMembership &other, const Identity &otherIdentity) const; /** * Sign this certificate diff --git a/node/Identity.hpp b/node/Identity.hpp index e6f658dc3..cc8de5126 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -109,6 +109,18 @@ public: */ inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); } + /** + * Compute a SHA384 hash of this identity's address and public key(s). + * + * @param sha384buf Buffer with 48 bytes of space to receive hash + */ + inline void publicKeyHash(void *sha384buf) const + { + uint8_t address[ZT_ADDRESS_LENGTH]; + _address.copyTo(address, ZT_ADDRESS_LENGTH); + SHA384(sha384buf, address, ZT_ADDRESS_LENGTH, _publicKey.data, ZT_C25519_PUBLIC_KEY_LEN); + } + /** * Compute the SHA512 hash of our private key (if we have one) * diff --git a/node/Membership.hpp b/node/Membership.hpp index 476987714..63a7c10f5 100644 --- a/node/Membership.hpp +++ b/node/Membership.hpp @@ -91,13 +91,14 @@ public: * Check whether the peer represented by this Membership should be allowed on this network at all * * @param nconf Our network config + * @param otherNodeIdentity Identity of remote node * @return True if this peer is allowed on this network at all */ - inline bool isAllowedOnNetwork(const NetworkConfig &nconf) const + inline bool isAllowedOnNetwork(const NetworkConfig &thisNodeNetworkConfig, const Identity &otherNodeIdentity) const { - if (nconf.isPublic()) return true; + if (thisNodeNetworkConfig.isPublic()) return true; if (_com.timestamp() <= _comRevocationThreshold) return false; - return nconf.com.agreesWith(_com); + return thisNodeNetworkConfig.com.agreesWith(_com, otherNodeIdentity); } inline bool recentlyAssociated(const int64_t now) const diff --git a/node/Network.cpp b/node/Network.cpp index a9007258f..f3138f3ac 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1227,7 +1227,7 @@ bool Network::gate(void *tPtr,const SharedPtr &peer) try { if (_config) { Membership *m = _memberships.get(peer->address()); - if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config))) ) { + if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config, peer->identity()))) ) { if (!m) m = &(_membership(peer->address())); if (m->multicastLikeGate(now)) { @@ -1487,8 +1487,11 @@ void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMu Membership *m = (Membership *)0; Hashtable::Iterator i(_memberships); while (i.next(a,m)) { - if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) ) - _announceMulticastGroupsTo(tPtr,*a,groups); + const Identity remoteIdentity(RR->topology->getIdentity(tPtr, *a)); + if (remoteIdentity) { + if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config, remoteIdentity)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) ) + _announceMulticastGroupsTo(tPtr,*a,groups); + } } } } diff --git a/selftest.cpp b/selftest.cpp index 357e9a026..42e9bc232 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -561,8 +561,8 @@ static int testCertificate() std::cout << idA.address().toString(buf) << ", " << idB.address().toString(buf) << std::endl; std::cout << "[certificate] Generating certificates A and B..."; - CertificateOfMembership cA(10000,100,1,idA.address()); - CertificateOfMembership cB(10099,100,1,idB.address()); + CertificateOfMembership cA(10000,100,1,idA); + CertificateOfMembership cB(10099,100,1,idB); std::cout << std::endl; std::cout << "[certificate] Signing certificates A and B with authority..."; @@ -574,13 +574,13 @@ static int testCertificate() //std::cout << "[certificate] B: " << cB.toString() << std::endl; std::cout << "[certificate] A agrees with B and B with A... "; - if (cA.agreesWith(cB)) + if (cA.agreesWith(cB, idB)) std::cout << "yes, "; else { std::cout << "FAIL" << std::endl; return -1; } - if (cB.agreesWith(cA)) + if (cB.agreesWith(cA, idA)) std::cout << "yes." << std::endl; else { std::cout << "FAIL" << std::endl; @@ -588,18 +588,18 @@ static int testCertificate() } std::cout << "[certificate] Generating two certificates that should not agree..."; - cA = CertificateOfMembership(10000,100,1,idA.address()); - cB = CertificateOfMembership(10101,100,1,idB.address()); + cA = CertificateOfMembership(10000,100,1,idA); + cB = CertificateOfMembership(10101,100,1,idB); std::cout << std::endl; std::cout << "[certificate] A agrees with B and B with A... "; - if (!cA.agreesWith(cB)) + if (!cA.agreesWith(cB, idB)) std::cout << "no, "; else { std::cout << "FAIL" << std::endl; return -1; } - if (!cB.agreesWith(cA)) + if (!cB.agreesWith(cA, idA)) std::cout << "no." << std::endl; else { std::cout << "FAIL" << std::endl; From 565885a4c0240172067776fc854793ca002b8935 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 20 Sep 2021 18:38:29 -0400 Subject: [PATCH 06/68] Remove ancient controller support. # Conflicts: # RELEASE-NOTES.md --- RELEASE-NOTES.md | 19 +++- node/CertificateOfMembership.cpp | 158 ++++++------------------------- node/CertificateOfMembership.hpp | 56 +---------- node/Constants.hpp | 5 - 4 files changed, 44 insertions(+), 194 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index d73340c50..4404eaf87 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,11 +1,24 @@ ZeroTier Release Notes ====== -# 2021-08-31 -- Version 1.8.0 +# -- Version 1.8.1 + + * Fix UI issues on MacOS Mojave + * Fix icon not showing on Windows + * Re-eneable installation on Windows 7, 8, etc., but without any guarantee that it will work there! (7 is not supported) + * Add an extended hash verification to certificates of network membership to further harden against impersonation attacks + * Remove support for REALLY ancient 1.1.6 or earlier network controllers + +# 2021-09-15 -- Version 1.8.0 * A *completely* rewritten desktop UI for Mac and Windows! - * Implement a workaround for one potential source of a "coma" bug, which can occur if buggy NATs/routers stop allowing the service to communicate on a given port. ZeroTier now reassigns a new secondary port if it's offline for a while unless a secondary port is manually specified in local.conf. - * Fix for MacOS MTU issue on feth devices. + * Implement a workaround for one potential source of a "coma" bug, which can occur if buggy NATs/routers stop allowing the service to communicate on a given port. ZeroTier now reassigns a new secondary port if it's offline for a while unless a secondary port is manually specified in local.conf. Working around crummy buggy routers is an ongoing effort. + * Fix for MacOS MTU capping issue on feth devices + * Fix for mistakenly using v6 source addresses for v4 routes on some platforms + * Stop binding to temporary IPv6 addresses + * Set MAC address before bringing up Linux TAP link + * Check if DNS servers need to be applied on macOS + * Upgrade json.hpp dependency to version 3.10.2 # 2021-04-13 -- Version 1.6.5 diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp index 2580041c8..dbda9939f 100644 --- a/node/CertificateOfMembership.cpp +++ b/node/CertificateOfMembership.cpp @@ -20,134 +20,32 @@ namespace ZeroTier { -void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta) +CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo) { - _signedBy.zero(); + _qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP; + _qualifiers[0].value = timestamp; + _qualifiers[0].maxDelta = timestampMaxDelta; + _qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID; + _qualifiers[1].value = nwid; + _qualifiers[1].maxDelta = 0; + _qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO; + _qualifiers[2].value = issuedTo.address().toInt(); + _qualifiers[2].maxDelta = 0xffffffffffffffffULL; - for(unsigned int i=0;i<_qualifierCount;++i) { - if (_qualifiers[i].id == id) { - _qualifiers[i].value = value; - _qualifiers[i].maxDelta = maxDelta; - return; - } + // Include hash of full identity public key in COM for hardening purposes. Pack it in + // using the original COM format. Format may be revised in the future to make this cleaner. + uint64_t idHash[6]; + issuedTo.publicKeyHash(idHash); + for(unsigned long i=0;i<4;++i) { + _qualifiers[i + 3].id = (uint64_t)(i + 3); + _qualifiers[i + 3].value = Utils::ntoh(idHash[i]); + _qualifiers[i + 3].maxDelta = 0xffffffffffffffffULL; } - if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) { - _qualifiers[_qualifierCount].id = id; - _qualifiers[_qualifierCount].value = value; - _qualifiers[_qualifierCount].maxDelta = maxDelta; - ++_qualifierCount; - std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount])); - } -} - -#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF - -std::string CertificateOfMembership::toString() const -{ - char tmp[ZT_NETWORK_COM_MAX_QUALIFIERS * 32]; - std::string s; - - s.append("1:"); // COM_UINT64_ED25519 - - uint64_t *const buf = new uint64_t[_qualifierCount * 3]; - try { - unsigned int ptr = 0; - for(unsigned int i=0;i<_qualifierCount;++i) { - buf[ptr++] = Utils::hton(_qualifiers[i].id); - buf[ptr++] = Utils::hton(_qualifiers[i].value); - buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta); - } - s.append(Utils::hex(buf,ptr * sizeof(uint64_t),tmp)); - delete [] buf; - } catch ( ... ) { - delete [] buf; - throw; - } - - s.push_back(':'); - - s.append(_signedBy.toString(tmp)); - - if (_signedBy) { - s.push_back(':'); - s.append(Utils::hex(_signature.data,ZT_C25519_SIGNATURE_LEN,tmp)); - } - - return s; -} - -void CertificateOfMembership::fromString(const char *s) -{ - _qualifierCount = 0; - _signedBy.zero(); + _qualifierCount = 7; memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN); - - if (!*s) - return; - - unsigned int colonAt = 0; - while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt; - - if (!((colonAt == 1)&&(s[0] == '1'))) // COM_UINT64_ED25519? - return; - - s += colonAt + 1; - colonAt = 0; - while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt; - - if (colonAt) { - const unsigned int buflen = colonAt / 2; - char *const buf = new char[buflen]; - unsigned int bufactual = Utils::unhex(s,colonAt,buf,buflen); - char *bufptr = buf; - try { - while (bufactual >= 24) { - if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) { - _qualifiers[_qualifierCount].id = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8; - _qualifiers[_qualifierCount].value = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8; - _qualifiers[_qualifierCount].maxDelta = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8; - ++_qualifierCount; - } else { - bufptr += 24; - } - bufactual -= 24; - } - } catch ( ... ) {} - delete [] buf; - } - - if (s[colonAt]) { - s += colonAt + 1; - colonAt = 0; - while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt; - - if (colonAt) { - char addrbuf[ZT_ADDRESS_LENGTH]; - if (Utils::unhex(s,colonAt,addrbuf,sizeof(addrbuf)) == ZT_ADDRESS_LENGTH) - _signedBy.setTo(addrbuf,ZT_ADDRESS_LENGTH); - - if ((_signedBy)&&(s[colonAt])) { - s += colonAt + 1; - colonAt = 0; - while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt; - if (colonAt) { - if (Utils::unhex(s,colonAt,_signature.data,ZT_C25519_SIGNATURE_LEN) != ZT_C25519_SIGNATURE_LEN) - _signedBy.zero(); - } else { - _signedBy.zero(); - } - } else { - _signedBy.zero(); - } - } - } - - std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount])); } -#endif // ZT_SUPPORT_OLD_STYLE_NETCONF - bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other, const Identity &otherIdentity) const { if ((_qualifierCount == 0)||(other._qualifierCount == 0)) @@ -160,17 +58,15 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other, c bool fullIdentityVerification = false; for(unsigned int i=0;i<_qualifierCount;++i) { const uint64_t qid = _qualifiers[i].id; - if ((qid >= 3)&&(qid <= 6)) { + if ((qid >= 3)&&(qid <= 6)) fullIdentityVerification = true; - } else { - std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find(qid)); - if (otherQ == otherFields.end()) - return false; - const uint64_t a = _qualifiers[i].value; - const uint64_t b = otherQ->second; - if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[i].maxDelta) - return false; - } + std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find(qid)); + if (otherQ == otherFields.end()) + return false; + const uint64_t a = _qualifiers[i].value; + const uint64_t b = otherQ->second; + if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[i].maxDelta) + return false; } // If this COM has a full hash of its identity, assume the other must have this as well. diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp index 0e4e04748..1948dd7b7 100644 --- a/node/CertificateOfMembership.hpp +++ b/node/CertificateOfMembership.hpp @@ -112,31 +112,7 @@ public: * @param nwid Network ID * @param issuedTo Certificate recipient */ - CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo) - { - _qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP; - _qualifiers[0].value = timestamp; - _qualifiers[0].maxDelta = timestampMaxDelta; - _qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID; - _qualifiers[1].value = nwid; - _qualifiers[1].maxDelta = 0; - _qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO; - _qualifiers[2].value = issuedTo.address().toInt(); - _qualifiers[2].maxDelta = 0xffffffffffffffffULL; - - // Include hash of full identity public key in COM for hardening purposes. Pack it in - // using the original COM format. Format may be revised in the future to make this cleaner. - uint64_t idHash[6]; - issuedTo.publicKeyHash(idHash); - for(unsigned long i=0;i<4;++i) { - _qualifiers[i + 3].id = (uint64_t)(i + 3); - _qualifiers[i + 3].value = Utils::ntoh(idHash[i]); - _qualifiers[i + 3].maxDelta = 0xffffffffffffffffULL; - } - - _qualifierCount = 7; - memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN); - } + CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo); /** * Create from binary-serialized COM in buffer @@ -196,36 +172,6 @@ public: return 0ULL; } - /** - * Add or update a qualifier in this certificate - * - * Any signature is invalidated and signedBy is set to null. - * - * @param id Qualifier ID - * @param value Qualifier value - * @param maxDelta Qualifier maximum allowed difference (absolute value of difference) - */ - void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta); - inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); } - -#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF - /** - * @return String-serialized representation of this certificate - */ - std::string toString() const; - - /** - * Set this certificate equal to the hex-serialized string - * - * Invalid strings will result in invalid or undefined certificate - * contents. These will subsequently fail validation and comparison. - * Empty strings will result in an empty certificate. - * - * @param s String to deserialize - */ - void fromString(const char *s); -#endif // ZT_SUPPORT_OLD_STYLE_NETCONF - /** * Compare two certificates for parameter agreement * diff --git a/node/Constants.hpp b/node/Constants.hpp index 859fc2b66..011509294 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -646,11 +646,6 @@ */ #define ZT_TRUST_EXPIRATION 600000 -/** - * Enable support for older network configurations from older (pre-1.1.6) controllers - */ -#define ZT_SUPPORT_OLD_STYLE_NETCONF 1 - /** * Desired buffer size for UDP sockets (used in service and osdep but defined here) */ From c8a58b4b047ed36d5f208b0548a27d6ce8651b51 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 20 Sep 2021 20:02:39 -0400 Subject: [PATCH 07/68] Use a faster method of fingerprinting identities. --- node/CertificateOfMembership.cpp | 6 +++--- node/Identity.hpp | 26 ++++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp index dbda9939f..63a8efeca 100644 --- a/node/CertificateOfMembership.cpp +++ b/node/CertificateOfMembership.cpp @@ -34,8 +34,8 @@ CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t tim // Include hash of full identity public key in COM for hardening purposes. Pack it in // using the original COM format. Format may be revised in the future to make this cleaner. - uint64_t idHash[6]; - issuedTo.publicKeyHash(idHash); + uint64_t idHash[4]; + issuedTo.keyFingerprint(idHash); for(unsigned long i=0;i<4;++i) { _qualifiers[i + 3].id = (uint64_t)(i + 3); _qualifiers[i + 3].value = Utils::ntoh(idHash[i]); @@ -73,7 +73,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other, c // Otherwise we are on a controller that does not incorporate these. if (fullIdentityVerification) { uint64_t idHash[6]; - otherIdentity.publicKeyHash(idHash); + otherIdentity.keyFingerprint(idHash); for(unsigned long i=0;i<4;++i) { std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find((uint64_t)(i + 3))); if (otherQ == otherFields.end()) diff --git a/node/Identity.hpp b/node/Identity.hpp index cc8de5126..ae31e3963 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -23,6 +23,7 @@ #include "C25519.hpp" #include "Buffer.hpp" #include "SHA512.hpp" +#include "AES.hpp" #define ZT_IDENTITY_STRING_BUFFER_LENGTH 384 @@ -109,18 +110,6 @@ public: */ inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); } - /** - * Compute a SHA384 hash of this identity's address and public key(s). - * - * @param sha384buf Buffer with 48 bytes of space to receive hash - */ - inline void publicKeyHash(void *sha384buf) const - { - uint8_t address[ZT_ADDRESS_LENGTH]; - _address.copyTo(address, ZT_ADDRESS_LENGTH); - SHA384(sha384buf, address, ZT_ADDRESS_LENGTH, _publicKey.data, ZT_C25519_PUBLIC_KEY_LEN); - } - /** * Compute the SHA512 hash of our private key (if we have one) * @@ -136,6 +125,19 @@ public: return false; } + /** + * Get a 256-bit hash of this identity's public key(s) + * + * @param buf 256-bit (32-byte) buffer + */ + inline void keyFingerprint(void *buf) const + { + // This is much faster than SHA384, which matters on heavily loaded controllers. + AES c(_publicKey.data); + c.encrypt(_publicKey.data + 32, buf); + c.encrypt(_publicKey.data + 48, reinterpret_cast(buf) + 16); + } + /** * Sign a message with this identity (private key required) * From 30d5d5a8921d1740ac0b981c0c6e32292a437e39 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 20 Sep 2021 22:05:49 -0400 Subject: [PATCH 08/68] Revert "Don't assume roots validated the identity, just in case they did not." This reverts commit 39b97f91633dd8dca4a1c0834d49b7f172e9b935. --- node/IncomingPacket.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index ae6c1a849..5a2a94642 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -511,10 +511,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP case Packet::VERB_WHOIS: if (RR->topology->isUpstream(peer->identity())) { const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY); - // Good idea to locally validate here even if roots are doing so. In a truly distributed - // system there should not be single points of failure for global trust assertions. - if (id.locallyValidate()) - RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr(new Peer(RR,RR->identity,id)))); + RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr(new Peer(RR,RR->identity,id)))); } break; From b403f106fb8e078766643ba8f0d2719b3880a954 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 20 Sep 2021 22:05:39 -0400 Subject: [PATCH 09/68] Revert "Use a faster method of fingerprinting identities." This reverts commit b72e5e8386b73425f425b64cd81a7c53983d7fcf. --- node/CertificateOfMembership.cpp | 6 +++--- node/Identity.hpp | 26 ++++++++++++-------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp index 63a8efeca..dbda9939f 100644 --- a/node/CertificateOfMembership.cpp +++ b/node/CertificateOfMembership.cpp @@ -34,8 +34,8 @@ CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t tim // Include hash of full identity public key in COM for hardening purposes. Pack it in // using the original COM format. Format may be revised in the future to make this cleaner. - uint64_t idHash[4]; - issuedTo.keyFingerprint(idHash); + uint64_t idHash[6]; + issuedTo.publicKeyHash(idHash); for(unsigned long i=0;i<4;++i) { _qualifiers[i + 3].id = (uint64_t)(i + 3); _qualifiers[i + 3].value = Utils::ntoh(idHash[i]); @@ -73,7 +73,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other, c // Otherwise we are on a controller that does not incorporate these. if (fullIdentityVerification) { uint64_t idHash[6]; - otherIdentity.keyFingerprint(idHash); + otherIdentity.publicKeyHash(idHash); for(unsigned long i=0;i<4;++i) { std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find((uint64_t)(i + 3))); if (otherQ == otherFields.end()) diff --git a/node/Identity.hpp b/node/Identity.hpp index ae31e3963..cc8de5126 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -23,7 +23,6 @@ #include "C25519.hpp" #include "Buffer.hpp" #include "SHA512.hpp" -#include "AES.hpp" #define ZT_IDENTITY_STRING_BUFFER_LENGTH 384 @@ -110,6 +109,18 @@ public: */ inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); } + /** + * Compute a SHA384 hash of this identity's address and public key(s). + * + * @param sha384buf Buffer with 48 bytes of space to receive hash + */ + inline void publicKeyHash(void *sha384buf) const + { + uint8_t address[ZT_ADDRESS_LENGTH]; + _address.copyTo(address, ZT_ADDRESS_LENGTH); + SHA384(sha384buf, address, ZT_ADDRESS_LENGTH, _publicKey.data, ZT_C25519_PUBLIC_KEY_LEN); + } + /** * Compute the SHA512 hash of our private key (if we have one) * @@ -125,19 +136,6 @@ public: return false; } - /** - * Get a 256-bit hash of this identity's public key(s) - * - * @param buf 256-bit (32-byte) buffer - */ - inline void keyFingerprint(void *buf) const - { - // This is much faster than SHA384, which matters on heavily loaded controllers. - AES c(_publicKey.data); - c.encrypt(_publicKey.data + 32, buf); - c.encrypt(_publicKey.data + 48, reinterpret_cast(buf) + 16); - } - /** * Sign a message with this identity (private key required) * From ac0dc7844fb2373497a708b7df9bd69747771d06 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 5 Oct 2021 09:25:24 -0700 Subject: [PATCH 10/68] rework commit thread & some connection pool borrowing issues --- controller/ConnectionPool.hpp | 2 +- controller/PostgreSQL.cpp | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/controller/ConnectionPool.hpp b/controller/ConnectionPool.hpp index 8ffc1645c..74672eb43 100644 --- a/controller/ConnectionPool.hpp +++ b/controller/ConnectionPool.hpp @@ -95,7 +95,7 @@ public: if(m_pool.size()==0){ - if ((m_pool.size() + m_borrowed.size()) <= m_maxPoolSize) { + if ((m_pool.size() + m_borrowed.size()) < m_maxPoolSize) { try { std::shared_ptr conn = m_factory->create(); m_borrowed.insert(conn); diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 5f71a3699..6a7ea2192 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -1025,6 +1025,19 @@ void PostgreSQL::commitThread() fprintf(stderr, "not an object\n"); continue; } + + std::shared_ptr c; + try { + c = _pool->borrow(); + } catch (std::exception &e) { + fprintf(stderr, "ERROR: %s\n", e.what()); + continue; + } + + if (!c) { + fprintf(stderr, "Error getting database connection\n"); + continue; + } try { nlohmann::json *config = &(qitem.first); @@ -1032,7 +1045,6 @@ void PostgreSQL::commitThread() if (objtype == "member") { // fprintf(stderr, "%s: commitThread: member\n", _myAddressStr.c_str()); try { - auto c = _pool->borrow(); pqxx::work w(*c->c); std::string memberId = (*config)["id"]; @@ -1097,11 +1109,13 @@ void PostgreSQL::commitThread() fprintf(stderr, "%s: ipAssignError\n", _myAddressStr.c_str()); delete config; config = nullptr; + w.abort(); + _pool->unborrow(c); + c.reset(); continue; } w.commit(); - _pool->unborrow(c); const uint64_t nwidInt = OSUtils::jsonIntHex((*config)["nwid"], 0ULL); const uint64_t memberidInt = OSUtils::jsonIntHex((*config)["id"], 0ULL); @@ -1124,7 +1138,6 @@ void PostgreSQL::commitThread() } else if (objtype == "network") { try { // fprintf(stderr, "%s: commitThread: network\n", _myAddressStr.c_str()); - auto c = _pool->borrow(); pqxx::work w(*c->c); std::string id = (*config)["id"]; @@ -1244,7 +1257,6 @@ void PostgreSQL::commitThread() id, domain, s); w.commit(); - _pool->unborrow(c); const uint64_t nwidInt = OSUtils::jsonIntHex((*config)["nwid"], 0ULL); if (nwidInt) { @@ -1264,7 +1276,6 @@ void PostgreSQL::commitThread() } else if (objtype == "_delete_network") { // fprintf(stderr, "%s: commitThread: delete network\n", _myAddressStr.c_str()); try { - auto c = _pool->borrow(); pqxx::work w(*c->c); std::string networkId = (*config)["nwid"]; @@ -1273,7 +1284,6 @@ void PostgreSQL::commitThread() networkId); w.commit(); - _pool->unborrow(c); } catch (std::exception &e) { fprintf(stderr, "%s ERROR: Error deleting network: %s\n", _myAddressStr.c_str(), e.what()); } @@ -1281,7 +1291,6 @@ void PostgreSQL::commitThread() } else if (objtype == "_delete_member") { // fprintf(stderr, "%s commitThread: delete member\n", _myAddressStr.c_str()); try { - auto c = _pool->borrow(); pqxx::work w(*c->c); std::string memberId = (*config)["id"]; @@ -1292,7 +1301,6 @@ void PostgreSQL::commitThread() memberId, networkId); w.commit(); - _pool->unborrow(c); } catch (std::exception &e) { fprintf(stderr, "%s ERROR: Error deleting member: %s\n", _myAddressStr.c_str(), e.what()); } @@ -1302,6 +1310,8 @@ void PostgreSQL::commitThread() } catch (std::exception &e) { fprintf(stderr, "%s ERROR: Error getting objtype: %s\n", _myAddressStr.c_str(), e.what()); } + _pool->unborrow(c); + c.reset(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } From 4d26b5a8688f38f125409f65d51d935b35aacd6b Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 5 Oct 2021 17:02:50 -0700 Subject: [PATCH 11/68] no reason for this to be a pointer --- controller/PostgreSQL.cpp | 118 ++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 6a7ea2192..53d88d6fd 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -1040,20 +1040,32 @@ void PostgreSQL::commitThread() } try { - nlohmann::json *config = &(qitem.first); - const std::string objtype = (*config)["objtype"]; + nlohmann::json &config = (qitem.first); + const std::string objtype = config["objtype"]; if (objtype == "member") { // fprintf(stderr, "%s: commitThread: member\n", _myAddressStr.c_str()); + std::string memberId; + std::string networkId; try { pqxx::work w(*c->c); - std::string memberId = (*config)["id"]; - std::string networkId = (*config)["nwid"]; + memberId = config["id"]; + networkId = config["nwid"]; + std::string target = "NULL"; - if (!(*config)["remoteTraceTarget"].is_null()) { - target = (*config)["remoteTraceTarget"]; + if (!config["remoteTraceTarget"].is_null()) { + target = config["remoteTraceTarget"]; } + pqxx::row nwrow = w.exec_params1("SELECT COUNT(id) FROM ztc_network WHERE id = $1", networkId); + int nwcount = nwrow[0].as(); + + if (nwcount != 1) { + fprintf(stderr, "network %s does not exist. skipping member upsert\n", networkId.c_str()); + w.abort(); + _pool->unborrow(c); + continue; + } pqxx::result res = w.exec_params0( "INSERT INTO ztc_member (id, network_id, active_bridge, authorized, capabilities, " @@ -1070,21 +1082,21 @@ void PostgreSQL::commitThread() "v_minor = EXCLUDED.v_minor, v_rev = EXCLUDED.v_rev, v_proto = EXCLUDED.v_proto", memberId, networkId, - (bool)(*config)["activeBridge"], - (bool)(*config)["authorized"], - OSUtils::jsonDump((*config)["capabilities"], -1), - OSUtils::jsonString((*config)["identity"], ""), - (uint64_t)(*config)["lastAuthorizedTime"], - (uint64_t)(*config)["lastDeauthorizedTime"], - (bool)(*config)["noAutoAssignIps"], - (int)(*config)["remoteTraceLevel"], + (bool)config["activeBridge"], + (bool)config["authorized"], + OSUtils::jsonDump(config["capabilities"], -1), + OSUtils::jsonString(config["identity"], ""), + (uint64_t)config["lastAuthorizedTime"], + (uint64_t)config["lastDeauthorizedTime"], + (bool)config["noAutoAssignIps"], + (int)config["remoteTraceLevel"], target, - (uint64_t)(*config)["revision"], - OSUtils::jsonDump((*config)["tags"], -1), - (int)(*config)["vMajor"], - (int)(*config)["vMinor"], - (int)(*config)["vRev"], - (int)(*config)["vProto"]); + (uint64_t)config["revision"], + OSUtils::jsonDump(config["tags"], -1), + (int)config["vMajor"], + (int)config["vMinor"], + (int)config["vRev"], + (int)config["vProto"]); res = w.exec_params0("DELETE FROM ztc_member_ip_assignment WHERE member_id = $1 AND network_id = $2", @@ -1092,7 +1104,7 @@ void PostgreSQL::commitThread() std::vector assignments; bool ipAssignError = false; - for (auto i = (*config)["ipAssignments"].begin(); i != (*config)["ipAssignments"].end(); ++i) { + for (auto i = config["ipAssignments"].begin(); i != config["ipAssignments"].end(); ++i) { std::string addr = *i; if (std::find(assignments.begin(), assignments.end(), addr) != assignments.end()) { @@ -1107,8 +1119,6 @@ void PostgreSQL::commitThread() } if (ipAssignError) { fprintf(stderr, "%s: ipAssignError\n", _myAddressStr.c_str()); - delete config; - config = nullptr; w.abort(); _pool->unborrow(c); c.reset(); @@ -1117,13 +1127,13 @@ void PostgreSQL::commitThread() w.commit(); - const uint64_t nwidInt = OSUtils::jsonIntHex((*config)["nwid"], 0ULL); - const uint64_t memberidInt = OSUtils::jsonIntHex((*config)["id"], 0ULL); + const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL); + const uint64_t memberidInt = OSUtils::jsonIntHex(config["id"], 0ULL); if (nwidInt && memberidInt) { nlohmann::json nwOrig; nlohmann::json memOrig; - nlohmann::json memNew(*config); + nlohmann::json memNew(config); get(nwidInt, nwOrig, memberidInt, memOrig); @@ -1131,23 +1141,22 @@ void PostgreSQL::commitThread() } else { fprintf(stderr, "%s: Can't notify of change. Error parsing nwid or memberid: %llu-%llu\n", _myAddressStr.c_str(), (unsigned long long)nwidInt, (unsigned long long)memberidInt); } - } catch (std::exception &e) { - fprintf(stderr, "%s ERROR: Error updating member: %s\n", _myAddressStr.c_str(), e.what()); + fprintf(stderr, "%s ERROR: Error updating member %s-%s: %s\n", _myAddressStr.c_str(), networkId.c_str(), memberId.c_str(), e.what()); } } else if (objtype == "network") { try { // fprintf(stderr, "%s: commitThread: network\n", _myAddressStr.c_str()); pqxx::work w(*c->c); - std::string id = (*config)["id"]; + std::string id = config["id"]; std::string remoteTraceTarget = ""; - if(!(*config)["remoteTraceTarget"].is_null()) { - remoteTraceTarget = (*config)["remoteTraceTarget"]; + if(!config["remoteTraceTarget"].is_null()) { + remoteTraceTarget = config["remoteTraceTarget"]; } std::string rulesSource = ""; - if ((*config)["rulesSource"].is_string()) { - rulesSource = (*config)["rulesSource"]; + if (config["rulesSource"].is_string()) { + rulesSource = config["rulesSource"]; } // This ugly query exists because when we want to mirror networks to/from @@ -1176,25 +1185,25 @@ void PostgreSQL::commitThread() "sso_enabled = EXCLUDED.sso_enabled", id, _myAddressStr, - OSUtils::jsonDump((*config)["capabilitles"], -1), - (bool)(*config)["enableBroadcast"], + OSUtils::jsonDump(config["capabilitles"], -1), + (bool)config["enableBroadcast"], OSUtils::now(), - (int)(*config)["mtu"], - (int)(*config)["multicastLimit"], - OSUtils::jsonString((*config)["name"],""), - (bool)(*config)["private"], - (int)(*config)["remoteTraceLevel"], + (int)config["mtu"], + (int)config["multicastLimit"], + OSUtils::jsonString(config["name"],""), + (bool)config["private"], + (int)config["remoteTraceLevel"], remoteTraceTarget, - OSUtils::jsonDump((*config)["rules"], -1), + OSUtils::jsonDump(config["rules"], -1), rulesSource, - OSUtils::jsonDump((*config)["tags"], -1), - OSUtils::jsonDump((*config)["v4AssignMode"],-1), - OSUtils::jsonDump((*config)["v6AssignMode"], -1), - OSUtils::jsonBool((*config)["ssoEnabled"], false)); + OSUtils::jsonDump(config["tags"], -1), + OSUtils::jsonDump(config["v4AssignMode"],-1), + OSUtils::jsonDump(config["v6AssignMode"], -1), + OSUtils::jsonBool(config["ssoEnabled"], false)); res = w.exec_params0("DELETE FROM ztc_network_assignment_pool WHERE network_id = $1", 0); - auto pool = (*config)["ipAssignmentPools"]; + auto pool = config["ipAssignmentPools"]; bool err = false; for (auto i = pool.begin(); i != pool.end(); ++i) { std::string start = (*i)["ipRangeStart"]; @@ -1207,7 +1216,7 @@ void PostgreSQL::commitThread() res = w.exec_params0("DELETE FROM ztc_network_route WHERE network_id = $1", id); - auto routes = (*config)["routes"]; + auto routes = config["routes"]; err = false; for (auto i = routes.begin(); i != routes.end(); ++i) { std::string t = (*i)["target"]; @@ -1234,12 +1243,10 @@ void PostgreSQL::commitThread() fprintf(stderr, "%s: route add error\n", _myAddressStr.c_str()); w.abort(); _pool->unborrow(c); - delete config; - config = nullptr; continue; } - auto dns = (*config)["dns"]; + auto dns = config["dns"]; std::string domain = dns["domain"]; std::stringstream servers; servers << "{"; @@ -1258,10 +1265,10 @@ void PostgreSQL::commitThread() w.commit(); - const uint64_t nwidInt = OSUtils::jsonIntHex((*config)["nwid"], 0ULL); + const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL); if (nwidInt) { nlohmann::json nwOrig; - nlohmann::json nwNew(*config); + nlohmann::json nwNew(config); get(nwidInt, nwOrig); @@ -1269,7 +1276,6 @@ void PostgreSQL::commitThread() } else { fprintf(stderr, "%s: Can't notify network changed: %llu\n", _myAddressStr.c_str(), (unsigned long long)nwidInt); } - } catch (std::exception &e) { fprintf(stderr, "%s ERROR: Error updating network: %s\n", _myAddressStr.c_str(), e.what()); } @@ -1278,7 +1284,7 @@ void PostgreSQL::commitThread() try { pqxx::work w(*c->c); - std::string networkId = (*config)["nwid"]; + std::string networkId = config["nwid"]; pqxx::result res = w.exec_params0("UPDATE ztc_network SET deleted = true WHERE id = $1", networkId); @@ -1293,8 +1299,8 @@ void PostgreSQL::commitThread() try { pqxx::work w(*c->c); - std::string memberId = (*config)["id"]; - std::string networkId = (*config)["nwid"]; + std::string memberId = config["id"]; + std::string networkId = config["nwid"]; pqxx::result res = w.exec_params0( "UPDATE ztc_member SET hidden = true, deleted = true WHERE id = $1 AND network_id = $2", From 3818351287eab9c734011c7be61e0011a56f30c1 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 6 Oct 2021 09:39:30 -0700 Subject: [PATCH 12/68] use pqxx::pipeline for online update thread --- controller/PostgreSQL.cpp | 48 +++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 53d88d6fd..9edcf0594 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -1337,6 +1337,7 @@ void PostgreSQL::onlineNotification_Postgres() nlohmann::json jtmp1, jtmp2; while (_run == 1) { auto c = _pool->borrow(); + auto c2 = _pool->borrow(); try { fprintf(stderr, "%s onlineNotification_Postgres\n", _myAddressStr.c_str()); std::unordered_map< std::pair,std::pair,_PairHasher > lastOnline; @@ -1346,15 +1347,16 @@ void PostgreSQL::onlineNotification_Postgres() } pqxx::work w(*c->c); + pqxx::work w2(*c2->c); - // using pqxx::stream_to would be a really nice alternative here, but - // unfortunately it doesn't support upserts. - // fprintf(stderr, "online notification tick\n"); - std::stringstream memberUpdate; - memberUpdate << "INSERT INTO ztc_member_status (network_id, member_id, address, last_updated) VALUES "; + fprintf(stderr, "online notification tick\n"); + bool firstRun = true; bool memberAdded = false; int updateCount = 0; + + pqxx::pipeline pipe(w); + for (auto i=lastOnline.begin(); i != lastOnline.end(); ++i) { updateCount += 1; uint64_t nwid_i = i->first.first; @@ -1371,16 +1373,10 @@ void PostgreSQL::onlineNotification_Postgres() std::string networkId(nwidTmp); std::string memberId(memTmp); - const char *qvals[2] = { - networkId.c_str(), - memberId.c_str() - }; - try { - pqxx::row r = w.exec_params1("SELECT id, network_id FROM ztc_member WHERE network_id = $1 AND id = $2", + pqxx::row r = w2.exec_params1("SELECT id, network_id FROM ztc_member WHERE network_id = $1 AND id = $2", networkId, memberId); } catch (pqxx::unexpected_rows &e) { - // fprintf(stderr, "Member count failed: %s\n", e.what()); continue; } @@ -1388,32 +1384,30 @@ void PostgreSQL::onlineNotification_Postgres() std::string ipAddr = i->second.second.toIpString(ipTmp); std::string timestamp = std::to_string(ts); - if (firstRun) { - firstRun = false; - } else { - memberUpdate << ", "; - } - - memberUpdate << "('" << networkId << "', '" << memberId << "', "; + std::stringstream memberUpdate; + memberUpdate << "INSERT INTO ztc_member_status (network_id, member_id, address, last_updated) VALUES " + << "('" << networkId << "', '" << memberId << "', "; if (ipAddr.empty()) { memberUpdate << "NULL, "; } else { memberUpdate << "'" << ipAddr << "', "; } - memberUpdate << "TO_TIMESTAMP(" << timestamp << "::double precision/1000))"; - memberAdded = true; - } - memberUpdate << " ON CONFLICT (network_id, member_id) DO UPDATE SET address = EXCLUDED.address, last_updated = EXCLUDED.last_updated;"; + memberUpdate << "TO_TIMESTAMP(" << timestamp << "::double precision/1000)) " + << " ON CONFLICT (network_id, member_id) DO UPDATE SET address = EXCLUDED.address, last_updated = EXCLUDED.last_updated"; - if (memberAdded) { - //fprintf(stderr, "%s\n", memberUpdate.str().c_str()); - pqxx::result res = w.exec0(memberUpdate.str()); - w.commit(); + pipe.insert(memberUpdate.str()); } + while(!pipe.empty()) { + pipe.retrieve(); + } + + pipe.complete(); + w.commit(); fprintf(stderr, "%s: Updated online status of %d members\n", _myAddressStr.c_str(), updateCount); } catch (std::exception &e) { fprintf(stderr, "%s: error in onlinenotification thread: %s\n", _myAddressStr.c_str(), e.what()); } + _pool->unborrow(c2); _pool->unborrow(c); ConnectionPoolStats stats = _pool->get_stats(); From 271dfc0d2b90ec8ad46e7b188441734bf974f731 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 27 Oct 2021 16:11:06 -0700 Subject: [PATCH 13/68] initial rust oidc lib --- .gitignore | 1 + zeroidc/Cargo.lock | 1330 ++++++++++++++++++++++++++++++++++++++++++++ zeroidc/Cargo.toml | 17 + zeroidc/build.rs | 37 ++ zeroidc/src/lib.rs | 13 + 5 files changed, 1398 insertions(+) create mode 100644 zeroidc/Cargo.lock create mode 100644 zeroidc/Cargo.toml create mode 100644 zeroidc/build.rs create mode 100644 zeroidc/src/lib.rs diff --git a/.gitignore b/.gitignore index 2143346ff..a67ae875e 100755 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ attic/world/*.c25519 attic/world/mkworld workspace/ workspace2/ +zeroidc/target/ diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock new file mode 100644 index 000000000..bb202f290 --- /dev/null +++ b/zeroidc/Cargo.lock @@ -0,0 +1,1330 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cbindgen" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" + +[[package]] +name = "futures-io" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" + +[[package]] +name = "futures-macro" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" + +[[package]] +name = "futures-task" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" + +[[package]] +name = "futures-util" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +dependencies = [ + "autocfg", + "futures-core", + "futures-io", + "futures-macro", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" + +[[package]] +name = "hyper" +version = "0.14.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +dependencies = [ + "futures-util", + "hyper", + "log", + "rustls", + "tokio", + "tokio-rustls", + "webpki", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oauth2" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e47cfc4c0a1a519d9a025ebfbac3a2439d1b5cdf397d72dcb79b11d9920dab" +dependencies = [ + "base64 0.13.0", + "chrono", + "getrandom 0.2.3", + "http", + "rand 0.8.4", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror", + "url", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openidconnect" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42ff51db0f23ae55dd6f234a15ed7bb468bc97938670693a3eaa42869110167" +dependencies = [ + "base64 0.12.3", + "chrono", + "http", + "itertools", + "log", + "oauth2", + "rand 0.7.3", + "ring", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_path_to_error", + "thiserror", + "untrusted", + "url", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" +dependencies = [ + "base64 0.13.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rustls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.0", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a65a7291a8a568adcae4c10a677ebcedbc6c9cec91c054dee2ce40b0e3290eb" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0421d4f173fab82d72d6babf36d57fae38b994ca5c2d78e704260ba6d12118b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "socket2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand 0.8.4", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "winapi", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", + "serde", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + +[[package]] +name = "zeroidc" +version = "0.1.0" +dependencies = [ + "cbindgen", + "openidconnect", +] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml new file mode 100644 index 000000000..0dd5bc919 --- /dev/null +++ b/zeroidc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "zeroidc" +version = "0.1.0" +edition = "2018" +build = "build.rs" + +[lib] +crate-type = ["staticlib"] + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +openidconnect = "2.1.0" + +[build-dependencies] +cbindgen = "0.20.0" \ No newline at end of file diff --git a/zeroidc/build.rs b/zeroidc/build.rs new file mode 100644 index 000000000..de8109d11 --- /dev/null +++ b/zeroidc/build.rs @@ -0,0 +1,37 @@ +extern crate cbindgen; + +use std::env; +use std::path::PathBuf; +use cbindgen::{Config, Language}; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + let package_name = env::var("CARGO_PKG_NAME").unwrap(); + let output_file = target_dir() + .join(format!("{}.hpp", package_name)) + .display() + .to_string(); + + let config = Config { + language: Language::C, + cpp_compat: true, + namespace: Some(String::from("zeroidc")), + ..Default::default() + }; + + cbindgen::generate_with_config(&crate_dir, config) + .unwrap() + .write_to_file(&output_file); +} + +/// Find the location of the `target/` directory. Note that this may be +/// overridden by `cmake`, so we also need to check the `CARGO_TARGET_DIR` +/// variable. +fn target_dir() -> PathBuf { + if let Ok(target) = env::var("CARGO_TARGET_DIR") { + PathBuf::from(target) + } else { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("target") + } +} \ No newline at end of file diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs new file mode 100644 index 000000000..7e80cdfc6 --- /dev/null +++ b/zeroidc/src/lib.rs @@ -0,0 +1,13 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} + + +#[no_mangle] +pub extern "C" fn hello_rust() { + println!("Hello, Rust from C!") +} From c689c0bd8ced0c9cd5c40bb06308848ffee7f204 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 27 Oct 2021 17:09:01 -0700 Subject: [PATCH 14/68] integrate with mac build system and add skeleton --- make-mac.mk | 13 ++++++++++--- zeroidc/build.rs | 2 +- zeroidc/src/lib.rs | 27 ++++++++++++++++++--------- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/make-mac.mk b/make-mac.mk index 7438ad9ed..eeddba88b 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -103,8 +103,8 @@ mac-agent: FORCE osdep/MacDNSHelper.o: osdep/MacDNSHelper.mm $(CXX) $(CXXFLAGS) -c osdep/MacDNSHelper.mm -o osdep/MacDNSHelper.o -one: $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent - $(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS) +one: $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent zeroidc + $(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS) zeroidc/target/libzeroidc.a # $(STRIP) zerotier-one ln -sf zerotier-one zerotier-idtool ln -sf zerotier-one zerotier-cli @@ -112,6 +112,13 @@ one: $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent zerotier-one: one +zeroidc: zeroidc/target/libzeroidc.a + +zeroidc/target/libzeroidc.a: + cd zeroidc && cargo build --target=x86_64-apple-darwin --release + cd zeroidc && cargo build --target=aarch64-apple-darwin --release + cd zeroidc && lipo -create target/x86_64-apple-darwin/release/libzeroidc.a target/aarch64-apple-darwin/release/libzeroidc.a -output target/libzeroidc.a + central-controller: make ARCH_FLAGS="-arch x86_64" ZT_CONTROLLER=1 one @@ -157,7 +164,7 @@ central-controller-docker: FORCE docker build --no-cache -t registry.zerotier.com/zerotier-central/ztcentral-controller:${TIMESTAMP} -f ext/central-controller-docker/Dockerfile --build-arg git_branch=$(shell git name-rev --name-only HEAD) . clean: - rm -rf MacEthernetTapAgent *.dSYM build-* *.a *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier doc/node_modules zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_* + rm -rf MacEthernetTapAgent *.dSYM build-* *.a *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier doc/node_modules zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_* zeroidc/target/ distclean: clean diff --git a/zeroidc/build.rs b/zeroidc/build.rs index de8109d11..0b417c084 100644 --- a/zeroidc/build.rs +++ b/zeroidc/build.rs @@ -9,7 +9,7 @@ fn main() { let package_name = env::var("CARGO_PKG_NAME").unwrap(); let output_file = target_dir() - .join(format!("{}.hpp", package_name)) + .join(format!("{}.h", package_name)) .display() .to_string(); diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 7e80cdfc6..5d4c83f23 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -1,13 +1,22 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +#[repr(C)] +pub struct ZeroIDC {} #[no_mangle] -pub extern "C" fn hello_rust() { - println!("Hello, Rust from C!") +pub extern "C" fn zeroidc_new() -> Box { + Box::new(ZeroIDC{}) } + +#[no_mangle] +pub extern "C" fn zeroidc_delete(_: Option>) {} + +#[no_mangle] +pub extern "C" fn zeroidc_start(idc: &mut ZeroIDC) { + +} + +#[no_mangle] +pub extern "C" fn zeroidc_stop(idc: &mut ZeroIDC) { + +} + From 0069b1bac3e60204734fa24a58e9da895a6627e6 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 28 Oct 2021 09:29:33 -0700 Subject: [PATCH 15/68] we can start & stop a thread. so that's nice. --- zeroidc/Cargo.toml | 2 +- zeroidc/src/lib.rs | 62 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 0dd5bc919..892f8a38e 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -14,4 +14,4 @@ crate-type = ["staticlib"] openidconnect = "2.1.0" [build-dependencies] -cbindgen = "0.20.0" \ No newline at end of file +cbindgen = "0.20.0" diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 5d4c83f23..d275a0463 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -1,22 +1,68 @@ +use std::sync::{Arc, Mutex}; +use std::thread::{sleep, spawn, JoinHandle}; +use std::time::Duration; -#[repr(C)] -pub struct ZeroIDC {} +pub struct ZeroIDC { + inner: Arc>, +} + +struct Inner { + running: bool, + oidc_thread: Option>, +} + +impl ZeroIDC { + fn new() -> ZeroIDC { + ZeroIDC { + inner: Arc::new(Mutex::new(Inner { + running: false, + oidc_thread: None, + })), + } + } + + fn start(&mut self) { + let local = Arc::clone(&self.inner); + + if !(*local.lock().unwrap()).running { + let inner_local = Arc::clone(&self.inner); + (*local.lock().unwrap()).oidc_thread = Some(spawn(move || { + (*inner_local.lock().unwrap()).running = true; + + while (*inner_local.lock().unwrap()).running { + println!("tick"); + sleep(Duration::from_secs(1)); + } + + println!("thread done!") + })); + } + } + + fn stop(&mut self) { + let local = self.inner.clone(); + if (*local.lock().unwrap()).running { + if let Some(u) = (*local.lock().unwrap()).oidc_thread.take() { + u.join().expect("join failed"); + } + } + } +} #[no_mangle] pub extern "C" fn zeroidc_new() -> Box { - Box::new(ZeroIDC{}) + Box::new(ZeroIDC::new()) } #[no_mangle] pub extern "C" fn zeroidc_delete(_: Option>) {} #[no_mangle] -pub extern "C" fn zeroidc_start(idc: &mut ZeroIDC) { - +pub extern "C" fn zeroidc_start(idc: &'static mut ZeroIDC) { + idc.start(); } #[no_mangle] -pub extern "C" fn zeroidc_stop(idc: &mut ZeroIDC) { - +pub extern "C" fn zeroidc_stop(idc: &'static mut ZeroIDC) { + idc.stop(); } - From 31888f5ef85ed7b8d1634fb6051d7f0f2671c373 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 29 Oct 2021 09:43:14 -0700 Subject: [PATCH 16/68] provide issuer URL, client ID and local web port to constructor also construct the oidc client --- zeroidc/Cargo.lock | 2 + zeroidc/Cargo.toml | 2 + zeroidc/src/lib.rs | 103 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index bb202f290..24479eaaf 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -1325,6 +1325,8 @@ dependencies = [ name = "zeroidc" version = "0.1.0" dependencies = [ + "base64 0.13.0", "cbindgen", "openidconnect", + "url", ] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 892f8a38e..814a497c5 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -12,6 +12,8 @@ crate-type = ["staticlib"] [dependencies] openidconnect = "2.1.0" +base64 = "0.13.0" +url = "2.2.2" [build-dependencies] cbindgen = "0.20.0" diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index d275a0463..dc94d95ee 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -1,7 +1,20 @@ +extern crate base64; +extern crate openidconnect; +extern crate url; + use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn, JoinHandle}; use std::time::Duration; +use openidconnect::core::{CoreClient, CoreProviderMetadata}; +use openidconnect::reqwest::http_client; +use openidconnect::{ClientId, IssuerUrl, RedirectUrl}; + +use url::Url; + +use std::ffi::CStr; +use std::os::raw::c_char; + pub struct ZeroIDC { inner: Arc>, } @@ -9,16 +22,50 @@ pub struct ZeroIDC { struct Inner { running: bool, oidc_thread: Option>, + oidc_client: Option, } impl ZeroIDC { - fn new() -> ZeroIDC { - ZeroIDC { + fn new(issuer: &str, client_id: &str, port: u16) -> Result { + let idc = ZeroIDC { inner: Arc::new(Mutex::new(Inner { running: false, oidc_thread: None, + oidc_client: None, })), - } + }; + + let iss = match IssuerUrl::new(issuer.to_string()) { + Ok(i) => i, + Err(e) => return Err(e.to_string()), + }; + + let provider_meta = match CoreProviderMetadata::discover(&iss, http_client) { + Ok(m) => m, + Err(e) => return Err(e.to_string()), + }; + + let r = format!("http://localhost:{}", port); + let redir_url = match Url::parse(&r) { + Ok(s) => s, + Err(e) => return Err(e.to_string()), + }; + + let redirect = match RedirectUrl::new(redir_url.to_string()) { + Ok(s) => s, + Err(e) => return Err(e.to_string()), + }; + + (*idc.inner.lock().unwrap()).oidc_client = Some( + CoreClient::from_provider_metadata( + provider_meta, + ClientId::new(client_id.to_string()), + None, + ) + .set_redirect_uri(redirect), + ); + + Ok(idc) } fn start(&mut self) { @@ -50,19 +97,59 @@ impl ZeroIDC { } #[no_mangle] -pub extern "C" fn zeroidc_new() -> Box { - Box::new(ZeroIDC::new()) +pub extern "C" fn zeroidc_new( + issuer: *const c_char, + client_id: *const c_char, + web_port: u16, +) -> *mut ZeroIDC { + if issuer.is_null() { + println!("issuer is null"); + return std::ptr::null_mut(); + } + + if client_id.is_null() { + println!("client_id is null"); + return std::ptr::null_mut(); + } + + let iss = unsafe { CStr::from_ptr(issuer) }; + let c_id = unsafe { CStr::from_ptr(client_id) }; + + match ZeroIDC::new(iss.to_str().unwrap(), c_id.to_str().unwrap(), web_port) { + Ok(idc) => { + return Box::into_raw(Box::new(idc)); + } + Err(s) => { + println!("Error creating ZeroIDC instance: {}", s); + return std::ptr::null_mut(); + } + } } #[no_mangle] -pub extern "C" fn zeroidc_delete(_: Option>) {} +pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) { + if ptr.is_null() { + return; + } + unsafe { + Box::from_raw(ptr); + } +} #[no_mangle] -pub extern "C" fn zeroidc_start(idc: &'static mut ZeroIDC) { +pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; idc.start(); } #[no_mangle] -pub extern "C" fn zeroidc_stop(idc: &'static mut ZeroIDC) { +pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; idc.stop(); } From 56cf874d994c79830d8b630b9abdc4f08d9cdedf Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 29 Oct 2021 15:31:23 -0700 Subject: [PATCH 17/68] slow progress --- zeroidc/src/lib.rs | 73 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index dc94d95ee..99db6517d 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -6,9 +6,10 @@ use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn, JoinHandle}; use std::time::Duration; -use openidconnect::core::{CoreClient, CoreProviderMetadata}; +use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; -use openidconnect::{ClientId, IssuerUrl, RedirectUrl}; +use openidconnect::AuthenticationFlow; +use openidconnect::{ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, Scope}; use url::Url; @@ -21,15 +22,36 @@ pub struct ZeroIDC { struct Inner { running: bool, + auth_endpoint: String, oidc_thread: Option>, oidc_client: Option, } +fn csrf_func(csrf_token: String) -> Box CsrfToken> { + return Box::new(move || CsrfToken::new(csrf_token.to_string())); +} + +fn nonce_func(nonce: String) -> Box Nonce> { + return Box::new(move || Nonce::new(nonce.to_string())); +} + +struct authres { + url: Url, + csrf_token: CsrfToken, + nonce: Nonce, +} + impl ZeroIDC { - fn new(issuer: &str, client_id: &str, port: u16) -> Result { + fn new( + issuer: &str, + client_id: &str, + auth_ep: &str, + local_web_port: u16, + ) -> Result { let idc = ZeroIDC { inner: Arc::new(Mutex::new(Inner { running: false, + auth_endpoint: auth_ep.to_string(), oidc_thread: None, oidc_client: None, })), @@ -45,7 +67,7 @@ impl ZeroIDC { Err(e) => return Err(e.to_string()), }; - let r = format!("http://localhost:{}", port); + let r = format!("http://localhost:{}/sso", local_web_port); let redir_url = match Url::parse(&r) { Ok(s) => s, Err(e) => return Err(e.to_string()), @@ -94,13 +116,40 @@ impl ZeroIDC { } } } + + fn get_auth_url(&mut self) -> Option { + let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + + let r = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { + let (auth_url, csrf_token, nonce) = c + .authorize_url( + AuthenticationFlow::::AuthorizationCode, + csrf_func("my-csrf".to_string()), + nonce_func("my-nonce".to_string()), + ) + .add_scope(Scope::new("read".to_string())) + .add_scope(Scope::new("read".to_string())) + .add_scope(Scope::new("openid".to_string())) + .set_pkce_challenge(pkce_challenge) + .url(); + + return authres { + url: auth_url, + csrf_token, + nonce, + }; + }); + + r + } } #[no_mangle] pub extern "C" fn zeroidc_new( issuer: *const c_char, client_id: *const c_char, - web_port: u16, + auth_endpoint: *const c_char, + web_listen_port: u16, ) -> *mut ZeroIDC { if issuer.is_null() { println!("issuer is null"); @@ -112,10 +161,20 @@ pub extern "C" fn zeroidc_new( return std::ptr::null_mut(); } + if auth_endpoint.is_null() { + println!("auth_endpoint is null"); + return std::ptr::null_mut(); + } + let iss = unsafe { CStr::from_ptr(issuer) }; let c_id = unsafe { CStr::from_ptr(client_id) }; - - match ZeroIDC::new(iss.to_str().unwrap(), c_id.to_str().unwrap(), web_port) { + let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) }; + match ZeroIDC::new( + iss.to_str().unwrap(), + c_id.to_str().unwrap(), + auth_endpoint.to_str().unwrap(), + web_listen_port, + ) { Ok(idc) => { return Box::into_raw(Box::new(idc)); } From ebc4c898ff15824f8238255a9ffc91b145315812 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 29 Oct 2021 15:43:39 -0700 Subject: [PATCH 18/68] pass in csrf token & nonce (generated externally) --- zeroidc/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 99db6517d..ccc376652 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -8,7 +8,7 @@ use std::time::Duration; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; -use openidconnect::AuthenticationFlow; +use openidconnect::{AuthenticationFlow, PkceCodeVerifier}; use openidconnect::{ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, Scope}; use url::Url; @@ -39,6 +39,7 @@ struct authres { url: Url, csrf_token: CsrfToken, nonce: Nonce, + pkce_verifier: PkceCodeVerifier, } impl ZeroIDC { @@ -117,15 +118,15 @@ impl ZeroIDC { } } - fn get_auth_url(&mut self) -> Option { + fn get_auth_url(&mut self, csrf_token: String, nonce: String) -> Option { let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); let r = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { let (auth_url, csrf_token, nonce) = c .authorize_url( AuthenticationFlow::::AuthorizationCode, - csrf_func("my-csrf".to_string()), - nonce_func("my-nonce".to_string()), + csrf_func(csrf_token), + nonce_func(nonce), ) .add_scope(Scope::new("read".to_string())) .add_scope(Scope::new("read".to_string())) @@ -137,6 +138,7 @@ impl ZeroIDC { url: auth_url, csrf_token, nonce, + pkce_verifier, }; }); From 1c7a5439d59eef630cc7a0d20603b51315b72c1d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 29 Oct 2021 16:04:19 -0700 Subject: [PATCH 19/68] expose AuthInfo struct to external code also get the auth URL --- zeroidc/src/lib.rs | 64 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index ccc376652..fe27274dd 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -13,7 +13,7 @@ use openidconnect::{ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, Re use url::Url; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::os::raw::c_char; pub struct ZeroIDC { @@ -35,7 +35,7 @@ fn nonce_func(nonce: String) -> Box Nonce> { return Box::new(move || Nonce::new(nonce.to_string())); } -struct authres { +pub struct AuthInfo { url: Url, csrf_token: CsrfToken, nonce: Nonce, @@ -118,7 +118,7 @@ impl ZeroIDC { } } - fn get_auth_url(&mut self, csrf_token: String, nonce: String) -> Option { + fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option { let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); let r = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { @@ -134,7 +134,7 @@ impl ZeroIDC { .set_pkce_challenge(pkce_challenge) .url(); - return authres { + return AuthInfo { url: auth_url, csrf_token, nonce, @@ -214,3 +214,59 @@ pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { }; idc.stop(); } + +#[no_mangle] +pub extern "C" fn zeroidc_get_auth_info( + ptr: *mut ZeroIDC, + csrf_token: *const c_char, + nonce: *const c_char, +) -> *mut AuthInfo { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + + if csrf_token.is_null() { + println!("csrf_token is null"); + return std::ptr::null_mut(); + } + + if nonce.is_null() { + println!("nonce is null"); + return std::ptr::null_mut(); + } + + let csrf_token = unsafe { CStr::from_ptr(csrf_token) } + .to_str() + .unwrap() + .to_string(); + let nonce = unsafe { CStr::from_ptr(nonce) } + .to_str() + .unwrap() + .to_string(); + + match idc.get_auth_info(csrf_token, nonce) { + Some(a) => Box::into_raw(Box::new(a)), + None => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) { + if ptr.is_null() { + return; + } + unsafe { + Box::from_raw(ptr); + } +} + +#[no_mangle] +pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { + let ai = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + let s = CString::new(ai.url.to_string()).unwrap(); + return s.as_ptr(); +} From 4cadfd736ff3b1dcc85b57dbd63aff8f0c7b3d0a Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 2 Nov 2021 15:55:26 -0700 Subject: [PATCH 20/68] move ffi code into its own file --- zeroidc/src/ext.rs | 132 +++++++++++++++++++++++++++++++++++++++++++++ zeroidc/src/lib.rs | 130 +------------------------------------------- 2 files changed, 134 insertions(+), 128 deletions(-) create mode 100644 zeroidc/src/ext.rs diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs new file mode 100644 index 000000000..f76c4ef60 --- /dev/null +++ b/zeroidc/src/ext.rs @@ -0,0 +1,132 @@ +mod ext { + + use std::ffi::{CStr, CString}; + use std::os::raw::c_char; + + use crate::{AuthInfo, ZeroIDC}; + + #[no_mangle] + pub extern "C" fn zeroidc_new( + issuer: *const c_char, + client_id: *const c_char, + auth_endpoint: *const c_char, + web_listen_port: u16, + ) -> *mut ZeroIDC { + if issuer.is_null() { + println!("issuer is null"); + return std::ptr::null_mut(); + } + + if client_id.is_null() { + println!("client_id is null"); + return std::ptr::null_mut(); + } + + if auth_endpoint.is_null() { + println!("auth_endpoint is null"); + return std::ptr::null_mut(); + } + + let iss = unsafe { CStr::from_ptr(issuer) }; + let c_id = unsafe { CStr::from_ptr(client_id) }; + let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) }; + match ZeroIDC::new( + iss.to_str().unwrap(), + c_id.to_str().unwrap(), + auth_endpoint.to_str().unwrap(), + web_listen_port, + ) { + Ok(idc) => { + return Box::into_raw(Box::new(idc)); + } + Err(s) => { + println!("Error creating ZeroIDC instance: {}", s); + return std::ptr::null_mut(); + } + } + } + + #[no_mangle] + pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) { + if ptr.is_null() { + return; + } + unsafe { + Box::from_raw(ptr); + } + } + + #[no_mangle] + pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + idc.start(); + } + + #[no_mangle] + pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + idc.stop(); + } + + #[no_mangle] + pub extern "C" fn zeroidc_get_auth_info( + ptr: *mut ZeroIDC, + csrf_token: *const c_char, + nonce: *const c_char, + ) -> *mut AuthInfo { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + + if csrf_token.is_null() { + println!("csrf_token is null"); + return std::ptr::null_mut(); + } + + if nonce.is_null() { + println!("nonce is null"); + return std::ptr::null_mut(); + } + + let csrf_token = unsafe { CStr::from_ptr(csrf_token) } + .to_str() + .unwrap() + .to_string(); + let nonce = unsafe { CStr::from_ptr(nonce) } + .to_str() + .unwrap() + .to_string(); + + match idc.get_auth_info(csrf_token, nonce) { + Some(a) => Box::into_raw(Box::new(a)), + None => std::ptr::null_mut(), + } + } + + #[no_mangle] + pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) { + if ptr.is_null() { + return; + } + unsafe { + Box::from_raw(ptr); + } + } + + #[no_mangle] + pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { + let ai = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + let s = CString::new(ai.url.to_string()).unwrap(); + return s.as_ptr(); + } +} diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index fe27274dd..7de1f4abe 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -1,3 +1,5 @@ +pub mod ext; + extern crate base64; extern crate openidconnect; extern crate url; @@ -13,9 +15,6 @@ use openidconnect::{ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, Re use url::Url; -use std::ffi::{CStr, CString}; -use std::os::raw::c_char; - pub struct ZeroIDC { inner: Arc>, } @@ -145,128 +144,3 @@ impl ZeroIDC { r } } - -#[no_mangle] -pub extern "C" fn zeroidc_new( - issuer: *const c_char, - client_id: *const c_char, - auth_endpoint: *const c_char, - web_listen_port: u16, -) -> *mut ZeroIDC { - if issuer.is_null() { - println!("issuer is null"); - return std::ptr::null_mut(); - } - - if client_id.is_null() { - println!("client_id is null"); - return std::ptr::null_mut(); - } - - if auth_endpoint.is_null() { - println!("auth_endpoint is null"); - return std::ptr::null_mut(); - } - - let iss = unsafe { CStr::from_ptr(issuer) }; - let c_id = unsafe { CStr::from_ptr(client_id) }; - let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) }; - match ZeroIDC::new( - iss.to_str().unwrap(), - c_id.to_str().unwrap(), - auth_endpoint.to_str().unwrap(), - web_listen_port, - ) { - Ok(idc) => { - return Box::into_raw(Box::new(idc)); - } - Err(s) => { - println!("Error creating ZeroIDC instance: {}", s); - return std::ptr::null_mut(); - } - } -} - -#[no_mangle] -pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) { - if ptr.is_null() { - return; - } - unsafe { - Box::from_raw(ptr); - } -} - -#[no_mangle] -pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) { - let idc = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - idc.start(); -} - -#[no_mangle] -pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { - let idc = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - idc.stop(); -} - -#[no_mangle] -pub extern "C" fn zeroidc_get_auth_info( - ptr: *mut ZeroIDC, - csrf_token: *const c_char, - nonce: *const c_char, -) -> *mut AuthInfo { - let idc = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - - if csrf_token.is_null() { - println!("csrf_token is null"); - return std::ptr::null_mut(); - } - - if nonce.is_null() { - println!("nonce is null"); - return std::ptr::null_mut(); - } - - let csrf_token = unsafe { CStr::from_ptr(csrf_token) } - .to_str() - .unwrap() - .to_string(); - let nonce = unsafe { CStr::from_ptr(nonce) } - .to_str() - .unwrap() - .to_string(); - - match idc.get_auth_info(csrf_token, nonce) { - Some(a) => Box::into_raw(Box::new(a)), - None => std::ptr::null_mut(), - } -} - -#[no_mangle] -pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) { - if ptr.is_null() { - return; - } - unsafe { - Box::from_raw(ptr); - } -} - -#[no_mangle] -pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { - let ai = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - let s = CString::new(ai.url.to_string()).unwrap(); - return s.as_ptr(); -} From f7934972ea040725a4e8c110f1c1e61363eb3351 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 2 Nov 2021 15:56:40 -0700 Subject: [PATCH 21/68] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a67ae875e..173920d18 100755 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,4 @@ attic/world/mkworld workspace/ workspace2/ zeroidc/target/ +tmp/ From f8bf91426bbfb2d1d245975f66ac634fb3e9d1a1 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 2 Nov 2021 16:30:09 -0700 Subject: [PATCH 22/68] . --- zeroidc/src/ext.rs | 247 ++++++++++++++++++++++----------------------- 1 file changed, 122 insertions(+), 125 deletions(-) diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index f76c4ef60..3e89f247f 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -1,132 +1,129 @@ -mod ext { +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; - use std::ffi::{CStr, CString}; - use std::os::raw::c_char; +use crate::{AuthInfo, ZeroIDC}; - use crate::{AuthInfo, ZeroIDC}; +#[no_mangle] +pub extern "C" fn zeroidc_new( + issuer: *const c_char, + client_id: *const c_char, + auth_endpoint: *const c_char, + web_listen_port: u16, +) -> *mut ZeroIDC { + if issuer.is_null() { + println!("issuer is null"); + return std::ptr::null_mut(); + } - #[no_mangle] - pub extern "C" fn zeroidc_new( - issuer: *const c_char, - client_id: *const c_char, - auth_endpoint: *const c_char, - web_listen_port: u16, - ) -> *mut ZeroIDC { - if issuer.is_null() { - println!("issuer is null"); + if client_id.is_null() { + println!("client_id is null"); + return std::ptr::null_mut(); + } + + if auth_endpoint.is_null() { + println!("auth_endpoint is null"); + return std::ptr::null_mut(); + } + + let iss = unsafe { CStr::from_ptr(issuer) }; + let c_id = unsafe { CStr::from_ptr(client_id) }; + let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) }; + match ZeroIDC::new( + iss.to_str().unwrap(), + c_id.to_str().unwrap(), + auth_endpoint.to_str().unwrap(), + web_listen_port, + ) { + Ok(idc) => { + return Box::into_raw(Box::new(idc)); + } + Err(s) => { + println!("Error creating ZeroIDC instance: {}", s); return std::ptr::null_mut(); } - - if client_id.is_null() { - println!("client_id is null"); - return std::ptr::null_mut(); - } - - if auth_endpoint.is_null() { - println!("auth_endpoint is null"); - return std::ptr::null_mut(); - } - - let iss = unsafe { CStr::from_ptr(issuer) }; - let c_id = unsafe { CStr::from_ptr(client_id) }; - let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) }; - match ZeroIDC::new( - iss.to_str().unwrap(), - c_id.to_str().unwrap(), - auth_endpoint.to_str().unwrap(), - web_listen_port, - ) { - Ok(idc) => { - return Box::into_raw(Box::new(idc)); - } - Err(s) => { - println!("Error creating ZeroIDC instance: {}", s); - return std::ptr::null_mut(); - } - } - } - - #[no_mangle] - pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) { - if ptr.is_null() { - return; - } - unsafe { - Box::from_raw(ptr); - } - } - - #[no_mangle] - pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) { - let idc = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - idc.start(); - } - - #[no_mangle] - pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { - let idc = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - idc.stop(); - } - - #[no_mangle] - pub extern "C" fn zeroidc_get_auth_info( - ptr: *mut ZeroIDC, - csrf_token: *const c_char, - nonce: *const c_char, - ) -> *mut AuthInfo { - let idc = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - - if csrf_token.is_null() { - println!("csrf_token is null"); - return std::ptr::null_mut(); - } - - if nonce.is_null() { - println!("nonce is null"); - return std::ptr::null_mut(); - } - - let csrf_token = unsafe { CStr::from_ptr(csrf_token) } - .to_str() - .unwrap() - .to_string(); - let nonce = unsafe { CStr::from_ptr(nonce) } - .to_str() - .unwrap() - .to_string(); - - match idc.get_auth_info(csrf_token, nonce) { - Some(a) => Box::into_raw(Box::new(a)), - None => std::ptr::null_mut(), - } - } - - #[no_mangle] - pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) { - if ptr.is_null() { - return; - } - unsafe { - Box::from_raw(ptr); - } - } - - #[no_mangle] - pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { - let ai = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; - let s = CString::new(ai.url.to_string()).unwrap(); - return s.as_ptr(); } } + +#[no_mangle] +pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) { + if ptr.is_null() { + return; + } + unsafe { + Box::from_raw(ptr); + } +} + +#[no_mangle] +pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + idc.start(); +} + +#[no_mangle] +pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + idc.stop(); +} + +#[no_mangle] +pub extern "C" fn zeroidc_get_auth_info( + ptr: *mut ZeroIDC, + csrf_token: *const c_char, + nonce: *const c_char, +) -> *mut AuthInfo { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + + if csrf_token.is_null() { + println!("csrf_token is null"); + return std::ptr::null_mut(); + } + + if nonce.is_null() { + println!("nonce is null"); + return std::ptr::null_mut(); + } + + let csrf_token = unsafe { CStr::from_ptr(csrf_token) } + .to_str() + .unwrap() + .to_string(); + let nonce = unsafe { CStr::from_ptr(nonce) } + .to_str() + .unwrap() + .to_string(); + + match idc.get_auth_info(csrf_token, nonce) { + Some(a) => Box::into_raw(Box::new(a)), + None => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) { + if ptr.is_null() { + return; + } + unsafe { + Box::from_raw(ptr); + } +} + +#[no_mangle] +pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { + let ai = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + let s = CString::new(ai.url.to_string()).unwrap(); + return s.as_ptr(); +} From 8d39c9a861c9a1ff4064648bceb2d556da151147 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 4 Nov 2021 15:40:08 -0700 Subject: [PATCH 23/68] plumbing full flow from controller -> client network --- controller/DB.hpp | 26 +++++++- controller/DBMirrorSet.cpp | 10 +-- controller/DBMirrorSet.hpp | 2 +- controller/EmbeddedNetworkController.cpp | 81 +++++++++++++++++++----- controller/PostgreSQL.cpp | 47 +++++++++----- controller/PostgreSQL.hpp | 2 +- include/ZeroTierOne.h | 27 ++++++++ make-mac.mk | 14 ++-- node/IncomingPacket.cpp | 59 +++++++++++++++-- node/Network.cpp | 47 +++++++++++++- node/Network.hpp | 17 ++++- node/NetworkConfig.cpp | 68 ++++++++++++++++---- node/NetworkConfig.hpp | 65 ++++++++++++++++++- zeroidc/.cargo/config.toml | 5 ++ 14 files changed, 400 insertions(+), 70 deletions(-) create mode 100644 zeroidc/.cargo/config.toml diff --git a/controller/DB.hpp b/controller/DB.hpp index 0a5d784c2..24f388b8f 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -40,6 +40,30 @@ namespace ZeroTier { +struct AuthInfo +{ +public: + AuthInfo() + : enabled(false) + , version(0) + , authenticationURL() + , authenticationExpiryTime(0) + , centralAuthURL() + , ssoNonce() + , ssoState() + , ssoClientID() + {} + + bool enabled; + uint64_t version; + std::string authenticationURL; + uint64_t authenticationExpiryTime; + std::string centralAuthURL; + std::string ssoNonce; + std::string ssoState; + std::string ssoClientID; +}; + /** * Base class with common infrastructure for all controller DB implementations */ @@ -108,7 +132,7 @@ public: virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0; virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) = 0; - virtual std::string getSSOAuthURL(const nlohmann::json &member, const std::string &redirectURL) { return ""; } + virtual AuthInfo getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL) { return AuthInfo(); } virtual void networkMemberSSOHasExpired(uint64_t nwid, int64_t ts); inline void addListener(DB::ChangeListener *const listener) diff --git a/controller/DBMirrorSet.cpp b/controller/DBMirrorSet.cpp index de7ebefe1..0e5a90f57 100644 --- a/controller/DBMirrorSet.cpp +++ b/controller/DBMirrorSet.cpp @@ -125,16 +125,16 @@ bool DBMirrorSet::get(const uint64_t networkId,nlohmann::json &network,std::vect return false; } -std::string DBMirrorSet::getSSOAuthURL(const nlohmann::json &member, const std::string &redirectURL) +AuthInfo DBMirrorSet::getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL) { std::lock_guard l(_dbs_l); for(auto d=_dbs.begin();d!=_dbs.end();++d) { - std::string url = (*d)->getSSOAuthURL(member, redirectURL); - if (!url.empty()) { - return url; + AuthInfo info = (*d)->getSSOAuthInfo(member, redirectURL); + if (info.enabled) { + return info; } } - return ""; + return AuthInfo(); } void DBMirrorSet::networkMemberSSOHasExpired(uint64_t nwid, int64_t ts) diff --git a/controller/DBMirrorSet.hpp b/controller/DBMirrorSet.hpp index 4ce962740..9a957ee38 100644 --- a/controller/DBMirrorSet.hpp +++ b/controller/DBMirrorSet.hpp @@ -51,7 +51,7 @@ public: virtual void onNetworkMemberUpdate(const void *db,uint64_t networkId,uint64_t memberId,const nlohmann::json &member); virtual void onNetworkMemberDeauthorize(const void *db,uint64_t networkId,uint64_t memberId); - std::string getSSOAuthURL(const nlohmann::json &member, const std::string &redirectURL); + AuthInfo getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL); void networkMemberSSOHasExpired(uint64_t nwid, int64_t ts); inline void addDB(const std::shared_ptr &db) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index ea70cb3ae..9ee65ddef 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1360,27 +1360,53 @@ void EmbeddedNetworkController::_request( // Otherwise no, we use standard auth logic. bool networkSSOEnabled = OSUtils::jsonBool(network["ssoEnabled"], false); bool memberSSOExempt = OSUtils::jsonBool(member["ssoExempt"], false); - std::string authenticationURL; - if (networkSSOEnabled && !memberSSOExempt) { - authenticationURL = _db.getSSOAuthURL(member, _ssoRedirectURL); + AuthInfo info; + if (networkSSOEnabled && ! memberSSOExempt) { + info = _db.getSSOAuthInfo(member, _ssoRedirectURL); + assert(info.enabled == networkSSOEnabled); + std::string memberId = member["id"]; //fprintf(stderr, "ssoEnabled && !ssoExempt %s-%s\n", nwids, memberId.c_str()); uint64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0); //fprintf(stderr, "authExpiryTime: %lld\n", authenticationExpiryTime); if (authenticationExpiryTime < now) { - if (!authenticationURL.empty()) { - _db.networkMemberSSOHasExpired(nwid, now); - onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); + if (info.version == 0) { + if (!info.authenticationURL.empty()) { + _db.networkMemberSSOHasExpired(nwid, now); + onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); - Dictionary<3072> authInfo; - authInfo.add("aU", authenticationURL.c_str()); - //fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str()); + Dictionary<4096> authInfo; + authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, 0ULL); + authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str()); + //fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str()); - DB::cleanMember(member); - _db.save(member,true); + DB::cleanMember(member); + _db.save(member,true); - _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); - return; + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); + return; + } + } else if (info.version == 1) { + if (!info.authenticationURL.empty()) { + _db.networkMemberSSOHasExpired(nwid, now); + onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); + + Dictionary<8192> authInfo; + authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, info.version); + authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, info.centralAuthURL.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str()); + + DB::cleanMember(member); + _db.save(member, true); + + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); + return; + } + } else { + fprintf(stderr, "invalid sso info.version %llu\n", info.version); } } else if (authorized) { _db.memberWillExpire(authenticationExpiryTime, nwid, identity.address().toInt()); @@ -1452,9 +1478,32 @@ void EmbeddedNetworkController::_request( nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL); nc->ssoEnabled = OSUtils::jsonBool(network["ssoEnabled"], false); - nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL); - if (!authenticationURL.empty()) - Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), authenticationURL.c_str()); + nc->ssoVersion = info.version; + + if (info.version == 0) { + nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL); + if (!info.authenticationURL.empty()) { + Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), info.authenticationURL.c_str()); + } + } + else if (info.version == 1) { + nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL); + if (!info.authenticationURL.empty()) { + Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), info.authenticationURL.c_str()); + } + if (!info.centralAuthURL.empty()) { + Utils::scopy(nc->centralAuthURL, sizeof(nc->centralAuthURL), info.centralAuthURL.c_str()); + } + if (!info.ssoNonce.empty()) { + Utils::scopy(nc->ssoNonce, sizeof(nc->ssoNonce), info.ssoNonce.c_str()); + } + if (!info.ssoState.empty()) { + Utils::scopy(nc->ssoState, sizeof(nc->ssoState), info.ssoState.c_str()); + } + if (!info.ssoClientID.empty()) { + Utils::scopy(nc->ssoClientID, sizeof(nc->ssoClientID), info.ssoClientID.c_str()); + } + } std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],"")); if (rtt.length() == 10) { diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 9edcf0594..d94602e51 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -336,7 +336,7 @@ void PostgreSQL::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, } } -std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::string &redirectURL) +AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL) { // NONCE is just a random character string. no semantic meaning // state = HMAC SHA384 of Nonce based on shared sso key @@ -347,10 +347,12 @@ std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::s // how do we tell when a nonce is used? if auth_expiration_time is set std::string networkId = member["nwid"]; std::string memberId = member["id"]; - char authenticationURL[4096] = {0}; - //fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str()); - bool have_auth = false; + + char authenticationURL[4096] = {0}; + AuthInfo info; + info.enabled = true; + // fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str()); try { auto c = _pool->borrow(); pqxx::work w(*c->c); @@ -390,38 +392,51 @@ std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::s exit(6); } - r = w.exec_params("SELECT org.client_id, org.authorization_endpoint " + r = w.exec_params("SELECT org.client_id, org.authorization_endpoint, org.sso_version " "FROM ztc_network AS nw, ztc_org AS org " "WHERE nw.id = $1 AND nw.sso_enabled = true AND org.owner_id = nw.owner_id", networkId); std::string client_id = ""; std::string authorization_endpoint = ""; + uint64_t sso_version = 0; if (r.size() == 1) { client_id = r.at(0)[0].as(); authorization_endpoint = r.at(0)[1].as(); + sso_version = r.at(0)[2].as(); } else if (r.size() > 1) { fprintf(stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n", networkId.c_str()); } else { fprintf(stderr, "No client or auth endpoint?!?\n"); } - + + info.version = sso_version; + // no catch all else because we don't actually care if no records exist here. just continue as normal. if ((!client_id.empty())&&(!authorization_endpoint.empty())) { - have_auth = true; - + uint8_t state[48]; HMACSHA384(_ssoPsk, nonceBytes, sizeof(nonceBytes), state); char state_hex[256]; Utils::hex(state, 48, state_hex); - OSUtils::ztsnprintf(authenticationURL, sizeof(authenticationURL), - "%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s", - authorization_endpoint.c_str(), - redirectURL.c_str(), - nonce.c_str(), - state_hex, - client_id.c_str()); + if (info.version == 0) { + char url[2048] = {0}; + OSUtils::ztsnprintf(url, sizeof(authenticationURL), + "%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s", + authorization_endpoint.c_str(), + redirectURL.c_str(), + nonce.c_str(), + state_hex, + client_id.c_str()); + info.authenticationURL = std::string(url); + } else if (info.version == 1) { + info.ssoClientID = client_id; + info.authenticationURL = authorization_endpoint; + info.ssoNonce = nonce; + info.ssoState = std::string(state_hex); + info.centralAuthURL = redirectURL; + } } else { fprintf(stderr, "client_id: %s\nauthorization_endpoint: %s\n", client_id.c_str(), authorization_endpoint.c_str()); } @@ -432,7 +447,7 @@ std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::s fprintf(stderr, "ERROR: Error updating member on load: %s\n", e.what()); } - return std::string(authenticationURL); + return info; //std::string(authenticationURL); } void PostgreSQL::initializeNetworks() diff --git a/controller/PostgreSQL.hpp b/controller/PostgreSQL.hpp index 637b29749..acbc65a67 100644 --- a/controller/PostgreSQL.hpp +++ b/controller/PostgreSQL.hpp @@ -107,7 +107,7 @@ public: virtual void eraseNetwork(const uint64_t networkId); virtual void eraseMember(const uint64_t networkId, const uint64_t memberId); virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress &physicalAddress); - virtual std::string getSSOAuthURL(const nlohmann::json &member, const std::string &redirectURL); + virtual AuthInfo getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL); protected: struct _PairHasher diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 2bdea6474..063db4396 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1194,11 +1194,18 @@ typedef struct */ ZT_VirtualNetworkDNS dns; + + /** * sso enabled */ bool ssoEnabled; + /** + * SSO verison + */ + uint64_t ssoVersion; + /** * If the status us AUTHENTICATION_REQUIRED, this may contain a URL for authentication. */ @@ -1208,6 +1215,26 @@ typedef struct * Time that current authentication expires. only valid if ssoEnabled is true */ uint64_t authenticationExpiryTime; + + /** + * central base URL. + */ + char centralAuthURL[2048]; + + /** + * sso nonce + */ + char ssoNonce[64]; + + /** + * sso state + */ + char ssoState[128]; + + /** + * oidc client id + */ + char ssoClientID[256]; } ZT_VirtualNetworkConfig; /** diff --git a/make-mac.mk b/make-mac.mk index eeddba88b..5e889cddc 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -1,6 +1,8 @@ CC=clang CXX=clang++ -INCLUDES= +TOPDIR=$(shell PWD) + +INCLUDES=-I$(shell PWD)/zeroidc/target DEFS= LIBS= ARCH_FLAGS=-arch x86_64 -arch arm64 @@ -26,7 +28,7 @@ DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUIL include objects.mk ONE_OBJS+=osdep/MacEthernetTap.o osdep/MacKextEthernetTap.o osdep/MacDNSHelper.o ext/http-parser/http_parser.o -LIBS+=-framework CoreServices -framework SystemConfiguration -framework CoreFoundation +LIBS+=-framework CoreServices -framework SystemConfiguration -framework CoreFoundation -framework Security # Official releases are signed with our Apple cert and apply software updates by default ifeq ($(ZT_OFFICIAL_RELEASE),1) @@ -103,7 +105,7 @@ mac-agent: FORCE osdep/MacDNSHelper.o: osdep/MacDNSHelper.mm $(CXX) $(CXXFLAGS) -c osdep/MacDNSHelper.mm -o osdep/MacDNSHelper.o -one: $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent zeroidc +one: zeroidc $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent $(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS) zeroidc/target/libzeroidc.a # $(STRIP) zerotier-one ln -sf zerotier-one zerotier-idtool @@ -115,8 +117,8 @@ zerotier-one: one zeroidc: zeroidc/target/libzeroidc.a zeroidc/target/libzeroidc.a: - cd zeroidc && cargo build --target=x86_64-apple-darwin --release - cd zeroidc && cargo build --target=aarch64-apple-darwin --release + cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=x86_64-apple-darwin --release + cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=aarch64-apple-darwin --release cd zeroidc && lipo -create target/x86_64-apple-darwin/release/libzeroidc.a target/aarch64-apple-darwin/release/libzeroidc.a -output target/libzeroidc.a central-controller: @@ -126,6 +128,8 @@ zerotier-idtool: one zerotier-cli: one +$(CORE_OBJS): zeroidc + libzerotiercore.a: $(CORE_OBJS) ar rcs libzerotiercore.a $(CORE_OBJS) ranlib libzerotiercore.a diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5a2a94642..df2626e89 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -199,17 +199,62 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar const uint16_t errorDataSize = at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8); s -= 2; if (s >= (int)errorDataSize) { - Dictionary<3072> authInfo(((const char *)this->data()) + (ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 10), errorDataSize); - char authenticationURL[2048]; - if (authInfo.get("aU", authenticationURL, sizeof(authenticationURL)) > 0) { - authenticationURL[sizeof(authenticationURL) - 1] = 0; // ensure always zero terminated - network->setAuthenticationRequired(authenticationURL); - noUrl = false; + Dictionary<8192> authInfo(((const char *)this->data()) + (ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 10), errorDataSize); + + uint64_t authVer = authInfo.getUI(ZT_AUTHINFO_DICT_KEY_VERSION, 0ULL); + + if (authVer == 0) { + char authenticationURL[2048]; + + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) { + authenticationURL[sizeof(authenticationURL) - 1] = 0; // ensure always zero terminated + network->setAuthenticationRequired(authenticationURL); + noUrl = false; + } + } else if (authVer == 1) { + bool haveAuthURL = false; + char authenticationURL[2048] = { 0 }; + bool haveCentralURL = false; + char centralAuthURL[2048] = { 0 }; + bool haveNonce = false; + char ssoNonce[64] = { 0 }; + bool haveState = false; + char ssoState[128] = {0}; + bool haveClientID = false; + char ssoClientID[256] = { 0 }; + + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) { + authenticationURL[sizeof(authenticationURL) - 1] = 0; + haveAuthURL = true; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, centralAuthURL, sizeof(centralAuthURL))>0) { + centralAuthURL[sizeof(centralAuthURL) - 1] = 0; + haveCentralURL = true; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_NONCE, ssoNonce, sizeof(ssoNonce)) > 0) { + ssoNonce[sizeof(ssoNonce) - 1] = 0; + haveNonce = true; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_STATE, ssoState, sizeof(ssoState)) > 0) { + ssoState[sizeof(ssoState) - 1] = 0; + haveState = true; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, ssoClientID, sizeof(ssoClientID)) > 0) { + ssoClientID[sizeof(ssoClientID) - 1] = 0; + haveClientID = true; + } + + noUrl = ! (haveAuthURL && haveCentralURL && haveNonce && haveState && haveClientID); + + if (!noUrl) { + network->setAuthenticationRequired(authenticationURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); + } } } } - if (noUrl) + if (noUrl) { network->setAuthenticationRequired(""); + } } } break; diff --git a/node/Network.cpp b/node/Network.cpp index f3138f3ac..0d9261e3b 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -32,6 +32,7 @@ #include "Node.hpp" #include "Peer.hpp" #include "Trace.hpp" +#include "zeroidc.h" #include @@ -550,7 +551,8 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u _lastConfigUpdate(0), _destroyed(false), _netconfFailure(NETCONF_FAILURE_NONE), - _portError(0) + _portError(0), + _idc(nullptr) { for(int i=0;inode->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp); _portInitialized = true; } + + if (nconf->ssoEnabled) { + if (_config.ssoVersion == 1) { + // start sso handling for network + } + } } Network::~Network() @@ -598,6 +606,12 @@ Network::~Network() ZT_VirtualNetworkConfig ctmp; _externalConfig(&ctmp); + if (_idc) { + zeroidc::zeroidc_stop(_idc); + zeroidc::zeroidc_delete(_idc); + _idc = nullptr; + } + if (_destroyed) { // This is done in Node::leave() so we can pass tPtr properly //RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp); @@ -1434,8 +1448,13 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const memcpy(&ec->dns, &_config.dns, sizeof(ZT_VirtualNetworkDNS)); Utils::scopy(ec->authenticationURL, sizeof(ec->authenticationURL), _authenticationURL.c_str()); + ec->ssoVersion = _config.ssoVersion; ec->authenticationExpiryTime = _config.authenticationExpiryTime; ec->ssoEnabled = _config.ssoEnabled; + Utils::scopy(ec->centralAuthURL, sizeof(ec->centralAuthURL), _config.centralAuthURL); + Utils::scopy(ec->ssoNonce, sizeof(ec->ssoNonce), _config.ssoNonce); + Utils::scopy(ec->ssoState, sizeof(ec->ssoState), _config.ssoState); + Utils::scopy(ec->ssoClientID, sizeof(ec->ssoClientID), _config.ssoClientID); } void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup) @@ -1542,4 +1561,30 @@ Membership &Network::_membership(const Address &a) return _memberships[a]; } +void Network::setAuthenticationRequired(const char* authEndpoint, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state) +{ + Mutex::Lock _l(_lock); + _netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED; + _config.ssoEnabled = true; + _config.ssoVersion = 1; + + Utils::scopy(_config.authenticationURL, sizeof(_config.authenticationURL), authEndpoint); + Utils::scopy(_config.centralAuthURL, sizeof(_config.centralAuthURL), centralEndpoint); + Utils::scopy(_config.ssoClientID, sizeof(_config.ssoClientID), clientID); + Utils::scopy(_config.ssoNonce, sizeof(_config.ssoNonce), nonce); + Utils::scopy(_config.ssoState, sizeof(_config.ssoState), state); + + // TODO: propaagte to full flow module + if (!this->_idc) { + this->_idc = zeroidc::zeroidc_new(_config.authenticationURL, _config.ssoClientID, _config.centralAuthURL, 9993); + zeroidc::zeroidc_start(this->_idc); + } + + zeroidc::AuthInfo *info = zeroidc::zeroidc_get_auth_info(this->_idc, _config.ssoState, _config.ssoNonce); + const char* url = zeroidc::zeroidc_get_auth_url(info); + if (url != NULL) { + fprintf(stderr, "full auth URL: %s\n", url); + } +} + } // namespace ZeroTier diff --git a/node/Network.hpp b/node/Network.hpp index 33de16911..1aa64cf43 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -41,6 +41,10 @@ #define ZT_NETWORK_MAX_INCOMING_UPDATES 3 #define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1) +namespace zeroidc { +typedef struct ZeroIDC ZeroIDC; +} + namespace ZeroTier { class RuntimeEnvironment; @@ -229,7 +233,14 @@ public: _netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED; _authenticationURL = (url) ? url : ""; _config.ssoEnabled = true; - } + _config.ssoVersion = 0; + } + + /** + * set netconf failure to 'authentication required' along with info needed + * for sso full flow authentication. + */ + void setAuthenticationRequired(const char* authEndpoint, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state); /** * Causes this network to request an updated configuration from its master node now @@ -457,8 +468,10 @@ private: Mutex _lock; AtomicCounter __refCount; + + zeroidc::ZeroIDC *_idc; }; -} // namespace ZeroTier +} // namespace ZeroTier #endif diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 6d148cc45..ca1cf5d12 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -182,12 +182,24 @@ bool NetworkConfig::toDictionary(Dictionary &d,b if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_DNS,*tmp)) return false; } - if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false; - if (this->ssoEnabled) { - if (this->authenticationURL[0]) { - if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false; + if (this->ssoVersion == 0) { + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false; + + if (this->ssoEnabled) { + if (this->authenticationURL[0]) { + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false; + } + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, this->authenticationExpiryTime)) return false; } - if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, this->authenticationExpiryTime)) return false; + } else if(this->ssoVersion == 1) { + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID)) return false; } delete tmp; @@ -374,18 +386,48 @@ bool NetworkConfig::fromDictionary(const DictionaryssoVersion = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, 0ULL); this->ssoEnabled = d.getB(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, false); - if (this->ssoEnabled) { - if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) { - this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0; // ensure null terminated + + if (this->ssoVersion == 0) { + // implicit flow + if (this->ssoEnabled) { + if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) { + this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0; // ensure null terminated + } else { + this->authenticationURL[0] = 0; + } + this->authenticationExpiryTime = d.getI(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, 0); } else { this->authenticationURL[0] = 0; + this->authenticationExpiryTime = 0; + } + } else if (this->ssoVersion == 1) { + // full flow + if (this->ssoEnabled) { + if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) { + this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0; + } + if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL, (unsigned int)sizeof(this->centralAuthURL)) > 0) { + this->centralAuthURL[sizeof(this->centralAuthURL) - 1] = 0; + } + if (d.get(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce, (unsigned int)sizeof(this->ssoNonce)) > 0) { + this->ssoNonce[sizeof(this->ssoNonce) - 1] = 0; + } + if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState, (unsigned int)sizeof(this->ssoState)) > 0) { + this->ssoState[sizeof(this->ssoState) - 1] = 0; + } + if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID, (unsigned int)sizeof(this->ssoClientID)) > 0) { + this->ssoClientID[sizeof(this->ssoClientID) - 1] = 0; + } + } else { + this->authenticationURL[0] = 0; + this->authenticationExpiryTime = 0; + this->centralAuthURL[0] = 0; + this->ssoNonce[0] = 0; + this->ssoState[0] = 0; + this->ssoClientID[0] = 0; } - this->authenticationExpiryTime = d.getI(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, 0); - } else { - this->authenticationURL[0] = 0; - this->authenticationExpiryTime = 0; } } diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 301852adf..f546eadec 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -180,10 +180,35 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_DICT_KEY_DNS "DNS" // sso enabld #define ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED "ssoe" +// so version +#define ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION "ssov" // authentication URL #define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL "aurl" // authentication expiry #define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME "aexpt" +// central endpoint +#define ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL "ssoce" +// nonce +#define ZT_NETWORKCONFIG_DICT_KEY_NONCE "sson" +// state +#define ZT_NETWORKCONFIG_DICT_KEY_STATE "ssos" +// client ID +#define ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID "ssocid" + +// AuthInfo fields -- used by ncSendError for sso + +// AuthInfo Version +#define ZT_AUTHINFO_DICT_KEY_VERSION "aV" +// authenticaiton URL +#define ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL "aU" +// Central endpoint URL +#define ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL "aCU" +// Nonce +#define ZT_AUTHINFO_DICT_KEY_NONCE "aN" +// State +#define ZT_AUTHINFO_DICT_KEY_STATE "aS" +// Client ID +#define ZT_AUTHINFO_DICT_KEY_CLIENT_ID "aCID" // Legacy fields -- these are obsoleted but are included when older clients query @@ -242,7 +267,11 @@ public: dnsCount(0), ssoEnabled(false), authenticationURL(), - authenticationExpiryTime(0) + authenticationExpiryTime(0), + centralAuthURL(), + ssoNonce(), + ssoState(), + ssoClientID() { name[0] = 0; memset(specialists, 0, sizeof(uint64_t)*ZT_MAX_NETWORK_SPECIALISTS); @@ -250,6 +279,11 @@ public: memset(staticIps, 0, sizeof(InetAddress)*ZT_MAX_ZT_ASSIGNED_ADDRESSES); memset(rules, 0, sizeof(ZT_VirtualNetworkRule)*ZT_MAX_NETWORK_RULES); memset(&dns, 0, sizeof(ZT_VirtualNetworkDNS)); + memset(authenticationURL, 0, sizeof(authenticationURL)); + memset(centralAuthURL, 0, sizeof(centralAuthURL)); + memset(ssoNonce, 0, sizeof(ssoNonce)); + memset(ssoState, 0, sizeof(ssoState)); + memset(ssoClientID, 0, sizeof(ssoClientID)); } /** @@ -619,15 +653,42 @@ public: */ bool ssoEnabled; + /** + * SSO verison + */ + uint64_t ssoVersion; + /** * Authentication URL if authentication is required */ char authenticationURL[2048]; - /** +/** * Time current authentication expires or 0 if external authentication is disabled + * + * Not used if authVersion >= 1 */ uint64_t authenticationExpiryTime; + + /** + * central base URL. + */ + char centralAuthURL[2048]; + + /** + * sso nonce + */ + char ssoNonce[64]; + + /** + * sso state + */ + char ssoState[128]; + + /** + * oidc client id + */ + char ssoClientID[256]; }; } // namespace ZeroTier diff --git a/zeroidc/.cargo/config.toml b/zeroidc/.cargo/config.toml new file mode 100644 index 000000000..b5447409e --- /dev/null +++ b/zeroidc/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.x86_64-apple-darwin] +rustflags=["-C", "link-arg=-mmacosx-version-min=10.13"] + +[target.aarch64-apple-darwin] +rustflags=["-C", "link-arg=-mmacosx-version-min=10.13"] From 43433cdb5a3b376ab0ac82a562fc33cdc146d88f Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 4 Nov 2021 17:16:23 -0700 Subject: [PATCH 24/68] integrate rust build of zeroidc to linux --- controller/EmbeddedNetworkController.cpp | 2 +- make-linux.mk | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 9ee65ddef..6351fb4a8 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1376,7 +1376,7 @@ void EmbeddedNetworkController::_request( onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); Dictionary<4096> authInfo; - authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, 0ULL); + authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, (uint64_t)0ULL); authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str()); //fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str()); diff --git a/make-linux.mk b/make-linux.mk index 324344b34..645aa0fd5 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -9,9 +9,9 @@ ifeq ($(origin CXX),default) CXX:=$(shell if [ -e /opt/rh/devtoolset-8/root/usr/bin/g++ ]; then echo /opt/rh/devtoolset-8/root/usr/bin/g++; else echo $(CXX); fi) endif -INCLUDES?= +INCLUDES?=-Izeroidc/target DEFS?= -LDLIBS?= +LDLIBS?=zeroidc/target/release/libzeroidc.a -ldl DESTDIR?= include objects.mk @@ -311,6 +311,8 @@ zerotier-idtool: zerotier-one zerotier-cli: zerotier-one ln -sf zerotier-one zerotier-cli +$(CORE_OBJS): zeroidc + libzerotiercore.a: FORCE make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++11 -fstack-protector -fPIC" $(CORE_OBJS) ar rcs libzerotiercore.a $(CORE_OBJS) @@ -329,7 +331,7 @@ manpages: FORCE doc: manpages clean: FORCE - rm -rf *.a *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one doc/node_modules ext/misc/*.o debian/.debhelper debian/debhelper-build-stamp docker/zerotier-one + rm -rf *.a *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one doc/node_modules ext/misc/*.o debian/.debhelper debian/debhelper-build-stamp docker/zerotier-one zeroidc/target distclean: clean @@ -351,6 +353,9 @@ debug: FORCE make ZT_DEBUG=1 one make ZT_DEBUG=1 selftest +zeroidc: FORCE + cd zeroidc && cargo build --release + # Note: keep the symlinks in /var/lib/zerotier-one to the binaries since these # provide backward compatibility with old releases where the binaries actually # lived here. Folks got scripts. From 4d021e16a59c6c4de1a07d5ff5d4a20b9f604467 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 8 Nov 2021 09:29:31 -0800 Subject: [PATCH 25/68] update field lengths --- include/ZeroTierOne.h | 4 ++-- node/NetworkConfig.hpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 063db4396..0a8ec85fd 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1224,12 +1224,12 @@ typedef struct /** * sso nonce */ - char ssoNonce[64]; + char ssoNonce[128]; /** * sso state */ - char ssoState[128]; + char ssoState[256]; /** * oidc client id diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index f546eadec..8b18e150d 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -663,7 +663,7 @@ public: */ char authenticationURL[2048]; -/** + /** * Time current authentication expires or 0 if external authentication is disabled * * Not used if authVersion >= 1 @@ -678,12 +678,12 @@ public: /** * sso nonce */ - char ssoNonce[64]; + char ssoNonce[128]; /** * sso state */ - char ssoState[128]; + char ssoState[256]; /** * oidc client id From f268237372d6b990248cecfcf25215d0d4bea7dd Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 8 Nov 2021 09:32:15 -0800 Subject: [PATCH 26/68] add FORCE tag to cargo step --- make-mac.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make-mac.mk b/make-mac.mk index 5e889cddc..0e4657254 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -116,7 +116,7 @@ zerotier-one: one zeroidc: zeroidc/target/libzeroidc.a -zeroidc/target/libzeroidc.a: +zeroidc/target/libzeroidc.a: FORCE cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=x86_64-apple-darwin --release cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=aarch64-apple-darwin --release cd zeroidc && lipo -create target/x86_64-apple-darwin/release/libzeroidc.a target/aarch64-apple-darwin/release/libzeroidc.a -output target/libzeroidc.a From fa21fdc1cc45623e7eaf1696e3eb0a268a2dea7e Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 11 Nov 2021 16:19:26 -0800 Subject: [PATCH 27/68] rename stuff for clarity authenticationURL will still be used by the client for v1 and v2 of sso --- controller/DB.hpp | 2 ++ controller/EmbeddedNetworkController.cpp | 2 +- controller/PostgreSQL.cpp | 2 +- include/ZeroTierOne.h | 5 +++++ node/IncomingPacket.cpp | 14 +++++++------- node/Network.cpp | 4 ++-- node/Network.hpp | 2 +- node/NetworkConfig.cpp | 7 ++++++- node/NetworkConfig.hpp | 11 +++++++++++ 9 files changed, 36 insertions(+), 13 deletions(-) diff --git a/controller/DB.hpp b/controller/DB.hpp index 24f388b8f..d0641d72e 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -48,6 +48,7 @@ public: , version(0) , authenticationURL() , authenticationExpiryTime(0) + , issuerURL() , centralAuthURL() , ssoNonce() , ssoState() @@ -58,6 +59,7 @@ public: uint64_t version; std::string authenticationURL; uint64_t authenticationExpiryTime; + std::string issuerURL; std::string centralAuthURL; std::string ssoNonce; std::string ssoState; diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 6351fb4a8..4ce48fa26 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1393,7 +1393,7 @@ void EmbeddedNetworkController::_request( Dictionary<8192> authInfo; authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, info.version); - authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, info.issuerURL.c_str()); authInfo.add(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, info.centralAuthURL.c_str()); authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str()); authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str()); diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index d94602e51..f79c8725d 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -432,7 +432,7 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str info.authenticationURL = std::string(url); } else if (info.version == 1) { info.ssoClientID = client_id; - info.authenticationURL = authorization_endpoint; + info.issuerURL = authorization_endpoint; info.ssoNonce = nonce; info.ssoState = std::string(state_hex); info.centralAuthURL = redirectURL; diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 0a8ec85fd..6d61c6ea8 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1216,6 +1216,11 @@ typedef struct */ uint64_t authenticationExpiryTime; + /** + * OIDC issuer URL. + */ + char issuerURL[2048]; + /** * central base URL. */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index df2626e89..15003b4eb 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -212,8 +212,8 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar noUrl = false; } } else if (authVer == 1) { - bool haveAuthURL = false; - char authenticationURL[2048] = { 0 }; + bool haveIssuerURL = false; + char issuerURL[2048] = { 0 }; bool haveCentralURL = false; char centralAuthURL[2048] = { 0 }; bool haveNonce = false; @@ -223,9 +223,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar bool haveClientID = false; char ssoClientID[256] = { 0 }; - if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) { - authenticationURL[sizeof(authenticationURL) - 1] = 0; - haveAuthURL = true; + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, issuerURL, sizeof(issuerURL)) > 0) { + issuerURL[sizeof(issuerURL) - 1] = 0; + haveIssuerURL = true; } if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, centralAuthURL, sizeof(centralAuthURL))>0) { centralAuthURL[sizeof(centralAuthURL) - 1] = 0; @@ -244,10 +244,10 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar haveClientID = true; } - noUrl = ! (haveAuthURL && haveCentralURL && haveNonce && haveState && haveClientID); + noUrl = ! (haveIssuerURL && haveCentralURL && haveNonce && haveState && haveClientID); if (!noUrl) { - network->setAuthenticationRequired(authenticationURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); + network->setAuthenticationRequired(issuerURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); } } } diff --git a/node/Network.cpp b/node/Network.cpp index 0d9261e3b..b50337794 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1561,14 +1561,14 @@ Membership &Network::_membership(const Address &a) return _memberships[a]; } -void Network::setAuthenticationRequired(const char* authEndpoint, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state) +void Network::setAuthenticationRequired(const char* issuerURL, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state) { Mutex::Lock _l(_lock); _netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED; _config.ssoEnabled = true; _config.ssoVersion = 1; - Utils::scopy(_config.authenticationURL, sizeof(_config.authenticationURL), authEndpoint); + Utils::scopy(_config.issuerURL, sizeof(_config.issuerURL), issuerURL); Utils::scopy(_config.centralAuthURL, sizeof(_config.centralAuthURL), centralEndpoint); Utils::scopy(_config.ssoClientID, sizeof(_config.ssoClientID), clientID); Utils::scopy(_config.ssoNonce, sizeof(_config.ssoNonce), nonce); diff --git a/node/Network.hpp b/node/Network.hpp index 1aa64cf43..680b54473 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -240,7 +240,7 @@ public: * set netconf failure to 'authentication required' along with info needed * for sso full flow authentication. */ - void setAuthenticationRequired(const char* authEndpoint, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state); + void setAuthenticationRequired(const char* issuerURL, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state); /** * Causes this network to request an updated configuration from its master node now diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index ca1cf5d12..2b76b6730 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -196,7 +196,9 @@ bool NetworkConfig::toDictionary(Dictionary &d,b if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false; - if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL)) return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL, this->issuerURL)) return false; + if (! d.add(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL)) + return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID)) return false; @@ -408,6 +410,9 @@ bool NetworkConfig::fromDictionary(const DictionaryauthenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) { this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0; } + if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL, this->issuerURL, (unsigned int)sizeof(this->issuerURL)) > 0) { + this->issuerURL[sizeof(this->issuerURL) - 1] = 0; + } if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL, (unsigned int)sizeof(this->centralAuthURL)) > 0) { this->centralAuthURL[sizeof(this->centralAuthURL) - 1] = 0; } diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 8b18e150d..8c08838c5 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -186,6 +186,8 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL "aurl" // authentication expiry #define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME "aexpt" +// oidc issuer URL +#define ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL "iurl" // central endpoint #define ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL "ssoce" // nonce @@ -201,6 +203,8 @@ namespace ZeroTier { #define ZT_AUTHINFO_DICT_KEY_VERSION "aV" // authenticaiton URL #define ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL "aU" +// issuer URL +#define ZT_AUTHINFO_DICT_KEY_ISSUER_URL "iU" // Central endpoint URL #define ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL "aCU" // Nonce @@ -268,6 +272,7 @@ public: ssoEnabled(false), authenticationURL(), authenticationExpiryTime(0), + issuerURL(), centralAuthURL(), ssoNonce(), ssoState(), @@ -280,6 +285,7 @@ public: memset(rules, 0, sizeof(ZT_VirtualNetworkRule)*ZT_MAX_NETWORK_RULES); memset(&dns, 0, sizeof(ZT_VirtualNetworkDNS)); memset(authenticationURL, 0, sizeof(authenticationURL)); + memset(issuerURL, 0, sizeof(issuerURL)); memset(centralAuthURL, 0, sizeof(centralAuthURL)); memset(ssoNonce, 0, sizeof(ssoNonce)); memset(ssoState, 0, sizeof(ssoState)); @@ -670,6 +676,11 @@ public: */ uint64_t authenticationExpiryTime; + /** + * OIDC issuer URL + */ + char issuerURL[2048]; + /** * central base URL. */ From 0875fb5fe024a5773abab470119646d06fd1fcb0 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 11 Nov 2021 16:29:14 -0800 Subject: [PATCH 28/68] have this make an rlib too for the future --- zeroidc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 814a497c5..ba0f134eb 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -5,7 +5,7 @@ edition = "2018" build = "build.rs" [lib] -crate-type = ["staticlib"] +crate-type = ["staticlib","rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 91e9b736dd5513a1326325db4d57125cbf9e1647 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 18 Nov 2021 10:32:25 -0800 Subject: [PATCH 29/68] make service objs dependent on zeroidc --- make-mac.mk | 2 +- node/Network.cpp | 28 +--------------------------- node/Network.hpp | 6 ------ zeroidc/Cargo.toml | 1 + 4 files changed, 3 insertions(+), 34 deletions(-) diff --git a/make-mac.mk b/make-mac.mk index 0e4657254..784c167e7 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -128,7 +128,7 @@ zerotier-idtool: one zerotier-cli: one -$(CORE_OBJS): zeroidc +$(ONE_OBJS): zeroidc libzerotiercore.a: $(CORE_OBJS) ar rcs libzerotiercore.a $(CORE_OBJS) diff --git a/node/Network.cpp b/node/Network.cpp index b50337794..357cf1432 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -32,7 +32,6 @@ #include "Node.hpp" #include "Peer.hpp" #include "Trace.hpp" -#include "zeroidc.h" #include @@ -551,8 +550,7 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u _lastConfigUpdate(0), _destroyed(false), _netconfFailure(NETCONF_FAILURE_NONE), - _portError(0), - _idc(nullptr) + _portError(0) { for(int i=0;inode->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp); _portInitialized = true; } - - if (nconf->ssoEnabled) { - if (_config.ssoVersion == 1) { - // start sso handling for network - } - } } Network::~Network() @@ -606,12 +598,6 @@ Network::~Network() ZT_VirtualNetworkConfig ctmp; _externalConfig(&ctmp); - if (_idc) { - zeroidc::zeroidc_stop(_idc); - zeroidc::zeroidc_delete(_idc); - _idc = nullptr; - } - if (_destroyed) { // This is done in Node::leave() so we can pass tPtr properly //RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp); @@ -1573,18 +1559,6 @@ void Network::setAuthenticationRequired(const char* issuerURL, const char* centr Utils::scopy(_config.ssoClientID, sizeof(_config.ssoClientID), clientID); Utils::scopy(_config.ssoNonce, sizeof(_config.ssoNonce), nonce); Utils::scopy(_config.ssoState, sizeof(_config.ssoState), state); - - // TODO: propaagte to full flow module - if (!this->_idc) { - this->_idc = zeroidc::zeroidc_new(_config.authenticationURL, _config.ssoClientID, _config.centralAuthURL, 9993); - zeroidc::zeroidc_start(this->_idc); - } - - zeroidc::AuthInfo *info = zeroidc::zeroidc_get_auth_info(this->_idc, _config.ssoState, _config.ssoNonce); - const char* url = zeroidc::zeroidc_get_auth_url(info); - if (url != NULL) { - fprintf(stderr, "full auth URL: %s\n", url); - } } } // namespace ZeroTier diff --git a/node/Network.hpp b/node/Network.hpp index 680b54473..10b48f1f9 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -41,10 +41,6 @@ #define ZT_NETWORK_MAX_INCOMING_UPDATES 3 #define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1) -namespace zeroidc { -typedef struct ZeroIDC ZeroIDC; -} - namespace ZeroTier { class RuntimeEnvironment; @@ -468,8 +464,6 @@ private: Mutex _lock; AtomicCounter __refCount; - - zeroidc::ZeroIDC *_idc; }; } // namespace ZeroTier diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index ba0f134eb..41ac73029 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -3,6 +3,7 @@ name = "zeroidc" version = "0.1.0" edition = "2018" build = "build.rs" +publish = false [lib] crate-type = ["staticlib","rlib"] From 2e356613ecbfbd1cd2dcb118490cee65e62261a5 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 18 Nov 2021 12:39:55 -0800 Subject: [PATCH 30/68] Refactor NetworkState into object with accessor methods --- service/OneService.cpp | 249 ++++++++++++++++++++++++++++------------- 1 file changed, 169 insertions(+), 80 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 255bad21e..309784979 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -53,6 +53,8 @@ #include "OneService.hpp" #include "SoftwareUpdater.hpp" +#include + #ifdef __WINDOWS__ #include #include @@ -526,31 +528,117 @@ public: volatile int64_t _nextBackgroundTaskDeadline; // Configured networks - struct NetworkState + class NetworkState { - NetworkState() : - tap((EthernetTap *)0) + public: + NetworkState() + : _tap((EthernetTap *)0) + , _idc(nullptr) { // Real defaults are in network 'up' code in network event handler - settings.allowManaged = true; - settings.allowGlobal = false; - settings.allowDefault = false; - settings.allowDNS = false; - memset(&config, 0, sizeof(ZT_VirtualNetworkConfig)); + _settings.allowManaged = true; + _settings.allowGlobal = false; + _settings.allowDefault = false; + _settings.allowDNS = false; + memset(&_config, 0, sizeof(ZT_VirtualNetworkConfig)); } ~NetworkState() { - this->managedRoutes.clear(); - this->tap.reset(); + this->_managedRoutes.clear(); + this->_tap.reset(); + + if (_idc) { + zeroidc::zeroidc_delete(_idc); + _idc = nullptr; + } } - std::shared_ptr tap; - ZT_VirtualNetworkConfig config; // memcpy() of raw config from core - std::vector managedIps; - std::map< InetAddress, SharedPtr > managedRoutes; - NetworkSettings settings; + void setTap(std::shared_ptr tap) { + this->_tap = tap; + } + + std::shared_ptr tap() const { + return _tap; + } + + NetworkSettings settings() const { + return _settings; + } + + void setSettings(const NetworkSettings &settings) { + _settings = settings; + } + + void setAllowManaged(bool allow) { + _settings.allowManaged = allow; + } + + bool allowManaged() const { + return _settings.allowManaged; + } + + void setAllowGlobal(bool allow) { + _settings.allowGlobal = allow; + } + + bool allowGlobal() const { + return _settings.allowGlobal; + } + + void setAllowDefault(bool allow) { + _settings.allowDefault = allow; + } + + bool allowDefault() const { + return _settings.allowDefault; + } + + void setAllowDNS(bool allow) { + _settings.allowDNS = allow; + } + + bool allowDNS() const { + return _settings.allowDNS; + } + + std::vector allowManagedWhitelist() const { + return _settings.allowManagedWhitelist; + } + + void addToAllowManagedWhiteList(const InetAddress& addr) { + _settings.allowManagedWhitelist.push_back(addr); + } + + const ZT_VirtualNetworkConfig& config() { + return _config; + } + + void setConfig(const ZT_VirtualNetworkConfig *nwc) { + memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); + } + + std::vector& managedIps() { + return _managedIps; + } + + void setManagedIps(const std::vector &managedIps) { + _managedIps = managedIps; + } + + std::map< InetAddress, SharedPtr >& managedRoutes() { + return _managedRoutes; + } + + private: + std::shared_ptr _tap; + ZT_VirtualNetworkConfig _config; // memcpy() of raw config from core + std::vector _managedIps; + std::map< InetAddress, SharedPtr > _managedRoutes; + NetworkSettings _settings; + zeroidc::ZeroIDC *_idc; }; + std::map _nets; Mutex _nets_m; @@ -888,7 +976,7 @@ public: { Mutex::Lock _l(_nets_m); for(std::map::iterator n(_nets.begin());n!=_nets.end();++n) { - if (n->second.tap) + if (n->second.tap()) syncManagedStuff(n->second,false,true,false); } } @@ -913,9 +1001,9 @@ public: Mutex::Lock _l(_nets_m); mgChanges.reserve(_nets.size() + 1); for(std::map::const_iterator n(_nets.begin());n!=_nets.end();++n) { - if (n->second.tap) { + if (n->second.tap()) { mgChanges.push_back(std::pair< uint64_t,std::pair< std::vector,std::vector > >(n->first,std::pair< std::vector,std::vector >())); - n->second.tap->scanMulticastGroups(mgChanges.back().second.first,mgChanges.back().second.second); + n->second.tap()->scanMulticastGroups(mgChanges.back().second.first,mgChanges.back().second.second); } } } @@ -1114,8 +1202,8 @@ public: { Mutex::Lock _l(_nets_m); std::map::const_iterator n(_nets.find(nwid)); - if ((n != _nets.end())&&(n->second.tap)) - return n->second.tap->deviceName(); + if ((n != _nets.end())&&(n->second.tap())) + return n->second.tap()->deviceName(); else return std::string(); } @@ -1157,7 +1245,7 @@ public: std::map::const_iterator n(_nets.find(nwid)); if (n == _nets.end()) return false; - settings = n->second.settings; + settings = n->second.settings(); return true; } @@ -1168,20 +1256,20 @@ public: std::map::iterator n(_nets.find(nwid)); if (n == _nets.end()) return false; - n->second.settings = settings; + n->second.setSettings(settings); char nlcpath[4096]; OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid); FILE *out = fopen(nlcpath,"w"); if (out) { - fprintf(out,"allowManaged=%d\n",(int)n->second.settings.allowManaged); - fprintf(out,"allowGlobal=%d\n",(int)n->second.settings.allowGlobal); - fprintf(out,"allowDefault=%d\n",(int)n->second.settings.allowDefault); - fprintf(out,"allowDNS=%d\n",(int)n->second.settings.allowDNS); + fprintf(out,"allowManaged=%d\n",(int)n->second.allowManaged()); + fprintf(out,"allowGlobal=%d\n",(int)n->second.allowGlobal()); + fprintf(out,"allowDefault=%d\n",(int)n->second.allowDefault()); + fprintf(out,"allowDNS=%d\n",(int)n->second.allowDNS()); fclose(out); } - if (n->second.tap) + if (n->second.tap()) syncManagedStuff(n->second,true,true,true); return true; @@ -1969,12 +2057,12 @@ public: // Checks if a managed IP or route target is allowed bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target) { - if (!n.settings.allowManaged) + if (!n.allowManaged()) return false; - if (!n.settings.allowManagedWhitelist.empty()) { + if (!n.allowManagedWhitelist().empty()) { bool allowed = false; - for (InetAddress addr : n.settings.allowManagedWhitelist) { + for (InetAddress addr : n.allowManagedWhitelist()) { if (addr.containsAddress(target) && addr.netmaskBits() <= target.netmaskBits()) { allowed = true; break; @@ -1984,7 +2072,7 @@ public: } if (target.isDefaultRoute()) - return n.settings.allowDefault; + return n.allowDefault(); switch(target.ipScope()) { case InetAddress::IP_SCOPE_NONE: case InetAddress::IP_SCOPE_MULTICAST: @@ -1992,7 +2080,7 @@ public: case InetAddress::IP_SCOPE_LINK_LOCAL: return false; case InetAddress::IP_SCOPE_GLOBAL: - return n.settings.allowGlobal; + return n.allowGlobal(); default: return true; } @@ -2016,18 +2104,18 @@ public: // assumes _nets_m is locked if (syncIps) { std::vector newManagedIps; - newManagedIps.reserve(n.config.assignedAddressCount); - for(unsigned int i=0;i(&(n.config.assignedAddresses[i])); + newManagedIps.reserve(n.config().assignedAddressCount); + for(unsigned int i=0;i(&(n.config().assignedAddresses[i])); if (checkIfManagedIsAllowed(n,*ii)) newManagedIps.push_back(*ii); } std::sort(newManagedIps.begin(),newManagedIps.end()); newManagedIps.erase(std::unique(newManagedIps.begin(),newManagedIps.end()),newManagedIps.end()); - for(std::vector::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) { + for(std::vector::iterator ip(n.managedIps().begin());ip!=n.managedIps().end();++ip) { if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) { - if (!n.tap->removeIp(*ip)) + if (!n.tap()->removeIp(*ip)) fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString(ipbuf)); } } @@ -2036,13 +2124,13 @@ public: fprintf(stderr,"ERROR: unable to add ip addresses to ifcfg" ZT_EOL_S); #else for(std::vector::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) { - if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) { - if (!n.tap->addIp(*ip)) + if (std::find(n.managedIps().begin(),n.managedIps().end(),*ip) == n.managedIps().end()) { + if (!n.tap()->addIp(*ip)) fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf)); } } #endif - n.managedIps.swap(newManagedIps); + n.setManagedIps(newManagedIps); } if (syncRoutes) { @@ -2052,18 +2140,18 @@ public: OSUtils::ztsnprintf(tapdevbuf,sizeof(tapdevbuf),"%.16llx",(unsigned long long)((WindowsEthernetTap *)(n.tap.get()))->luid().Value); std::string tapdev(tapdevbuf); #else - std::string tapdev(n.tap->deviceName()); + std::string tapdev(n.tap()->deviceName()); #endif - std::vector tapIps(n.tap->ips()); + std::vector tapIps(n.tap()->ips()); std::set myIps(tapIps.begin(), tapIps.end()); - for(unsigned int i=0;i haveRouteTargets; - for(unsigned int i=0;i(&(n.config.routes[i].target)); - const InetAddress *const via = reinterpret_cast(&(n.config.routes[i].via)); + for(unsigned int i=0;i(&(n.config().routes[i].target)); + const InetAddress *const via = reinterpret_cast(&(n.config().routes[i].via)); // Make sure we are allowed to set this managed route, and that 'via' is not our IP. The latter // avoids setting routes via the router on the router. @@ -2100,48 +2188,48 @@ public: haveRouteTargets.insert(*target); #ifndef ZT_SDK - SharedPtr &mr = n.managedRoutes[*target]; + SharedPtr &mr = n.managedRoutes()[*target]; if (!mr) mr.set(new ManagedRoute(*target, *via, *src, tapdev.c_str())); #endif } - for(std::map< InetAddress, SharedPtr >::iterator r(n.managedRoutes.begin());r!=n.managedRoutes.end();) { + for(std::map< InetAddress, SharedPtr >::iterator r(n.managedRoutes().begin());r!=n.managedRoutes().end();) { if (haveRouteTargets.find(r->first) == haveRouteTargets.end()) - n.managedRoutes.erase(r++); + n.managedRoutes().erase(r++); else ++r; } // Sync device-local managed routes first, then indirect results. That way // we don't get destination unreachable for routes that are via things // that do not yet have routes in the system. - for(std::map< InetAddress, SharedPtr >::iterator r(n.managedRoutes.begin());r!=n.managedRoutes.end();++r) { + for(std::map< InetAddress, SharedPtr >::iterator r(n.managedRoutes().begin());r!=n.managedRoutes().end();++r) { if (!r->second->via()) r->second->sync(); } - for(std::map< InetAddress, SharedPtr >::iterator r(n.managedRoutes.begin());r!=n.managedRoutes.end();++r) { + for(std::map< InetAddress, SharedPtr >::iterator r(n.managedRoutes().begin());r!=n.managedRoutes().end();++r) { if (r->second->via()) r->second->sync(); } } if (syncDns) { - if (n.settings.allowDNS) { - if (strlen(n.config.dns.domain) != 0) { + if (n.allowDNS()) { + if (strlen(n.config().dns.domain) != 0) { std::vector servers; for (int j = 0; j < ZT_MAX_DNS_SERVERS; ++j) { - InetAddress a(n.config.dns.server_addr[j]); + InetAddress a(n.config().dns.server_addr[j]); if (a.isV4() || a.isV6()) { servers.push_back(a); } } - n.tap->setDns(n.config.dns.domain, servers); + n.tap()->setDns(n.config().dns.domain, servers); } } else { #ifdef __APPLE__ - MacDNSHelper::removeDNS(n.config.nwid); + MacDNSHelper::removeDNS(n.config().nwid); #elif defined(__WINDOWS__) - WinDNSHelper::removeDNS(n.config.nwid); + WinDNSHelper::removeDNS(n.config().nwid); #endif } @@ -2414,12 +2502,12 @@ public: switch(op) { case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP: - if (!n.tap) { + if (!n.tap()) { try { char friendlyName[128]; OSUtils::ztsnprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid); - n.tap = EthernetTap::newInstance( + n.setTap(EthernetTap::newInstance( nullptr, _homePath.c_str(), MAC(nwc->mac), @@ -2428,7 +2516,7 @@ public: nwid, friendlyName, StapFrameHandler, - (void *)this); + (void *)this)); *nuptr = (void *)&n; char nlcpath[256]; @@ -2442,28 +2530,28 @@ public: std::string addresses (allowManaged.begin(), allowManaged.size()); if (allowManaged.size() <= 5) { // untidy parsing for backward compatibility if (allowManaged[0] == '1' || allowManaged[0] == 't' || allowManaged[0] == 'T') { - n.settings.allowManaged = true; + n.setAllowManaged(true); } else { - n.settings.allowManaged = false; + n.setAllowManaged(false); } } else { // this should be a list of IP addresses - n.settings.allowManaged = true; + n.setAllowManaged(true); size_t pos = 0; while (true) { size_t nextPos = addresses.find(',', pos); std::string address = addresses.substr(pos, (nextPos == std::string::npos ? addresses.size() : nextPos) - pos); - n.settings.allowManagedWhitelist.push_back(InetAddress(address.c_str())); + n.addToAllowManagedWhiteList(InetAddress(address.c_str())); if (nextPos == std::string::npos) break; pos = nextPos + 1; } } } else { - n.settings.allowManaged = true; + n.setAllowManaged(true); } - n.settings.allowGlobal = nc.getB("allowGlobal", false); - n.settings.allowDefault = nc.getB("allowDefault", false); - n.settings.allowDNS = nc.getB("allowDNS", false); + n.setAllowGlobal(nc.getB("allowGlobal", false)); + n.setAllowDefault(nc.getB("allowDefault", false)); + n.setAllowDNS(nc.getB("allowDNS", false)); } } catch (std::exception &exc) { #ifdef __WINDOWS__ @@ -2484,8 +2572,9 @@ public: // After setting up tap, fall through to CONFIG_UPDATE since we also want to do this... case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: - memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig)); - if (n.tap) { // sanity check + n.setConfig(nwc); + + if (n.tap()) { // sanity check #if defined(__WINDOWS__) && !defined(ZT_SDK) // wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized // @@ -2497,7 +2586,7 @@ public: } #endif syncManagedStuff(n,true,true,true); - n.tap->setMtu(nwc->mtu); + n.tap()->setMtu(nwc->mtu); } else { _nets.erase(nwid); return -999; // tap init failed @@ -2506,12 +2595,12 @@ public: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY: - if (n.tap) { // sanity check + if (n.tap()) { // sanity check #if defined(__WINDOWS__) && !defined(ZT_SDK) std::string winInstanceId(((WindowsEthernetTap *)(n.tap.get()))->instanceId()); #endif *nuptr = (void *)0; - n.tap.reset(); + n.tap().reset(); _nets.erase(nwid); #if defined(__WINDOWS__) && !defined(ZT_SDK) if ((op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0)) @@ -2926,9 +3015,9 @@ public: inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { NetworkState *n = reinterpret_cast(*nuptr); - if ((!n)||(!n->tap)) + if ((!n)||(!n->tap())) return; - n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len); + n->tap()->put(MAC(sourceMac),MAC(destMac),etherType,data,len); } inline int nodePathCheckFunction(uint64_t ztaddr,const int64_t localSocket,const struct sockaddr_storage *remoteAddr) @@ -2937,8 +3026,8 @@ public: { Mutex::Lock _l(_nets_m); for(std::map::const_iterator n(_nets.begin());n!=_nets.end();++n) { - if (n->second.tap) { - std::vector ips(n->second.tap->ips()); + if (n->second.tap()) { + std::vector ips(n->second.tap()->ips()); for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) { if (i->containsAddress(*(reinterpret_cast(remoteAddr)))) { return 0; @@ -3110,8 +3199,8 @@ public: { Mutex::Lock _l(_nets_m); for(std::map::const_iterator n(_nets.begin());n!=_nets.end();++n) { - if (n->second.tap) { - std::vector ips(n->second.tap->ips()); + if (n->second.tap()) { + std::vector ips(n->second.tap()->ips()); for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) { if (i->ipsEqual(ifaddr)) return false; From 87fdd644d464245be8864b5d91669f6b7f68216c Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 18 Nov 2021 13:36:09 -0800 Subject: [PATCH 31/68] cleanup some ifdef'd things --- service/OneService.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 309784979..5b2448363 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1217,10 +1217,10 @@ public: { Mutex::Lock _l(_nets_m); NetworkState &n = _nets[nwid]; - *numRoutes = *numRoutes < n.config.routeCount ? *numRoutes : n.config.routeCount; + *numRoutes = *numRoutes < n.config().routeCount ? *numRoutes : n.config().routeCount; for(unsigned int i=0; i<*numRoutes; i++) { ZT_VirtualNetworkRoute *vnr = (ZT_VirtualNetworkRoute*)routeArray; - memcpy(&vnr[i], &(n.config.routes[i]), sizeof(ZT_VirtualNetworkRoute)); + memcpy(&vnr[i], &(n.config().routes[i]), sizeof(ZT_VirtualNetworkRoute)); } } @@ -2137,7 +2137,7 @@ public: // Get tap device name (use LUID in hex on Windows) and IP addresses. #if defined(__WINDOWS__) && !defined(ZT_SDK) char tapdevbuf[64]; - OSUtils::ztsnprintf(tapdevbuf,sizeof(tapdevbuf),"%.16llx",(unsigned long long)((WindowsEthernetTap *)(n.tap.get()))->luid().Value); + OSUtils::ztsnprintf(tapdevbuf,sizeof(tapdevbuf),"%.16llx",(unsigned long long)((WindowsEthernetTap *)(n.tap().get()))->luid().Value); std::string tapdev(tapdevbuf); #else std::string tapdev(n.tap()->deviceName()); @@ -2581,7 +2581,7 @@ public: // without WindowsEthernetTap::isInitialized() returning true, the won't actually // be online yet and setting managed routes on it will fail. const int MAX_SLEEP_COUNT = 500; - for (int i = 0; !((WindowsEthernetTap *)(n.tap.get()))->isInitialized() && i < MAX_SLEEP_COUNT; i++) { + for (int i = 0; !((WindowsEthernetTap *)(n.tap().get()))->isInitialized() && i < MAX_SLEEP_COUNT; i++) { Sleep(10); } #endif @@ -2597,7 +2597,7 @@ public: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY: if (n.tap()) { // sanity check #if defined(__WINDOWS__) && !defined(ZT_SDK) - std::string winInstanceId(((WindowsEthernetTap *)(n.tap.get()))->instanceId()); + std::string winInstanceId(((WindowsEthernetTap *)(n.tap().get()))->instanceId()); #endif *nuptr = (void *)0; n.tap().reset(); From 5f548705dd4156fc6fe0178b1229c7e0b7077360 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 29 Nov 2021 14:11:29 -0800 Subject: [PATCH 32/68] lots more refactoring --- service/OneService.cpp | 437 ++++++++++++++++++++++------------------- zeroidc/src/ext.rs | 10 + zeroidc/src/lib.rs | 10 + 3 files changed, 256 insertions(+), 201 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 5b2448363..4dce383f7 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -147,6 +147,161 @@ size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data namespace ZeroTier { +// Configured networks +class NetworkState +{ +public: + NetworkState() + : _webPort(9993) + , _tap((EthernetTap *)0) + , _idc(nullptr) + , _ainfo(nullptr) + { + // Real defaults are in network 'up' code in network event handler + _settings.allowManaged = true; + _settings.allowGlobal = false; + _settings.allowDefault = false; + _settings.allowDNS = false; + memset(&_config, 0, sizeof(ZT_VirtualNetworkConfig)); + } + + ~NetworkState() + { + this->_managedRoutes.clear(); + this->_tap.reset(); + + if (_ainfo) { + zeroidc::zeroidc_auth_info_delete(_ainfo); + _ainfo = nullptr; + } + + if (_idc) { + zeroidc::zeroidc_stop(_idc); + zeroidc::zeroidc_delete(_idc); + _idc = nullptr; + } + } + + void setWebPort(unsigned int port) { + _webPort = port; + } + + void setTap(std::shared_ptr tap) { + this->_tap = tap; + } + + std::shared_ptr tap() const { + return _tap; + } + + OneService::NetworkSettings settings() const { + return _settings; + } + + void setSettings(const OneService::NetworkSettings &settings) { + _settings = settings; + } + + void setAllowManaged(bool allow) { + _settings.allowManaged = allow; + } + + bool allowManaged() const { + return _settings.allowManaged; + } + + void setAllowGlobal(bool allow) { + _settings.allowGlobal = allow; + } + + bool allowGlobal() const { + return _settings.allowGlobal; + } + + void setAllowDefault(bool allow) { + _settings.allowDefault = allow; + } + + bool allowDefault() const { + return _settings.allowDefault; + } + + void setAllowDNS(bool allow) { + _settings.allowDNS = allow; + } + + bool allowDNS() const { + return _settings.allowDNS; + } + + std::vector allowManagedWhitelist() const { + return _settings.allowManagedWhitelist; + } + + void addToAllowManagedWhiteList(const InetAddress& addr) { + _settings.allowManagedWhitelist.push_back(addr); + } + + const ZT_VirtualNetworkConfig& config() { + return _config; + } + + void setConfig(const ZT_VirtualNetworkConfig *nwc) { + memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); + + if (_config.ssoEnabled && _config.ssoVersion == 1) { + if (_idc == nullptr) { + assert(_config.issuerURL[0] != nullptr); + assert(_config.ssoClientID != nullptr); + assert(_config.centralAuthURL != nullptr); + _idc = zeroidc::zeroidc_new( + _config.issuerURL, + _config.ssoClientID, + _config.centralAuthURL, + _webPort + ); + } + + if (_ainfo != nullptr) { + zeroidc::zeroidc_auth_info_delete(_ainfo); + _ainfo = nullptr; + } + + _ainfo = zeroidc::zeroidc_get_auth_info( + _idc, + _config.ssoState, + _config.ssoNonce + ); + + const char* url = zeroidc::zeroidc_get_auth_url(_ainfo); + memcpy(_config.authenticationURL, url, strlen(url)); + _config.authenticationURL[strlen(url)] = 0; + } + } + + std::vector& managedIps() { + return _managedIps; + } + + void setManagedIps(const std::vector &managedIps) { + _managedIps = managedIps; + } + + std::map< InetAddress, SharedPtr >& managedRoutes() { + return _managedRoutes; + } + +private: + unsigned int _webPort; + std::shared_ptr _tap; + ZT_VirtualNetworkConfig _config; // memcpy() of raw config from core + std::vector _managedIps; + std::map< InetAddress, SharedPtr > _managedRoutes; + OneService::NetworkSettings _settings; + zeroidc::ZeroIDC *_idc; + zeroidc::AuthInfo *_ainfo; +}; + namespace { static const InetAddress NULL_INET_ADDR; @@ -173,12 +328,12 @@ static std::string _trimString(const std::string &s) return s.substr(start,end - start); } -static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings) +static void _networkToJson(nlohmann::json &nj,NetworkState &ns) { char tmp[256]; const char *nstatus = "",*ntype = ""; - switch(nc->status) { + switch(ns.config().status) { case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: nstatus = "REQUESTING_CONFIGURATION"; break; case ZT_NETWORK_STATUS_OK: nstatus = "OK"; break; case ZT_NETWORK_STATUS_ACCESS_DENIED: nstatus = "ACCESS_DENIED"; break; @@ -187,65 +342,68 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc, case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: nstatus = "CLIENT_TOO_OLD"; break; case ZT_NETWORK_STATUS_AUTHENTICATION_REQUIRED: nstatus = "AUTHENTICATION_REQUIRED"; break; } - switch(nc->type) { + switch(ns.config().type) { case ZT_NETWORK_TYPE_PRIVATE: ntype = "PRIVATE"; break; case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break; } - OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",ns.config().nwid); nj["id"] = tmp; nj["nwid"] = tmp; - OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff)); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((ns.config().mac >> 40) & 0xff),(unsigned int)((ns.config().mac >> 32) & 0xff),(unsigned int)((ns.config().mac >> 24) & 0xff),(unsigned int)((ns.config().mac >> 16) & 0xff),(unsigned int)((ns.config().mac >> 8) & 0xff),(unsigned int)(ns.config().mac & 0xff)); nj["mac"] = tmp; - nj["name"] = nc->name; + nj["name"] = ns.config().name; nj["status"] = nstatus; nj["type"] = ntype; - nj["mtu"] = nc->mtu; - nj["dhcp"] = (bool)(nc->dhcp != 0); - nj["bridge"] = (bool)(nc->bridge != 0); - nj["broadcastEnabled"] = (bool)(nc->broadcastEnabled != 0); - nj["portError"] = nc->portError; - nj["netconfRevision"] = nc->netconfRevision; - nj["portDeviceName"] = portDeviceName; + nj["mtu"] = ns.config().mtu; + nj["dhcp"] = (bool)(ns.config().dhcp != 0); + nj["bridge"] = (bool)(ns.config().bridge != 0); + nj["broadcastEnabled"] = (bool)(ns.config().broadcastEnabled != 0); + nj["portError"] = ns.config().portError; + nj["netconfRevision"] = ns.config().netconfRevision; + nj["portDeviceName"] = ns.tap()->deviceName(); + + OneService::NetworkSettings localSettings = ns.settings(); + nj["allowManaged"] = localSettings.allowManaged; nj["allowGlobal"] = localSettings.allowGlobal; nj["allowDefault"] = localSettings.allowDefault; nj["allowDNS"] = localSettings.allowDNS; nlohmann::json aa = nlohmann::json::array(); - for(unsigned int i=0;iassignedAddressCount;++i) { - aa.push_back(reinterpret_cast(&(nc->assignedAddresses[i]))->toString(tmp)); + for(unsigned int i=0;i(&(ns.config().assignedAddresses[i]))->toString(tmp)); } nj["assignedAddresses"] = aa; nlohmann::json ra = nlohmann::json::array(); - for(unsigned int i=0;irouteCount;++i) { + for(unsigned int i=0;i(&(nc->routes[i].target))->toString(tmp); - if (nc->routes[i].via.ss_family == nc->routes[i].target.ss_family) - rj["via"] = reinterpret_cast(&(nc->routes[i].via))->toIpString(tmp); + rj["target"] = reinterpret_cast(&(ns.config().routes[i].target))->toString(tmp); + if (ns.config().routes[i].via.ss_family == ns.config().routes[i].target.ss_family) + rj["via"] = reinterpret_cast(&(ns.config().routes[i].via))->toIpString(tmp); else rj["via"] = nlohmann::json(); - rj["flags"] = (int)nc->routes[i].flags; - rj["metric"] = (int)nc->routes[i].metric; + rj["flags"] = (int)ns.config().routes[i].flags; + rj["metric"] = (int)ns.config().routes[i].metric; ra.push_back(rj); } nj["routes"] = ra; nlohmann::json mca = nlohmann::json::array(); - for(unsigned int i=0;imulticastSubscriptionCount;++i) { + for(unsigned int i=0;imulticastSubscriptions[i].mac).toString(tmp); - m["adi"] = nc->multicastSubscriptions[i].adi; + m["mac"] = MAC(ns.config().multicastSubscriptions[i].mac).toString(tmp); + m["adi"] = ns.config().multicastSubscriptions[i].adi; mca.push_back(m); } nj["multicastSubscriptions"] = mca; nlohmann::json m; - m["domain"] = nc->dns.domain; + m["domain"] = ns.config().dns.domain; m["servers"] = nlohmann::json::array(); for(int j=0;jdns.server_addr[j]); + InetAddress a(ns.config().dns.server_addr[j]); if (a.isV4() || a.isV6()) { char buf[256]; m["servers"].push_back(a.toIpString(buf)); @@ -253,9 +411,9 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc, } nj["dns"] = m; - nj["authenticationURL"] = nc->authenticationURL; - nj["authenticationExpiryTime"] = nc->authenticationExpiryTime; - nj["ssoEnabled"] = nc->ssoEnabled; + nj["authenticationURL"] = ns.config().authenticationURL; + nj["authenticationExpiryTime"] = ns.config().authenticationExpiryTime; + nj["ssoEnabled"] = ns.config().ssoEnabled; } static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer) @@ -527,117 +685,7 @@ public: // Deadline for the next background task service function volatile int64_t _nextBackgroundTaskDeadline; - // Configured networks - class NetworkState - { - public: - NetworkState() - : _tap((EthernetTap *)0) - , _idc(nullptr) - { - // Real defaults are in network 'up' code in network event handler - _settings.allowManaged = true; - _settings.allowGlobal = false; - _settings.allowDefault = false; - _settings.allowDNS = false; - memset(&_config, 0, sizeof(ZT_VirtualNetworkConfig)); - } - - ~NetworkState() - { - this->_managedRoutes.clear(); - this->_tap.reset(); - - if (_idc) { - zeroidc::zeroidc_delete(_idc); - _idc = nullptr; - } - } - - void setTap(std::shared_ptr tap) { - this->_tap = tap; - } - - std::shared_ptr tap() const { - return _tap; - } - - NetworkSettings settings() const { - return _settings; - } - - void setSettings(const NetworkSettings &settings) { - _settings = settings; - } - - void setAllowManaged(bool allow) { - _settings.allowManaged = allow; - } - - bool allowManaged() const { - return _settings.allowManaged; - } - - void setAllowGlobal(bool allow) { - _settings.allowGlobal = allow; - } - - bool allowGlobal() const { - return _settings.allowGlobal; - } - - void setAllowDefault(bool allow) { - _settings.allowDefault = allow; - } - - bool allowDefault() const { - return _settings.allowDefault; - } - - void setAllowDNS(bool allow) { - _settings.allowDNS = allow; - } - - bool allowDNS() const { - return _settings.allowDNS; - } - - std::vector allowManagedWhitelist() const { - return _settings.allowManagedWhitelist; - } - - void addToAllowManagedWhiteList(const InetAddress& addr) { - _settings.allowManagedWhitelist.push_back(addr); - } - - const ZT_VirtualNetworkConfig& config() { - return _config; - } - - void setConfig(const ZT_VirtualNetworkConfig *nwc) { - memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); - } - - std::vector& managedIps() { - return _managedIps; - } - - void setManagedIps(const std::vector &managedIps) { - _managedIps = managedIps; - } - - std::map< InetAddress, SharedPtr >& managedRoutes() { - return _managedRoutes; - } - - private: - std::shared_ptr _tap; - ZT_VirtualNetworkConfig _config; // memcpy() of raw config from core - std::vector _managedIps; - std::map< InetAddress, SharedPtr > _managedRoutes; - NetworkSettings _settings; - zeroidc::ZeroIDC *_idc; - }; + std::map _nets; Mutex _nets_m; @@ -1251,26 +1299,17 @@ public: virtual bool setNetworkSettings(const uint64_t nwid,const NetworkSettings &settings) { - Mutex::Lock _l(_nets_m); - - std::map::iterator n(_nets.find(nwid)); - if (n == _nets.end()) - return false; - n->second.setSettings(settings); - char nlcpath[4096]; OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid); FILE *out = fopen(nlcpath,"w"); if (out) { - fprintf(out,"allowManaged=%d\n",(int)n->second.allowManaged()); - fprintf(out,"allowGlobal=%d\n",(int)n->second.allowGlobal()); - fprintf(out,"allowDefault=%d\n",(int)n->second.allowDefault()); - fprintf(out,"allowDNS=%d\n",(int)n->second.allowDNS()); + fprintf(out,"allowManaged=%d\n",(int)settings.allowManaged); + fprintf(out,"allowGlobal=%d\n",(int)settings.allowGlobal); + fprintf(out,"allowDefault=%d\n",(int)settings.allowDefault); + fprintf(out,"allowDNS=%d\n",(int)settings.allowDNS); fclose(out); } - if (n->second.tap()) - syncManagedStuff(n->second,true,true,true); return true; } @@ -1474,18 +1513,16 @@ public: } } else if (ps[0] == "network") { - ZT_VirtualNetworkList *nws = _node->networks(); - if (nws) { + Mutex::Lock _l(_nets_m); + if (!_nets.empty()) { if (ps.size() == 1) { // Return [array] of all networks res = nlohmann::json::array(); - for(unsigned long i=0;inetworkCount;++i) { - OneService::NetworkSettings localSettings; - getNetworkSettings(nws->networks[i].nwid,localSettings); + for (auto it = _nets.begin(); it != _nets.end(); ++it) { + NetworkState &ns = it->second; nlohmann::json nj; - _networkToJson(nj,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings); - res.push_back(nj); + _networkToJson(res, ns); } scode = 200; @@ -1493,18 +1530,13 @@ public: // Return a single network by ID or 404 if not found const uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str()); - for(unsigned long i=0;inetworkCount;++i) { - if (nws->networks[i].nwid == wantnw) { - OneService::NetworkSettings localSettings; - getNetworkSettings(nws->networks[i].nwid,localSettings); - _networkToJson(res,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings); - scode = 200; - break; - } + if (_nets.find(wantnw) != _nets.end()) { + res = json::object(); + NetworkState& ns = _nets[wantnw]; + _networkToJson(res, ns); + scode = 200; } - } else scode = 404; - _node->freeQueryResult((void *)nws); } else scode = 500; } else if (ps[0] == "peer") { ZT_PeerList *pl = _node->peers(); @@ -1667,37 +1699,41 @@ public: uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str()); _node->join(wantnw,(void *)0,(void *)0); // does nothing if we are a member - ZT_VirtualNetworkList *nws = _node->networks(); - if (nws) { - for(unsigned long i=0;inetworkCount;++i) { - if (nws->networks[i].nwid == wantnw) { - OneService::NetworkSettings localSettings; - getNetworkSettings(nws->networks[i].nwid,localSettings); - - try { - json j(OSUtils::jsonParse(body)); - if (j.is_object()) { - json &allowManaged = j["allowManaged"]; - if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged; - json &allowGlobal = j["allowGlobal"]; - if (allowGlobal.is_boolean()) localSettings.allowGlobal = (bool)allowGlobal; - json &allowDefault = j["allowDefault"]; - if (allowDefault.is_boolean()) localSettings.allowDefault = (bool)allowDefault; - json &allowDNS = j["allowDNS"]; - if (allowDNS.is_boolean()) localSettings.allowDNS = (bool)allowDNS; - } - } catch ( ... ) { - // discard invalid JSON + Mutex::Lock l(_nets_m); + if (!_nets.empty()) { + if (_nets.find(wantnw) != _nets.end()) { + NetworkState& ns = _nets[wantnw]; + try { + json j(OSUtils::jsonParse(body)); + + json &allowManaged = j["allowManaged"]; + if (allowManaged.is_boolean()) { + ns.setAllowManaged((bool)allowManaged); } - - setNetworkSettings(nws->networks[i].nwid,localSettings); - _networkToJson(res,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings); - - scode = 200; - break; + json& allowGlobal = j["allowGlobal"]; + if (allowGlobal.is_boolean()) { + ns.setAllowGlobal((bool)allowGlobal); + } + json& allowDefault = j["allowDefault"]; + if (allowDefault.is_boolean()) { + ns.setAllowDefault((bool)allowDefault); + } + json& allowDNS = j["allowDNS"]; + if (allowDNS.is_boolean()) { + ns.setAllowDNS((bool)allowDNS); + } + } catch (...) { + // discard invalid JSON } + setNetworkSettings(wantnw, ns.settings()); + if (ns.tap()) { + syncManagedStuff(ns,true,true,true); + } + + _networkToJson(res, ns); + + scode = 200; } - _node->freeQueryResult((void *)nws); } else scode = 500; } else scode = 404; @@ -2498,9 +2534,9 @@ public: { Mutex::Lock _l(_nets_m); NetworkState &n = _nets[nwid]; + n.setWebPort(_primaryPort); - switch(op) { - + switch (op) { case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP: if (!n.tap()) { try { @@ -2615,7 +2651,6 @@ public: _nets.erase(nwid); } break; - } return 0; } diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index 3e89f247f..507a64db7 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -72,6 +72,16 @@ pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { idc.stop(); } +#[no_mangle] +pub extern "C" fn zeroidc_is_running(ptr: *mut ZeroIDC) -> bool { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + + idc.is_running() +} + #[no_mangle] pub extern "C" fn zeroidc_get_auth_info( ptr: *mut ZeroIDC, diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 7de1f4abe..15beea403 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -117,6 +117,16 @@ impl ZeroIDC { } } + fn is_running(&mut self) -> bool { + let local = Arc::clone(&self.inner); + + if (*local.lock().unwrap()).running { + true + } else { + false + } + } + fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option { let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); From 6393a4beecf2e495cff38cf5d52fa48a67e8a4ac Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 30 Nov 2021 14:22:25 -0800 Subject: [PATCH 33/68] progress --- service/OneService.cpp | 66 ++++++++++++++++++++++++++++++++++-------- zeroidc/src/ext.rs | 34 +++++++++++++++++++--- zeroidc/src/lib.rs | 3 ++ 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 4dce383f7..59ba389b2 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -247,19 +247,28 @@ public: } void setConfig(const ZT_VirtualNetworkConfig *nwc) { + char nwbuf[17] = {}; + const char* nwid = Utils::hex(nwc->nwid, nwbuf); + fprintf(stderr, "NetworkState::setConfig(%s)\n", nwid); memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); if (_config.ssoEnabled && _config.ssoVersion == 1) { - if (_idc == nullptr) { - assert(_config.issuerURL[0] != nullptr); + fprintf(stderr, "ssoEnabled for %s\n", nwid); + if (_idc == nullptr) + { + assert(_config.issuerURL != nullptr); assert(_config.ssoClientID != nullptr); assert(_config.centralAuthURL != nullptr); + char buf[17] = {}; _idc = zeroidc::zeroidc_new( + Utils::hex(_config.nwid, buf), _config.issuerURL, _config.ssoClientID, _config.centralAuthURL, _webPort ); + + fprintf(stderr, "idc created (%s, %s, %s)\n", _config.issuerURL, _config.ssoClientID, _config.centralAuthURL); } if (_ainfo != nullptr) { @@ -291,6 +300,14 @@ public: return _managedRoutes; } + const char* getAuthURL() { + if (_ainfo != nullptr) { + return zeroidc::zeroidc_get_auth_url(_ainfo); + } + fprintf(stderr, "_ainfo is null\n"); + return ""; + } + private: unsigned int _webPort; std::shared_ptr _tap; @@ -410,10 +427,11 @@ static void _networkToJson(nlohmann::json &nj,NetworkState &ns) } } nj["dns"] = m; - - nj["authenticationURL"] = ns.config().authenticationURL; - nj["authenticationExpiryTime"] = ns.config().authenticationExpiryTime; - nj["ssoEnabled"] = ns.config().ssoEnabled; + if (ns.config().ssoEnabled) { + nj["authenticationURL"] = ns.getAuthURL(); + nj["authenticationExpiryTime"] = ns.config().authenticationExpiryTime; + nj["ssoEnabled"] = ns.config().ssoEnabled; + } } static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer) @@ -1519,10 +1537,12 @@ public: // Return [array] of all networks res = nlohmann::json::array(); + for (auto it = _nets.begin(); it != _nets.end(); ++it) { NetworkState &ns = it->second; nlohmann::json nj; - _networkToJson(res, ns); + _networkToJson(nj, ns); + res.push_back(nj); } scode = 200; @@ -1536,8 +1556,14 @@ public: _networkToJson(res, ns); scode = 200; } - } else scode = 404; - } else scode = 500; + } else { + fprintf(stderr, "not found\n"); + scode = 404; + } + } else { + fprintf(stderr, "_nets is empty??\n"); + scode = 500; + } } else if (ps[0] == "peer") { ZT_PeerList *pl = _node->peers(); if (pl) { @@ -1602,7 +1628,15 @@ public: } else scode = 404; } - } else scode = 401; // isAuth == false + } else if (ps[0] == "sso") { + // SSO redirect handling + fprintf(stderr, "sso get\n"); + fprintf(stderr, "path: %s\n", path.c_str()); + fprintf(stderr, "body: %s\n", body.c_str()); + scode = 200; scode = 200; + } else { + scode = 401; // isAuth == false && !sso + } } else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) { if (isAuth) { if (ps[0] == "bond") { @@ -1743,7 +1777,16 @@ public: else scode = 404; } - } else scode = 401; // isAuth == false + } else if (ps[0] == "sso") { + // sso post handling + fprintf(stderr, "sso post\n"); + fprintf(stderr, "path: %s\n", path.c_str()); + fprintf(stderr, "body: %s\n", body.c_str()); + scode = 200; + } + else { + scode = 401; // isAuth == false + } } else if (httpMethod == HTTP_DELETE) { if (isAuth) { @@ -1774,7 +1817,6 @@ public: scode = _controller->handleControlPlaneHttpDELETE(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); else scode = 404; } - } else scode = 401; // isAuth = false } else { scode = 400; diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index 507a64db7..ab69829ac 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -5,11 +5,17 @@ use crate::{AuthInfo, ZeroIDC}; #[no_mangle] pub extern "C" fn zeroidc_new( + network_id: *const c_char, issuer: *const c_char, client_id: *const c_char, auth_endpoint: *const c_char, web_listen_port: u16, ) -> *mut ZeroIDC { + if network_id.is_null() { + println!("network_id is null"); + return std::ptr::null_mut(); + + } if issuer.is_null() { println!("issuer is null"); return std::ptr::null_mut(); @@ -25,12 +31,14 @@ pub extern "C" fn zeroidc_new( return std::ptr::null_mut(); } - let iss = unsafe { CStr::from_ptr(issuer) }; - let c_id = unsafe { CStr::from_ptr(client_id) }; + let network_id = unsafe {CStr::from_ptr(network_id) }; + let issuer = unsafe { CStr::from_ptr(issuer) }; + let client_id = unsafe { CStr::from_ptr(client_id) }; let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) }; match ZeroIDC::new( - iss.to_str().unwrap(), - c_id.to_str().unwrap(), + network_id.to_str().unwrap(), + issuer.to_str().unwrap(), + client_id.to_str().unwrap(), auth_endpoint.to_str().unwrap(), web_listen_port, ) { @@ -82,6 +90,24 @@ pub extern "C" fn zeroidc_is_running(ptr: *mut ZeroIDC) -> bool { idc.is_running() } +#[no_mangle] +pub extern "C" fn zeroidc_process_form_post(ptr: *mut ZeroIDC, body: *const c_char) -> bool { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + + if body.is_null() { + println!("body is null"); + return false + } + + let body = unsafe { CStr::from_ptr(body) } + .to_str().unwrap().to_string(); + + false +} + #[no_mangle] pub extern "C" fn zeroidc_get_auth_info( ptr: *mut ZeroIDC, diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 15beea403..6deebeaaf 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -21,6 +21,7 @@ pub struct ZeroIDC { struct Inner { running: bool, + network_id: String, auth_endpoint: String, oidc_thread: Option>, oidc_client: Option, @@ -43,6 +44,7 @@ pub struct AuthInfo { impl ZeroIDC { fn new( + network_id: &str, issuer: &str, client_id: &str, auth_ep: &str, @@ -51,6 +53,7 @@ impl ZeroIDC { let idc = ZeroIDC { inner: Arc::new(Mutex::new(Inner { running: false, + network_id: network_id.to_string(), auth_endpoint: auth_ep.to_string(), oidc_thread: None, oidc_client: None, From 4a1d6f4cc828d7aa4ac76ca5558cac87167de1b0 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 30 Nov 2021 15:22:31 -0800 Subject: [PATCH 34/68] get central controller docker builds working again --- ext/central-controller-docker/Dockerfile | 2 +- ext/central-controller-docker/Dockerfile.builder | 1 + make-linux.mk | 2 +- service/OneService.cpp | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/central-controller-docker/Dockerfile b/ext/central-controller-docker/Dockerfile index 8435b1685..c59aa8f5d 100644 --- a/ext/central-controller-docker/Dockerfile +++ b/ext/central-controller-docker/Dockerfile @@ -2,7 +2,7 @@ FROM registry.zerotier.com/zerotier/controller-builder:latest as builder MAINTAINER Adam Ierymekno , Grant Limberg ADD . /ZeroTierOne -RUN cd ZeroTierOne && make clean && make central-controller -j8 +RUN export PATH=$PATH:~/.cargo/bin && cd ZeroTierOne && make clean && make central-controller -j8 FROM registry.zerotier.com/zerotier/controller-run:latest COPY --from=builder /ZeroTierOne/zerotier-one /usr/local/bin/zerotier-one diff --git a/ext/central-controller-docker/Dockerfile.builder b/ext/central-controller-docker/Dockerfile.builder index 69e69701f..573b1ef69 100644 --- a/ext/central-controller-docker/Dockerfile.builder +++ b/ext/central-controller-docker/Dockerfile.builder @@ -10,3 +10,4 @@ RUN dnf -qy module disable postgresql RUN yum -y install epel-release && yum -y update && yum clean all RUN yum groupinstall -y "Development Tools" && yum clean all RUN yum install -y bash cmake postgresql10 postgresql10-devel clang jemalloc jemalloc-devel libpqxx libpqxx-devel && yum clean all +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/make-linux.mk b/make-linux.mk index 117e9a5e0..bedf6b538 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -321,7 +321,7 @@ zerotier-idtool: zerotier-one zerotier-cli: zerotier-one ln -sf zerotier-one zerotier-cli -$(CORE_OBJS): zeroidc +$(ONE_OBJS): zeroidc libzerotiercore.a: FORCE make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++11 -fstack-protector -fPIC" $(CORE_OBJS) diff --git a/service/OneService.cpp b/service/OneService.cpp index 59ba389b2..b2b8a92b5 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -2253,7 +2253,7 @@ public: // Apple on the other hand seems to need this at least on some versions. #ifndef __APPLE__ bool haveRoute = false; - for(std::vector::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) { + for(std::vector::iterator ip(n.managedIps().begin());ip!=n.managedIps().end();++ip) { if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) { haveRoute = true; break; From d15516f0ef5e2fd06a91a88144e7ca793e4da75b Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 30 Nov 2021 16:18:34 -0800 Subject: [PATCH 35/68] query fix & controller build fix --- controller/PostgreSQL.cpp | 2 +- ext/central-controller-docker/Dockerfile.run_base | 2 +- service/OneService.cpp | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index f79c8725d..14e210540 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -392,7 +392,7 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str exit(6); } - r = w.exec_params("SELECT org.client_id, org.authorization_endpoint, org.sso_version " + r = w.exec_params("SELECT org.client_id, org.authorization_endpoint, org.sso_impl_version " "FROM ztc_network AS nw, ztc_org AS org " "WHERE nw.id = $1 AND nw.sso_enabled = true AND org.owner_id = nw.owner_id", networkId); diff --git a/ext/central-controller-docker/Dockerfile.run_base b/ext/central-controller-docker/Dockerfile.run_base index 4db9cbd28..9a08f5f72 100644 --- a/ext/central-controller-docker/Dockerfile.run_base +++ b/ext/central-controller-docker/Dockerfile.run_base @@ -2,4 +2,4 @@ FROM centos:8 RUN yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm RUN dnf -qy module disable postgresql RUN yum -y install epel-release && yum -y update && yum clean all -RUN yum install -y jemalloc jemalloc-devel postgresql10 libpqxx && yum clean all +RUN yum install -y jemalloc jemalloc-devel postgresql10 libpqxx libpqxx-devel && yum clean all diff --git a/service/OneService.cpp b/service/OneService.cpp index b2b8a92b5..b30747fb7 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -251,7 +251,9 @@ public: const char* nwid = Utils::hex(nwc->nwid, nwbuf); fprintf(stderr, "NetworkState::setConfig(%s)\n", nwid); memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); - + fprintf(stderr, "ssoEnabled: %s, ssoVersion: %d\n", + _config.ssoEnabled ? "true" : "false", _config.ssoVersion); + if (_config.ssoEnabled && _config.ssoVersion == 1) { fprintf(stderr, "ssoEnabled for %s\n", nwid); if (_idc == nullptr) From a33d7c64fe1932707fda20e7b41d7e2351b3f388 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 30 Nov 2021 17:27:13 -0800 Subject: [PATCH 36/68] more fixin --- controller/EmbeddedNetworkController.cpp | 28 +++------------------ controller/PostgreSQL.cpp | 32 +++++++++++++++++++++++- service/OneService.cpp | 7 +++++- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 4ce48fa26..812fdd606 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -63,29 +63,6 @@ namespace ZeroTier { namespace { -std::string url_encode(const std::string &value) { - std::ostringstream escaped; - escaped.fill('0'); - escaped << std::hex; - - for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) { - std::string::value_type c = (*i); - - // Keep alphanumeric and other accepted characters intact - if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { - escaped << c; - continue; - } - - // Any other characters are percent-encoded - escaped << std::uppercase; - escaped << '%' << std::setw(2) << int((unsigned char) c); - escaped << std::nouppercase; - } - - return escaped.str(); -} - static json _renderRule(ZT_VirtualNetworkRule &rule) { char tmp[128]; @@ -503,7 +480,7 @@ EmbeddedNetworkController::~EmbeddedNetworkController() } void EmbeddedNetworkController::setSSORedirectURL(const std::string &url) { - _ssoRedirectURL = url_encode(url); + _ssoRedirectURL = url; } void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender) @@ -1494,6 +1471,9 @@ void EmbeddedNetworkController::_request( if (!info.centralAuthURL.empty()) { Utils::scopy(nc->centralAuthURL, sizeof(nc->centralAuthURL), info.centralAuthURL.c_str()); } + if (!info.issuerURL.empty()) { + Utils::scopy(nc->issuerURL, sizeof(nc->issuerURL), info.issuerURL.c_str()); + } if (!info.ssoNonce.empty()) { Utils::scopy(nc->ssoNonce, sizeof(nc->ssoNonce), info.ssoNonce.c_str()); } diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 14e210540..15d359f82 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -80,6 +80,28 @@ std::vector split(std::string str, char delim){ return tokens; } +std::string url_encode(const std::string &value) { + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; + + for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) { + std::string::value_type c = (*i); + + // Keep alphanumeric and other accepted characters intact + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + escaped << c; + continue; + } + + // Any other characters are percent-encoded + escaped << std::uppercase; + escaped << '%' << std::setw(2) << int((unsigned char) c); + escaped << std::nouppercase; + } + + return escaped.str(); +} } // anonymous namespace @@ -425,7 +447,7 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str OSUtils::ztsnprintf(url, sizeof(authenticationURL), "%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s", authorization_endpoint.c_str(), - redirectURL.c_str(), + url_encode(redirectURL).c_str(), nonce.c_str(), state_hex, client_id.c_str()); @@ -436,6 +458,14 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str info.ssoNonce = nonce; info.ssoState = std::string(state_hex); info.centralAuthURL = redirectURL; + fprintf( + stderr, + "ssoClientID: %s\nissuerURL: %s\nssoNonce: %s\nssoState: %s\ncentralAuthURL: %s", + info.ssoClientID.c_str(), + info.issuerURL.c_str(), + info.ssoNonce.c_str(), + info.ssoState.c_str(), + info.centralAuthURL.c_str()); } } else { fprintf(stderr, "client_id: %s\nauthorization_endpoint: %s\n", client_id.c_str(), authorization_endpoint.c_str()); diff --git a/service/OneService.cpp b/service/OneService.cpp index b30747fb7..274b5b9c7 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -253,7 +253,7 @@ public: memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); fprintf(stderr, "ssoEnabled: %s, ssoVersion: %d\n", _config.ssoEnabled ? "true" : "false", _config.ssoVersion); - + if (_config.ssoEnabled && _config.ssoVersion == 1) { fprintf(stderr, "ssoEnabled for %s\n", nwid); if (_idc == nullptr) @@ -261,6 +261,11 @@ public: assert(_config.issuerURL != nullptr); assert(_config.ssoClientID != nullptr); assert(_config.centralAuthURL != nullptr); + + fprintf(stderr, "Issuer URL: %s\n", _config.issuerURL); + fprintf(stderr, "Client ID: %s\n", _config.ssoClientID); + fprintf(stderr, "Central Auth URL: %s\n", _config.centralAuthURL); + char buf[17] = {}; _idc = zeroidc::zeroidc_new( Utils::hex(_config.nwid, buf), From dfdac7adbd8a7e05b424df773837fae3c90db009 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 30 Nov 2021 17:31:46 -0800 Subject: [PATCH 37/68] iomanip --- controller/PostgreSQL.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 15d359f82..86ffe6855 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include From 73c186b111a7773875491e2322b901e1e0fc749b Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Dec 2021 09:27:30 -0800 Subject: [PATCH 38/68] add network_id extra param to auth flow --- zeroidc/src/lib.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 6deebeaaf..a0dca8892 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -62,7 +62,11 @@ impl ZeroIDC { let iss = match IssuerUrl::new(issuer.to_string()) { Ok(i) => i, - Err(e) => return Err(e.to_string()), + Err(e) => { + println!("Error generating Issuer URL"); + return Err(e.to_string()); + } + }; let provider_meta = match CoreProviderMetadata::discover(&iss, http_client) { @@ -73,7 +77,10 @@ impl ZeroIDC { let r = format!("http://localhost:{}/sso", local_web_port); let redir_url = match Url::parse(&r) { Ok(s) => s, - Err(e) => return Err(e.to_string()), + Err(e) => { + println!("Error generating redirect URL"); + return Err(e.to_string()); + } }; let redirect = match RedirectUrl::new(redir_url.to_string()) { @@ -130,8 +137,13 @@ impl ZeroIDC { } } + fn get_network_id(&mut self) -> String { + return (*self.inner.lock().unwrap()).network_id.clone() + } + fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option { let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + let network_id = self.get_network_id(); let r = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { let (auth_url, csrf_token, nonce) = c @@ -141,9 +153,10 @@ impl ZeroIDC { nonce_func(nonce), ) .add_scope(Scope::new("read".to_string())) - .add_scope(Scope::new("read".to_string())) + .add_scope(Scope::new("offline_access".to_string())) .add_scope(Scope::new("openid".to_string())) .set_pkce_challenge(pkce_challenge) + .add_extra_param("network_id", network_id) .url(); return AuthInfo { From 7cce23ae796e934130dd9bcd8d538adca18b4454 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Dec 2021 10:44:29 -0800 Subject: [PATCH 39/68] wip --- controller/EmbeddedNetworkController.cpp | 32 ++++++++++++------------ node/IncomingPacket.cpp | 28 ++++++--------------- node/NetworkConfig.cpp | 2 ++ node/NetworkConfig.hpp | 2 +- node/Node.cpp | 4 ++- service/OneService.cpp | 5 ++++ 6 files changed, 35 insertions(+), 38 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 812fdd606..6a8c9dd4b 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1338,7 +1338,7 @@ void EmbeddedNetworkController::_request( bool networkSSOEnabled = OSUtils::jsonBool(network["ssoEnabled"], false); bool memberSSOExempt = OSUtils::jsonBool(member["ssoExempt"], false); AuthInfo info; - if (networkSSOEnabled && ! memberSSOExempt) { + if (networkSSOEnabled && !memberSSOExempt) { info = _db.getSSOAuthInfo(member, _ssoRedirectURL); assert(info.enabled == networkSSOEnabled); @@ -1364,24 +1364,23 @@ void EmbeddedNetworkController::_request( return; } } else if (info.version == 1) { - if (!info.authenticationURL.empty()) { - _db.networkMemberSSOHasExpired(nwid, now); - onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); + _db.networkMemberSSOHasExpired(nwid, now); + onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); - Dictionary<8192> authInfo; - authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, info.version); - authInfo.add(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, info.issuerURL.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, info.centralAuthURL.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str()); + Dictionary<8192> authInfo; + authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, info.version); + authInfo.add(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, info.issuerURL.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, info.centralAuthURL.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str()); + authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str()); - DB::cleanMember(member); - _db.save(member, true); + DB::cleanMember(member); + _db.save(member, true); - _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); - return; - } + fprintf(stderr, "Sending NC_ERROR_AUTHENTICATION_REQUIRED\n"); + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); + return; } else { fprintf(stderr, "invalid sso info.version %llu\n", info.version); } @@ -1472,6 +1471,7 @@ void EmbeddedNetworkController::_request( Utils::scopy(nc->centralAuthURL, sizeof(nc->centralAuthURL), info.centralAuthURL.c_str()); } if (!info.issuerURL.empty()) { + fprintf(stderr, "copying issuerURL to nc: %s\n", info.issuerURL.c_str()); Utils::scopy(nc->issuerURL, sizeof(nc->issuerURL), info.issuerURL.c_str()); } if (!info.ssoNonce.empty()) { diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 15003b4eb..fbf3dd029 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -191,9 +191,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar } break; case Packet::ERROR_NETWORK_AUTHENTICATION_REQUIRED: { + fprintf(stderr, "\nPacket::ERROR_NETWORK_AUTHENTICATION_REQUIRED\n\n"); const SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if ((network)&&(network->controller() == peer->address())) { - bool noUrl = true; int s = (int)size() - (ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8); if (s > 2) { const uint16_t errorDataSize = at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8); @@ -204,55 +204,43 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar uint64_t authVer = authInfo.getUI(ZT_AUTHINFO_DICT_KEY_VERSION, 0ULL); if (authVer == 0) { + fprintf(stderr, "authVer == 1\n"); char authenticationURL[2048]; - + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) { authenticationURL[sizeof(authenticationURL) - 1] = 0; // ensure always zero terminated network->setAuthenticationRequired(authenticationURL); - noUrl = false; } } else if (authVer == 1) { - bool haveIssuerURL = false; + fprintf(stderr, "authVer == 2\n"); char issuerURL[2048] = { 0 }; - bool haveCentralURL = false; char centralAuthURL[2048] = { 0 }; - bool haveNonce = false; char ssoNonce[64] = { 0 }; - bool haveState = false; char ssoState[128] = {0}; - bool haveClientID = false; char ssoClientID[256] = { 0 }; if (authInfo.get(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, issuerURL, sizeof(issuerURL)) > 0) { issuerURL[sizeof(issuerURL) - 1] = 0; - haveIssuerURL = true; } if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, centralAuthURL, sizeof(centralAuthURL))>0) { centralAuthURL[sizeof(centralAuthURL) - 1] = 0; - haveCentralURL = true; } if (authInfo.get(ZT_AUTHINFO_DICT_KEY_NONCE, ssoNonce, sizeof(ssoNonce)) > 0) { ssoNonce[sizeof(ssoNonce) - 1] = 0; - haveNonce = true; } if (authInfo.get(ZT_AUTHINFO_DICT_KEY_STATE, ssoState, sizeof(ssoState)) > 0) { ssoState[sizeof(ssoState) - 1] = 0; - haveState = true; } if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, ssoClientID, sizeof(ssoClientID)) > 0) { ssoClientID[sizeof(ssoClientID) - 1] = 0; - haveClientID = true; } - noUrl = ! (haveIssuerURL && haveCentralURL && haveNonce && haveState && haveClientID); - - if (!noUrl) { - network->setAuthenticationRequired(issuerURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); - } + fprintf(stderr, "Setting auth required on network\n"); + network->setAuthenticationRequired(issuerURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); } } - } - if (noUrl) { + } else { + fprintf(stderr, "authinfo??????\n"); network->setAuthenticationRequired(""); } } diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 2b76b6730..4615b66a7 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -412,6 +412,7 @@ bool NetworkConfig::fromDictionary(const DictionaryissuerURL, (unsigned int)sizeof(this->issuerURL)) > 0) { this->issuerURL[sizeof(this->issuerURL) - 1] = 0; + fprintf(stderr, "Loaded issuer url: %s\n", this->issuerURL); } if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL, (unsigned int)sizeof(this->centralAuthURL)) > 0) { this->centralAuthURL[sizeof(this->centralAuthURL) - 1] = 0; @@ -432,6 +433,7 @@ bool NetworkConfig::fromDictionary(const DictionaryssoNonce[0] = 0; this->ssoState[0] = 0; this->ssoClientID[0] = 0; + this->issuerURL[0] = 0; } } } diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 8c08838c5..b09b8aa19 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -94,7 +94,7 @@ namespace ZeroTier { // Dictionary capacity needed for max size network config -#define ZT_NETWORKCONFIG_DICT_CAPACITY (4096 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP)) +#define ZT_NETWORKCONFIG_DICT_CAPACITY (4096 + (sizeof(ZT_VirtualNetworkConfig)) + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP)) // Dictionary capacity needed for max size network meta-data #define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024 diff --git a/node/Node.cpp b/node/Node.cpp index 5b0fa8cc5..75acc35de 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -741,8 +741,10 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des n->setAccessDenied(); break; case NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED: { - } + fprintf(stderr, "\n\nGot auth required\n\n"); + break; + } default: break; } diff --git a/service/OneService.cpp b/service/OneService.cpp index 274b5b9c7..89734a690 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -275,6 +275,11 @@ public: _webPort ); + if (_idc == nullptr) { + fprintf(stderr, "idc is null\n"); + return; + } + fprintf(stderr, "idc created (%s, %s, %s)\n", _config.issuerURL, _config.ssoClientID, _config.centralAuthURL); } From eaccce743f9740241e8440827e2a8231a268f4df Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Dec 2021 12:07:05 -0800 Subject: [PATCH 40/68] moar plumbing progress --- node/IncomingPacket.cpp | 13 +++++++------ node/Network.cpp | 15 ++++++++++++--- node/Network.hpp | 14 ++++++++++---- node/NetworkConfig.cpp | 5 ++--- node/Node.cpp | 4 ++-- service/OneService.cpp | 6 +++++- 6 files changed, 38 insertions(+), 19 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index fbf3dd029..f875d917b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -142,7 +142,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { const SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if ((network)&&(network->controller() == peer->address())) - network->setNotFound(); + network->setNotFound(tPtr); } break; @@ -153,7 +153,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { const SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if ((network)&&(network->controller() == peer->address())) - network->setNotFound(); + network->setNotFound(tPtr); } break; @@ -176,7 +176,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar // Network controller: network access denied. const SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if ((network)&&(network->controller() == peer->address())) - network->setAccessDenied(); + network->setAccessDenied(tPtr); } break; case Packet::ERROR_UNWANTED_MULTICAST: { @@ -209,7 +209,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) { authenticationURL[sizeof(authenticationURL) - 1] = 0; // ensure always zero terminated - network->setAuthenticationRequired(authenticationURL); + network->setAuthenticationRequired(tPtr, authenticationURL); } } else if (authVer == 1) { fprintf(stderr, "authVer == 2\n"); @@ -221,6 +221,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar if (authInfo.get(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, issuerURL, sizeof(issuerURL)) > 0) { issuerURL[sizeof(issuerURL) - 1] = 0; + fprintf(stderr, "Issuer URL from info: %s\n", issuerURL); } if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, centralAuthURL, sizeof(centralAuthURL))>0) { centralAuthURL[sizeof(centralAuthURL) - 1] = 0; @@ -236,12 +237,12 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar } fprintf(stderr, "Setting auth required on network\n"); - network->setAuthenticationRequired(issuerURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); + network->setAuthenticationRequired(tPtr, issuerURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); } } } else { fprintf(stderr, "authinfo??????\n"); - network->setAuthenticationRequired(""); + network->setAuthenticationRequired(tPtr, ""); } } } break; diff --git a/node/Network.cpp b/node/Network.cpp index 357cf1432..a8fa5ebca 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1115,7 +1115,7 @@ void Network::requestConfiguration(void *tPtr) this->setConfiguration(tPtr,*nconf,false); delete nconf; } else { - this->setNotFound(); + this->setNotFound(tPtr); } } else if ((_id & 0xff) == 0x01) { // ffAAaaaaaaaaaa01 -- where AA is the IPv4 /8 to use and aaaaaaaaaa is the anchor node for multicast gather and replication @@ -1199,7 +1199,7 @@ void Network::requestConfiguration(void *tPtr) if (RR->localNetworkController) { RR->localNetworkController->request(_id,InetAddress(),0xffffffffffffffffULL,RR->identity,rmd); } else { - this->setNotFound(); + this->setNotFound(tPtr); } return; } @@ -1438,6 +1438,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const ec->authenticationExpiryTime = _config.authenticationExpiryTime; ec->ssoEnabled = _config.ssoEnabled; Utils::scopy(ec->centralAuthURL, sizeof(ec->centralAuthURL), _config.centralAuthURL); + Utils::scopy(ec->issuerURL, sizeof(ec->issuerURL), _config.issuerURL); Utils::scopy(ec->ssoNonce, sizeof(ec->ssoNonce), _config.ssoNonce); Utils::scopy(ec->ssoState, sizeof(ec->ssoState), _config.ssoState); Utils::scopy(ec->ssoClientID, sizeof(ec->ssoClientID), _config.ssoClientID); @@ -1547,18 +1548,26 @@ Membership &Network::_membership(const Address &a) return _memberships[a]; } -void Network::setAuthenticationRequired(const char* issuerURL, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state) +void Network::setAuthenticationRequired(void *tPtr, const char* issuerURL, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state) { Mutex::Lock _l(_lock); _netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED; _config.ssoEnabled = true; _config.ssoVersion = 1; + fprintf(stderr, "Network::setAuthenticationRequired issuerURL: %s\n", issuerURL); Utils::scopy(_config.issuerURL, sizeof(_config.issuerURL), issuerURL); Utils::scopy(_config.centralAuthURL, sizeof(_config.centralAuthURL), centralEndpoint); Utils::scopy(_config.ssoClientID, sizeof(_config.ssoClientID), clientID); Utils::scopy(_config.ssoNonce, sizeof(_config.ssoNonce), nonce); Utils::scopy(_config.ssoState, sizeof(_config.ssoState), state); + _sendUpdateEvent(tPtr); +} + +void Network::_sendUpdateEvent(void *tPtr) { + ZT_VirtualNetworkConfig ctmp; + _externalConfig(&ctmp); + RR->node->configureVirtualNetworkPort(tPtr, _id, &_uPtr, (_portInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP, &ctmp); } } // namespace ZeroTier diff --git a/node/Network.hpp b/node/Network.hpp index 10b48f1f9..c201a6314 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -205,38 +205,43 @@ public: /** * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this */ - inline void setAccessDenied() + inline void setAccessDenied(void *tPtr) { Mutex::Lock _l(_lock); _netconfFailure = NETCONF_FAILURE_ACCESS_DENIED; + + _sendUpdateEvent(tPtr); } /** * Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this */ - inline void setNotFound() + inline void setNotFound(void *tPtr) { Mutex::Lock _l(_lock); _netconfFailure = NETCONF_FAILURE_NOT_FOUND; + + _sendUpdateEvent(tPtr); } /** * Set netconf failure to 'authentication required' possibly with an authorization URL */ - inline void setAuthenticationRequired(const char *url) + inline void setAuthenticationRequired(void *tPtr, const char *url) { Mutex::Lock _l(_lock); _netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED; _authenticationURL = (url) ? url : ""; _config.ssoEnabled = true; _config.ssoVersion = 0; + _sendUpdateEvent(tPtr); } /** * set netconf failure to 'authentication required' along with info needed * for sso full flow authentication. */ - void setAuthenticationRequired(const char* issuerURL, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state); + void setAuthenticationRequired(void *tPtr, const char* issuerURL, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state); /** * Causes this network to request an updated configuration from its master node now @@ -420,6 +425,7 @@ private: void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector &allMulticastGroups); std::vector _allMulticastGroups() const; Membership &_membership(const Address &a); + void _sendUpdateEvent(void *tPtr); const RuntimeEnvironment *const RR; void *_uPtr; diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 4615b66a7..2d5e2a382 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -195,10 +195,9 @@ bool NetworkConfig::toDictionary(Dictionary &d,b } else if(this->ssoVersion == 1) { if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false; - if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false; + //if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL, this->issuerURL)) return false; - if (! d.add(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL)) - return false; + if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID)) return false; diff --git a/node/Node.cpp b/node/Node.cpp index 75acc35de..a0dd03fc1 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -735,10 +735,10 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des switch(errorCode) { case NetworkController::NC_ERROR_OBJECT_NOT_FOUND: case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR: - n->setNotFound(); + n->setNotFound(nullptr); break; case NetworkController::NC_ERROR_ACCESS_DENIED: - n->setAccessDenied(); + n->setAccessDenied(nullptr); break; case NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED: { fprintf(stderr, "\n\nGot auth required\n\n"); diff --git a/service/OneService.cpp b/service/OneService.cpp index 89734a690..cc579a61a 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -250,7 +250,10 @@ public: char nwbuf[17] = {}; const char* nwid = Utils::hex(nwc->nwid, nwbuf); fprintf(stderr, "NetworkState::setConfig(%s)\n", nwid); + + fprintf(stderr, "issuerUrl before: %s\n", nwc->issuerURL); memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); + fprintf(stderr, "issuerUrl after: %s\n", _config.issuerURL); fprintf(stderr, "ssoEnabled: %s, ssoVersion: %d\n", _config.ssoEnabled ? "true" : "false", _config.ssoVersion); @@ -2662,8 +2665,9 @@ public: // After setting up tap, fall through to CONFIG_UPDATE since we also want to do this... case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: + fprintf(stderr, "conf update issuerURL: %s\n", nwc->issuerURL); n.setConfig(nwc); - + if (n.tap()) { // sanity check #if defined(__WINDOWS__) && !defined(ZT_SDK) // wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized From 663a09b38de3e16b0919e8d7dd992f77c5266724 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Dec 2021 13:01:32 -0800 Subject: [PATCH 41/68] oidc stuff coming across the wire properly and generating a working login URL --- controller/PostgreSQL.cpp | 8 +++++--- node/IncomingPacket.cpp | 2 -- node/Network.cpp | 1 - service/OneService.cpp | 7 +++---- zeroidc/src/ext.rs | 8 ++++++-- zeroidc/src/lib.rs | 15 ++++++++++++--- 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 86ffe6855..28bd7b844 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -415,18 +415,20 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str exit(6); } - r = w.exec_params("SELECT org.client_id, org.authorization_endpoint, org.sso_impl_version " + r = w.exec_params("SELECT org.client_id, org.authorization_endpoint, org.issuer, org.sso_impl_version " "FROM ztc_network AS nw, ztc_org AS org " "WHERE nw.id = $1 AND nw.sso_enabled = true AND org.owner_id = nw.owner_id", networkId); std::string client_id = ""; std::string authorization_endpoint = ""; + std::string issuer = ""; uint64_t sso_version = 0; if (r.size() == 1) { client_id = r.at(0)[0].as(); authorization_endpoint = r.at(0)[1].as(); - sso_version = r.at(0)[2].as(); + issuer = r.at(0)[2].as(); + sso_version = r.at(0)[3].as(); } else if (r.size() > 1) { fprintf(stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n", networkId.c_str()); } else { @@ -455,7 +457,7 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str info.authenticationURL = std::string(url); } else if (info.version == 1) { info.ssoClientID = client_id; - info.issuerURL = authorization_endpoint; + info.issuerURL = issuer; info.ssoNonce = nonce; info.ssoState = std::string(state_hex); info.centralAuthURL = redirectURL; diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index f875d917b..62320a7d1 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -221,7 +221,6 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar if (authInfo.get(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, issuerURL, sizeof(issuerURL)) > 0) { issuerURL[sizeof(issuerURL) - 1] = 0; - fprintf(stderr, "Issuer URL from info: %s\n", issuerURL); } if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, centralAuthURL, sizeof(centralAuthURL))>0) { centralAuthURL[sizeof(centralAuthURL) - 1] = 0; @@ -236,7 +235,6 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar ssoClientID[sizeof(ssoClientID) - 1] = 0; } - fprintf(stderr, "Setting auth required on network\n"); network->setAuthenticationRequired(tPtr, issuerURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); } } diff --git a/node/Network.cpp b/node/Network.cpp index a8fa5ebca..c77f94a6d 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1555,7 +1555,6 @@ void Network::setAuthenticationRequired(void *tPtr, const char* issuerURL, const _config.ssoEnabled = true; _config.ssoVersion = 1; - fprintf(stderr, "Network::setAuthenticationRequired issuerURL: %s\n", issuerURL); Utils::scopy(_config.issuerURL, sizeof(_config.issuerURL), issuerURL); Utils::scopy(_config.centralAuthURL, sizeof(_config.centralAuthURL), centralEndpoint); Utils::scopy(_config.ssoClientID, sizeof(_config.ssoClientID), clientID); diff --git a/service/OneService.cpp b/service/OneService.cpp index cc579a61a..cd6e524ce 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -251,9 +251,7 @@ public: const char* nwid = Utils::hex(nwc->nwid, nwbuf); fprintf(stderr, "NetworkState::setConfig(%s)\n", nwid); - fprintf(stderr, "issuerUrl before: %s\n", nwc->issuerURL); memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); - fprintf(stderr, "issuerUrl after: %s\n", _config.issuerURL); fprintf(stderr, "ssoEnabled: %s, ssoVersion: %d\n", _config.ssoEnabled ? "true" : "false", _config.ssoVersion); @@ -443,7 +441,9 @@ static void _networkToJson(nlohmann::json &nj,NetworkState &ns) } nj["dns"] = m; if (ns.config().ssoEnabled) { - nj["authenticationURL"] = ns.getAuthURL(); + const char* authURL = ns.getAuthURL(); + fprintf(stderr, "Auth URL: %s\n", authURL); + nj["authenticationURL"] = authURL; nj["authenticationExpiryTime"] = ns.config().authenticationExpiryTime; nj["ssoEnabled"] = ns.config().ssoEnabled; } @@ -2665,7 +2665,6 @@ public: // After setting up tap, fall through to CONFIG_UPDATE since we also want to do this... case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: - fprintf(stderr, "conf update issuerURL: %s\n", nwc->issuerURL); n.setConfig(nwc); if (n.tap()) { // sanity check diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index ab69829ac..d6f451378 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -156,10 +156,14 @@ pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) { #[no_mangle] pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { + if ptr.is_null() { + println!("passed a null object"); + return std::ptr::null_mut(); + } let ai = unsafe { - assert!(!ptr.is_null()); &mut *ptr }; + let s = CString::new(ai.url.to_string()).unwrap(); - return s.as_ptr(); + return s.into_raw(); } diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index a0dca8892..1a744e45c 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -71,7 +71,10 @@ impl ZeroIDC { let provider_meta = match CoreProviderMetadata::discover(&iss, http_client) { Ok(m) => m, - Err(e) => return Err(e.to_string()), + Err(e) => { + println!("Error discovering provider metadata"); + return Err(e.to_string()); + }, }; let r = format!("http://localhost:{}/sso", local_web_port); @@ -85,7 +88,10 @@ impl ZeroIDC { let redirect = match RedirectUrl::new(redir_url.to_string()) { Ok(s) => s, - Err(e) => return Err(e.to_string()), + Err(e) => { + println!("Error generating RedirectURL instance from string: {}", redir_url.to_string()); + return Err(e.to_string()); + } }; (*idc.inner.lock().unwrap()).oidc_client = Some( @@ -152,13 +158,16 @@ impl ZeroIDC { csrf_func(csrf_token), nonce_func(nonce), ) - .add_scope(Scope::new("read".to_string())) + .add_scope(Scope::new("profile".to_string())) + .add_scope(Scope::new("email".to_string())) .add_scope(Scope::new("offline_access".to_string())) .add_scope(Scope::new("openid".to_string())) .set_pkce_challenge(pkce_challenge) .add_extra_param("network_id", network_id) .url(); + println!("URL: {}", auth_url); + return AuthInfo { url: auth_url, csrf_token, From 730482e62f39a6b8a6fcd16e722f24fbc2278299 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Dec 2021 15:02:21 -0800 Subject: [PATCH 42/68] encode network ID into sso state param --- controller/PostgreSQL.cpp | 2 +- service/OneService.cpp | 14 ++--- zeroidc/Cargo.lock | 123 ++++++++++---------------------------- zeroidc/Cargo.toml | 2 +- zeroidc/src/ext.rs | 63 +++++++++++++++++++ 5 files changed, 101 insertions(+), 103 deletions(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 28bd7b844..66075fd1e 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -459,7 +459,7 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str info.ssoClientID = client_id; info.issuerURL = issuer; info.ssoNonce = nonce; - info.ssoState = std::string(state_hex); + info.ssoState = std::string(state_hex) + "_" +networkId; info.centralAuthURL = redirectURL; fprintf( stderr, diff --git a/service/OneService.cpp b/service/OneService.cpp index cd6e524ce..26636351c 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1648,7 +1648,12 @@ public: fprintf(stderr, "sso get\n"); fprintf(stderr, "path: %s\n", path.c_str()); fprintf(stderr, "body: %s\n", body.c_str()); - scode = 200; scode = 200; + + const char* state = zeroidc::zeroidc_get_state_param_value(path.c_str()); + const char* nwid = zeroidc::zeroidc_network_id_from_state(state); + fprintf(stderr, "state: %s\n", state); + fprintf(stderr, "nwid: %s\n", nwid); + scode = 200; } else { scode = 401; // isAuth == false && !sso } @@ -1791,13 +1796,6 @@ public: scode = _controller->handleControlPlaneHttpPOST(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); else scode = 404; } - - } else if (ps[0] == "sso") { - // sso post handling - fprintf(stderr, "sso post\n"); - fprintf(stderr, "path: %s\n", path.c_str()); - fprintf(stderr, "body: %s\n", body.c_str()); - scode = 200; } else { scode = 401; // isAuth == false diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index 24479eaaf..e729b2147 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -28,12 +28,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.0" @@ -108,7 +102,6 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time", "winapi", ] @@ -251,17 +244,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.3" @@ -271,7 +253,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -512,6 +494,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -547,11 +540,11 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e47cfc4c0a1a519d9a025ebfbac3a2439d1b5cdf397d72dcb79b11d9920dab" dependencies = [ - "base64 0.13.0", + "base64", "chrono", - "getrandom 0.2.3", + "getrandom", "http", - "rand 0.8.4", + "rand", "reqwest", "serde", "serde_json", @@ -575,17 +568,18 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openidconnect" -version = "2.1.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a42ff51db0f23ae55dd6f234a15ed7bb468bc97938670693a3eaa42869110167" +checksum = "7d523cf32bdf7696f36bc4198a42c34b65f0227b97f2f501ebfbe016baa5bc52" dependencies = [ - "base64 0.12.3", + "base64", "chrono", "http", "itertools", "log", + "num-bigint", "oauth2", - "rand 0.7.3", + "rand", "ring", "serde", "serde-value", @@ -660,19 +654,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - [[package]] name = "rand" version = "0.8.4" @@ -680,19 +661,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", - "rand_hc 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] @@ -702,16 +673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -720,16 +682,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.3", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -738,7 +691,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ - "rand_core 0.6.3", + "rand_core", ] [[package]] @@ -765,7 +718,7 @@ version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" dependencies = [ - "base64 0.13.0", + "base64", "bytes", "encoding_rs", "futures-core", @@ -816,7 +769,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64 0.13.0", + "base64", "log", "ring", "sct", @@ -961,7 +914,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if", "libc", - "rand 0.8.4", + "rand", "redox_syscall", "remove_dir_all", "winapi", @@ -996,16 +949,6 @@ dependencies = [ "syn", ] -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "tinyvec" version = "1.5.0" @@ -1183,12 +1126,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" @@ -1325,7 +1262,7 @@ dependencies = [ name = "zeroidc" version = "0.1.0" dependencies = [ - "base64 0.13.0", + "base64", "cbindgen", "openidconnect", "url", diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 41ac73029..28f96b774 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["staticlib","rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -openidconnect = "2.1.0" +openidconnect = "2.1.2" base64 = "0.13.0" url = "2.2.2" diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index d6f451378..22e7d9418 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -1,5 +1,6 @@ use std::ffi::{CStr, CString}; use std::os::raw::c_char; +use url::{Url, ParseError}; use crate::{AuthInfo, ZeroIDC}; @@ -167,3 +168,65 @@ pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { let s = CString::new(ai.url.to_string()).unwrap(); return s.into_raw(); } + +#[no_mangle] +pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, ai: *mut AuthInfo, code: *const c_char ) { + if idc.is_null() { + println!("idc is null"); + return + } + if ai.is_null() { + println!("ai is null"); + return + } + let idc = unsafe { + &mut *idc + }; + let ai = unsafe { + &mut *ai + }; + + +} + +#[no_mangle] +pub extern "C" fn zeroidc_get_state_param_value(path: *const c_char) -> *const c_char { + if path.is_null() { + println!("path is null"); + return std::ptr::null(); + } + + let path = unsafe {CStr::from_ptr(path)}.to_str().unwrap(); + + let url = "http://localhost:9993".to_string() + path; + let url = Url::parse(&url).unwrap(); + + let mut pairs = url.query_pairs(); + for p in pairs { + if p.0 == "state" { + let s = CString::new(p.1.into_owned()).unwrap(); + return s.into_raw() + } + } + + return std::ptr::null(); +} + +#[no_mangle] +pub extern "C" fn zeroidc_network_id_from_state(state: *const c_char) -> *const c_char { + if state.is_null() { + println!("state is null"); + return std::ptr::null(); + } + + let state = unsafe{CStr::from_ptr(state)}.to_str().unwrap(); + + let split = state.split("_"); + let split = split.collect::>(); + if split.len() != 2 { + return std::ptr::null(); + } + + let s = CString::new(split[1]).unwrap(); + return s.into_raw(); +} \ No newline at end of file From 4ce810b42155ffb8c6fc683656d81853cb2e9e68 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Dec 2021 16:57:18 -0800 Subject: [PATCH 43/68] On our way to processing tokens --- service/OneService.cpp | 33 +++++++++++++++++++++++++++++++-- zeroidc/src/ext.rs | 19 ++++++++++++++----- zeroidc/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 26636351c..5b0aaf68f 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -321,6 +321,25 @@ public: return ""; } + void doTokenExchange(const char *code) { + if (_ainfo == nullptr || _idc == nullptr) { + fprintf(stderr, "ainfo or idc null\n"); + return; + } + + zeroidc::zeroidc_token_exchange(_idc, _ainfo, code); + zeroidc::zeroidc_auth_info_delete(_ainfo); + _ainfo = zeroidc::zeroidc_get_auth_info( + _idc, + _config.ssoState, + _config.ssoNonce + ); + + const char* url = zeroidc::zeroidc_get_auth_url(_ainfo); + memcpy(_config.authenticationURL, url, strlen(url)); + _config.authenticationURL[strlen(url)] = 0; + } + private: unsigned int _webPort; std::shared_ptr _tap; @@ -1649,11 +1668,21 @@ public: fprintf(stderr, "path: %s\n", path.c_str()); fprintf(stderr, "body: %s\n", body.c_str()); - const char* state = zeroidc::zeroidc_get_state_param_value(path.c_str()); + const char* state = zeroidc::zeroidc_get_url_param_value("state", path.c_str()); const char* nwid = zeroidc::zeroidc_network_id_from_state(state); fprintf(stderr, "state: %s\n", state); fprintf(stderr, "nwid: %s\n", nwid); - scode = 200; + + const uint64_t id = Utils::hexStrToU64(nwid); + Mutex::Lock l(_nets_m); + if (_nets.find(id) != _nets.end()) { + NetworkState& ns = _nets[id]; + const char* code = zeroidc::zeroidc_get_url_param_value("code", path.c_str()); + ns.doTokenExchange(code); + scode = 200; + } else { + scode = 404; + } } else { scode = 401; // isAuth == false && !sso } diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index 22e7d9418..edf7eb56b 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -179,23 +179,32 @@ pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, ai: *mut AuthInfo, c println!("ai is null"); return } + if code.is_null() { + println!("code is null"); + return + } let idc = unsafe { &mut *idc }; let ai = unsafe { &mut *ai }; + let code = unsafe{CStr::from_ptr(code)}.to_str().unwrap(); - + idc.do_token_exchange(ai, code); } #[no_mangle] -pub extern "C" fn zeroidc_get_state_param_value(path: *const c_char) -> *const c_char { +pub extern "C" fn zeroidc_get_url_param_value(param: *const c_char, path: *const c_char) -> *const c_char { + if param.is_null() { + println!("param is null"); + return std::ptr::null(); + } if path.is_null() { println!("path is null"); return std::ptr::null(); } - + let param = unsafe {CStr::from_ptr(param)}.to_str().unwrap(); let path = unsafe {CStr::from_ptr(path)}.to_str().unwrap(); let url = "http://localhost:9993".to_string() + path; @@ -203,7 +212,7 @@ pub extern "C" fn zeroidc_get_state_param_value(path: *const c_char) -> *const c let mut pairs = url.query_pairs(); for p in pairs { - if p.0 == "state" { + if p.0 == param { let s = CString::new(p.1.into_owned()).unwrap(); return s.into_raw() } @@ -229,4 +238,4 @@ pub extern "C" fn zeroidc_network_id_from_state(state: *const c_char) -> *const let s = CString::new(split[1]).unwrap(); return s.into_raw(); -} \ No newline at end of file +} diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 1a744e45c..48b364e5b 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -10,10 +10,11 @@ use std::time::Duration; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; -use openidconnect::{AuthenticationFlow, PkceCodeVerifier}; -use openidconnect::{ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, Scope}; +use openidconnect::{AuthenticationFlow, PkceCodeVerifier, TokenResponse, OAuth2TokenResponse}; +use openidconnect::{AuthorizationCode, ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, RequestTokenError, Scope}; use url::Url; +use std::borrow::BorrowMut; pub struct ZeroIDC { inner: Arc>, @@ -39,7 +40,7 @@ pub struct AuthInfo { url: Url, csrf_token: CsrfToken, nonce: Nonce, - pkce_verifier: PkceCodeVerifier, + pkce_verifier: Option, } impl ZeroIDC { @@ -147,6 +148,36 @@ impl ZeroIDC { return (*self.inner.lock().unwrap()).network_id.clone() } + fn do_token_exchange(&mut self, auth_info: &mut AuthInfo, code: &str) { + if let Some(verifier) = auth_info.pkce_verifier.take() { + let token_response = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { + let r = c.exchange_code(AuthorizationCode::new(code.to_string())) + .set_pkce_verifier(verifier) + .request(http_client); + match r { + Ok(res) =>{ + return Some(res); + }, + Err(e) => { + println!("token response error"); + return None; + }, + } + }); + // TODO: do stuff with token response + if let Some(Some(tok)) = token_response { + let id_token = tok.id_token().unwrap(); + let claims = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { + + }); + let access_token = tok.access_token(); + let refresh_token = tok.refresh_token(); + } + } else { + println!("No pkce verifier! Can't exchange tokens!!!"); + } + } + fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option { let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); let network_id = self.get_network_id(); @@ -170,9 +201,9 @@ impl ZeroIDC { return AuthInfo { url: auth_url, + pkce_verifier: Some(pkce_verifier), csrf_token, nonce, - pkce_verifier, }; }); From 7a8c89be9d9d792c7d0708b15f92e2520ca6f644 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 3 Dec 2021 09:57:39 -0800 Subject: [PATCH 44/68] Post ID token to Central --- zeroidc/Cargo.lock | 237 ++++++++++++++++++++++++++++++++++++--------- zeroidc/Cargo.toml | 1 + zeroidc/src/ext.rs | 2 +- zeroidc/src/lib.rs | 33 ++++++- 4 files changed, 223 insertions(+), 50 deletions(-) diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index e729b2147..77e65a637 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -120,6 +120,22 @@ dependencies = [ "vec_map", ] +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.1" @@ -159,6 +175,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -190,19 +221,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" -[[package]] -name = "futures-macro" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" -dependencies = [ - "autocfg", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "futures-sink" version = "0.3.17" @@ -224,13 +242,10 @@ dependencies = [ "autocfg", "futures-core", "futures-io", - "futures-macro", "futures-task", "memchr", "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] @@ -360,17 +375,28 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" dependencies = [ - "futures-util", + "http", "hyper", - "log", "rustls", "tokio", "tokio-rustls", - "webpki", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", ] [[package]] @@ -485,6 +511,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -591,6 +635,39 @@ dependencies = [ "url", ] +[[package]] +name = "openssl" +version = "0.10.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + +[[package]] +name = "openssl-sys" +version = "0.9.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "1.1.1" @@ -618,24 +695,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" + [[package]] name = "ppv-lite86" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" version = "1.0.32" @@ -714,9 +785,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" +checksum = "07bea77bc708afa10e59905c3d4af7c8fd43c9214251673095ff8b14345fcbc5" dependencies = [ "base64", "bytes", @@ -727,18 +798,22 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "rustls", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "url", "wasm-bindgen", @@ -765,15 +840,23 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" dependencies = [ - "base64", "log", "ring", "sct", - "webpki", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64", ] [[package]] @@ -783,15 +866,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] -name = "sct" -version = "0.6.1" +name = "schannel" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", ] +[[package]] +name = "security-framework" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.130" @@ -981,14 +1097,24 @@ dependencies = [ ] [[package]] -name = "tokio-rustls" -version = "0.22.0" +name = "tokio-native-tls" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4baa378e417d780beff82bf54ceb0d195193ea6a00c14e22359e7f39456b5689" dependencies = [ "rustls", "tokio", - "webpki", + "webpki 0.22.0", ] [[package]] @@ -1104,6 +1230,12 @@ dependencies = [ "serde", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" @@ -1218,13 +1350,23 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "webpki-roots" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ - "webpki", + "webpki 0.21.4", ] [[package]] @@ -1265,5 +1407,6 @@ dependencies = [ "base64", "cbindgen", "openidconnect", + "reqwest", "url", ] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 28f96b774..912631f33 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -15,6 +15,7 @@ crate-type = ["staticlib","rlib"] openidconnect = "2.1.2" base64 = "0.13.0" url = "2.2.2" +reqwest = "0.11.7" [build-dependencies] cbindgen = "0.20.0" diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index edf7eb56b..87db679a5 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -210,7 +210,7 @@ pub extern "C" fn zeroidc_get_url_param_value(param: *const c_char, path: *const let url = "http://localhost:9993".to_string() + path; let url = Url::parse(&url).unwrap(); - let mut pairs = url.query_pairs(); + let pairs = url.query_pairs(); for p in pairs { if p.0 == param { let s = CString::new(p.1.into_owned()).unwrap(); diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 48b364e5b..14e004f7d 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -13,8 +13,10 @@ use openidconnect::reqwest::http_client; use openidconnect::{AuthenticationFlow, PkceCodeVerifier, TokenResponse, OAuth2TokenResponse}; use openidconnect::{AuthorizationCode, ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, RequestTokenError, Scope}; +use reqwest::blocking::Client; + use url::Url; -use std::borrow::BorrowMut; + pub struct ZeroIDC { inner: Arc>, @@ -159,7 +161,8 @@ impl ZeroIDC { return Some(res); }, Err(e) => { - println!("token response error"); + println!("token response error: {}", e.to_string()); + return None; }, } @@ -167,11 +170,37 @@ impl ZeroIDC { // TODO: do stuff with token response if let Some(Some(tok)) = token_response { let id_token = tok.id_token().unwrap(); + println!("ID token: {}", id_token.to_string()); + + let split = auth_info.csrf_token.secret().split("_"); + let split = split.collect::>(); + + let params = [("id_token", id_token.to_string()),("state", split[0].to_string())]; + let client = reqwest::blocking::Client::new(); + let res = client.post((*self.inner.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); + + match res { + Ok(res) => { + println!("hit url: {}", res.url().as_str()); + println!("Status: {}", res.status()); + }, + Err(res) => { + println!("hit url: {}", res.url().unwrap().as_str()); + println!("Status: {}", res.status().unwrap()); + println!("Post error: {}", res.to_string()); + } + } + let claims = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { }); let access_token = tok.access_token(); + println!("Access Token: {}", access_token.secret()); + let refresh_token = tok.refresh_token(); + println!("Refresh Token: {}", refresh_token.unwrap().secret()); } } else { println!("No pkce verifier! Can't exchange tokens!!!"); From 43c528fdb6a5f7a5b09106f6ec2fc05169dd9d08 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 3 Dec 2021 11:32:29 -0800 Subject: [PATCH 45/68] Get expiry time out of access token & propagate --- service/OneService.cpp | 11 ++++- zeroidc/Cargo.lock | 93 +++++++++++++++++++++++++++++++++++++++--- zeroidc/Cargo.toml | 2 + zeroidc/src/ext.rs | 10 +++++ zeroidc/src/lib.rs | 40 +++++++++++++----- 5 files changed, 139 insertions(+), 17 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 5b0aaf68f..2aeafc4b7 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -340,6 +340,15 @@ public: _config.authenticationURL[strlen(url)] = 0; } + uint64_t getExpiryTime() { + if (_idc == nullptr) { + fprintf(stderr, "idc is null\n"); + return 0; + } + + return zeroidc::zeroidc_get_exp_time(_idc); + } + private: unsigned int _webPort; std::shared_ptr _tap; @@ -463,7 +472,7 @@ static void _networkToJson(nlohmann::json &nj,NetworkState &ns) const char* authURL = ns.getAuthURL(); fprintf(stderr, "Auth URL: %s\n", authURL); nj["authenticationURL"] = authURL; - nj["authenticationExpiryTime"] = ns.config().authenticationExpiryTime; + nj["authenticationExpiryTime"] = (ns.getExpiryTime()*1000); nj["ssoEnabled"] = ns.config().ssoEnabled; } } diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index 77e65a637..d8c0135f2 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -28,6 +28,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.0" @@ -102,6 +108,7 @@ dependencies = [ "num-integer", "num-traits", "serde", + "time", "winapi", ] @@ -450,6 +457,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" +dependencies = [ + "base64 0.12.3", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -538,6 +559,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -584,7 +616,7 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e47cfc4c0a1a519d9a025ebfbac3a2439d1b5cdf397d72dcb79b11d9920dab" dependencies = [ - "base64", + "base64 0.13.0", "chrono", "getrandom", "http", @@ -616,12 +648,12 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d523cf32bdf7696f36bc4198a42c34b65f0227b97f2f501ebfbe016baa5bc52" dependencies = [ - "base64", + "base64 0.13.0", "chrono", "http", "itertools", "log", - "num-bigint", + "num-bigint 0.4.3", "oauth2", "rand", "ring", @@ -677,6 +709,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64 0.13.0", + "once_cell", + "regex", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -774,6 +817,21 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -789,7 +847,7 @@ version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bea77bc708afa10e59905c3d4af7c8fd43c9214251673095ff8b14345fcbc5" dependencies = [ - "base64", + "base64 0.13.0", "bytes", "encoding_rs", "futures-core", @@ -856,7 +914,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" dependencies = [ - "base64", + "base64 0.13.0", ] [[package]] @@ -983,6 +1041,17 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "simple_asn1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +dependencies = [ + "chrono", + "num-bigint 0.2.6", + "num-traits", +] + [[package]] name = "slab" version = "0.4.5" @@ -1065,6 +1134,16 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "tinyvec" version = "1.5.0" @@ -1404,9 +1483,11 @@ dependencies = [ name = "zeroidc" version = "0.1.0" dependencies = [ - "base64", + "base64 0.13.0", "cbindgen", + "jsonwebtoken", "openidconnect", "reqwest", + "serde", "url", ] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 912631f33..ae4063425 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -16,6 +16,8 @@ openidconnect = "2.1.2" base64 = "0.13.0" url = "2.2.2" reqwest = "0.11.7" +jsonwebtoken = "7.2.0" +serde = "1.0.130" [build-dependencies] cbindgen = "0.20.0" diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index 87db679a5..c7a3095d4 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -91,6 +91,16 @@ pub extern "C" fn zeroidc_is_running(ptr: *mut ZeroIDC) -> bool { idc.is_running() } +#[no_mangle] +pub extern "C" fn zeroidc_get_exp_time(ptr: *mut ZeroIDC) -> u64 { + let id = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + + id.get_exp_time() +} + #[no_mangle] pub extern "C" fn zeroidc_process_form_post(ptr: *mut ZeroIDC, body: *const c_char) -> bool { let idc = unsafe { diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 14e004f7d..7181f6854 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -7,17 +7,14 @@ extern crate url; use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn, JoinHandle}; use std::time::Duration; - +use serde::{Deserialize, Serialize}; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; -use openidconnect::{AuthenticationFlow, PkceCodeVerifier, TokenResponse, OAuth2TokenResponse}; -use openidconnect::{AuthorizationCode, ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, RequestTokenError, Scope}; - -use reqwest::blocking::Client; +use openidconnect::{AccessToken, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; +use jsonwebtoken::{dangerous_insecure_decode}; use url::Url; - pub struct ZeroIDC { inner: Arc>, } @@ -28,6 +25,14 @@ struct Inner { auth_endpoint: String, oidc_thread: Option>, oidc_client: Option, + access_token: Option, + refresh_token: Option, + exp_time: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Exp { + exp: u64 } fn csrf_func(csrf_token: String) -> Box CsrfToken> { @@ -60,6 +65,9 @@ impl ZeroIDC { auth_endpoint: auth_ep.to_string(), oidc_thread: None, oidc_client: None, + access_token: None, + refresh_token: None, + exp_time: 0, })), }; @@ -147,7 +155,11 @@ impl ZeroIDC { } fn get_network_id(&mut self) -> String { - return (*self.inner.lock().unwrap()).network_id.clone() + return (*self.inner.lock().unwrap()).network_id.clone(); + } + + fn get_exp_time(&mut self) -> u64 { + return (*self.inner.lock().unwrap()).exp_time; } fn do_token_exchange(&mut self, auth_info: &mut AuthInfo, code: &str) { @@ -185,6 +197,17 @@ impl ZeroIDC { Ok(res) => { println!("hit url: {}", res.url().as_str()); println!("Status: {}", res.status()); + + let at = tok.access_token().secret(); + let exp = dangerous_insecure_decode::(&at); + if let Ok(e) = exp { + (*self.inner.lock().unwrap()).exp_time = e.claims.exp + } + + (*self.inner.lock().unwrap()).access_token = Some(tok.access_token().clone()); + if let Some(t) = tok.refresh_token() { + (*self.inner.lock().unwrap()).refresh_token = Some(t.clone()); + } }, Err(res) => { println!("hit url: {}", res.url().unwrap().as_str()); @@ -193,9 +216,6 @@ impl ZeroIDC { } } - let claims = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { - - }); let access_token = tok.access_token(); println!("Access Token: {}", access_token.secret()); From 1192b1b42289e22feffbbde3278f24c6ed9e5f68 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 3 Dec 2021 15:44:04 -0800 Subject: [PATCH 46/68] refresh token run loop Need central-side work to complete --- node/NetworkConfig.cpp | 1 - service/OneService.cpp | 16 +++---- zeroidc/Cargo.lock | 13 +++++- zeroidc/Cargo.toml | 1 + zeroidc/src/ext.rs | 30 ++++++------ zeroidc/src/lib.rs | 103 +++++++++++++++++++++++++++++++++++++++-- 6 files changed, 135 insertions(+), 29 deletions(-) diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 2d5e2a382..13a9313aa 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -411,7 +411,6 @@ bool NetworkConfig::fromDictionary(const DictionaryissuerURL, (unsigned int)sizeof(this->issuerURL)) > 0) { this->issuerURL[sizeof(this->issuerURL) - 1] = 0; - fprintf(stderr, "Loaded issuer url: %s\n", this->issuerURL); } if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL, (unsigned int)sizeof(this->centralAuthURL)) > 0) { this->centralAuthURL[sizeof(this->centralAuthURL) - 1] = 0; diff --git a/service/OneService.cpp b/service/OneService.cpp index 2aeafc4b7..04442ab9b 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -249,23 +249,23 @@ public: void setConfig(const ZT_VirtualNetworkConfig *nwc) { char nwbuf[17] = {}; const char* nwid = Utils::hex(nwc->nwid, nwbuf); - fprintf(stderr, "NetworkState::setConfig(%s)\n", nwid); + // fprintf(stderr, "NetworkState::setConfig(%s)\n", nwid); memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); - fprintf(stderr, "ssoEnabled: %s, ssoVersion: %d\n", - _config.ssoEnabled ? "true" : "false", _config.ssoVersion); + // fprintf(stderr, "ssoEnabled: %s, ssoVersion: %d\n", + // _config.ssoEnabled ? "true" : "false", _config.ssoVersion); if (_config.ssoEnabled && _config.ssoVersion == 1) { - fprintf(stderr, "ssoEnabled for %s\n", nwid); + // fprintf(stderr, "ssoEnabled for %s\n", nwid); if (_idc == nullptr) { assert(_config.issuerURL != nullptr); assert(_config.ssoClientID != nullptr); assert(_config.centralAuthURL != nullptr); - fprintf(stderr, "Issuer URL: %s\n", _config.issuerURL); - fprintf(stderr, "Client ID: %s\n", _config.ssoClientID); - fprintf(stderr, "Central Auth URL: %s\n", _config.centralAuthURL); + // fprintf(stderr, "Issuer URL: %s\n", _config.issuerURL); + // fprintf(stderr, "Client ID: %s\n", _config.ssoClientID); + // fprintf(stderr, "Central Auth URL: %s\n", _config.centralAuthURL); char buf[17] = {}; _idc = zeroidc::zeroidc_new( @@ -281,7 +281,7 @@ public: return; } - fprintf(stderr, "idc created (%s, %s, %s)\n", _config.issuerURL, _config.ssoClientID, _config.centralAuthURL); + // fprintf(stderr, "idc created (%s, %s, %s)\n", _config.issuerURL, _config.ssoClientID, _config.centralAuthURL); } if (_ainfo != nullptr) { diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index d8c0135f2..2447d8d21 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -108,7 +108,7 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time", + "time 0.1.43", "winapi", ] @@ -1144,6 +1144,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "time" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" +dependencies = [ + "itoa", + "libc", +] + [[package]] name = "tinyvec" version = "1.5.0" @@ -1489,5 +1499,6 @@ dependencies = [ "openidconnect", "reqwest", "serde", + "time 0.3.5", "url", ] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index ae4063425..058564be5 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -18,6 +18,7 @@ url = "2.2.2" reqwest = "0.11.7" jsonwebtoken = "7.2.0" serde = "1.0.130" +time = { version = "0.3.5", features = ["formatting"] } [build-dependencies] cbindgen = "0.20.0" diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index c7a3095d4..f79f0f6f5 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -1,6 +1,6 @@ use std::ffi::{CStr, CString}; use std::os::raw::c_char; -use url::{Url, ParseError}; +use url::{Url}; use crate::{AuthInfo, ZeroIDC}; @@ -101,23 +101,23 @@ pub extern "C" fn zeroidc_get_exp_time(ptr: *mut ZeroIDC) -> u64 { id.get_exp_time() } -#[no_mangle] -pub extern "C" fn zeroidc_process_form_post(ptr: *mut ZeroIDC, body: *const c_char) -> bool { - let idc = unsafe { - assert!(!ptr.is_null()); - &mut *ptr - }; +// #[no_mangle] +// pub extern "C" fn zeroidc_process_form_post(ptr: *mut ZeroIDC, body: *const c_char) -> bool { +// let idc = unsafe { +// assert!(!ptr.is_null()); +// &mut *ptr +// }; - if body.is_null() { - println!("body is null"); - return false - } +// if body.is_null() { +// println!("body is null"); +// return false +// } - let body = unsafe { CStr::from_ptr(body) } - .to_str().unwrap().to_string(); +// let body = unsafe { CStr::from_ptr(body) } +// .to_str().unwrap().to_string(); - false -} +// false +// } #[no_mangle] pub extern "C" fn zeroidc_get_auth_info( diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 7181f6854..025585566 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -2,11 +2,13 @@ pub mod ext; extern crate base64; extern crate openidconnect; +extern crate time; extern crate url; +use std::time::{SystemTime, UNIX_EPOCH, Duration}; +use time::{OffsetDateTime, format_description}; use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn, JoinHandle}; -use std::time::Duration; use serde::{Deserialize, Serialize}; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; @@ -50,6 +52,23 @@ pub struct AuthInfo { pkce_verifier: Option, } +fn systemtime_strftime(dt: T, format: &str) -> String + where T: Into +{ + let f = format_description::parse(format); + match f { + Ok(f) => { + match dt.into().format(&f) { + Ok(s) => s, + Err(_e) => "".to_string(), + } + }, + Err(_e) => { + "".to_string() + }, + } +} + impl ZeroIDC { fn new( network_id: &str, @@ -124,10 +143,84 @@ impl ZeroIDC { let inner_local = Arc::clone(&self.inner); (*local.lock().unwrap()).oidc_thread = Some(spawn(move || { (*inner_local.lock().unwrap()).running = true; + let mut running = true; + + while running { + let exp = UNIX_EPOCH + Duration::from_secs((*inner_local.lock().unwrap()).exp_time); + let now = SystemTime::now(); + + println!("refresh token thread tick, now: {}, exp: {}", systemtime_strftime(now, "[year]-[month]-[day] [hour]:[minute]:[second]"), systemtime_strftime(exp, "[year]-[month]-[day] [hour]:[minute]:[second]")); + let refresh_token = (*inner_local.lock().unwrap()).refresh_token.clone(); + if let Some(refresh_token) = refresh_token { + if now >= (exp - Duration::from_secs(15)) { + let token_response = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { + let res = c.exchange_refresh_token(&refresh_token) + .request(http_client); + + res + + }); + + if let Some(res) = token_response { + if let Ok(res) = res { + let id_token = res.id_token(); + + if let Some(id_token) = id_token { + let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; + let client = reqwest::blocking::Client::new(); + let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); + + match r { + Ok(r) => { + if r.status().is_success() { + println!("hit url: {}", r.url().as_str()); + println!("status: {}", r.status()); + + + let access_token = res.access_token(); + let at = access_token.secret(); + let exp = dangerous_insecure_decode::(&at); + + if let Ok(e) = exp { + (*inner_local.lock().unwrap()).exp_time = e.claims.exp + } + + (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); + if let Some(t) = res.refresh_token() { + println!("New Refresh Token: {}", t.secret()); + (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); + } + println!("Central post succeeded"); + } else { + println!("Central post failed: {}", r.status().to_string()); + println!("hit url: {}", r.url().as_str()); + println!("Status: {}", r.status()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; + } + }, + Err(e) => { + println!("Central post failed: {}", e.to_string()); + println!("hit url: {}", e.url().unwrap().as_str()); + println!("Status: {}", e.status().unwrap()); + // (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; + } + } + } + } + } + } else { + println!("waiting to refresh"); + } + } else { + println!("no refresh token?"); + } - while (*inner_local.lock().unwrap()).running { - println!("tick"); sleep(Duration::from_secs(1)); + running = (*inner_local.lock().unwrap()).running; } println!("thread done!") @@ -207,12 +300,14 @@ impl ZeroIDC { (*self.inner.lock().unwrap()).access_token = Some(tok.access_token().clone()); if let Some(t) = tok.refresh_token() { (*self.inner.lock().unwrap()).refresh_token = Some(t.clone()); + self.start(); } }, Err(res) => { println!("hit url: {}", res.url().unwrap().as_str()); println!("Status: {}", res.status().unwrap()); println!("Post error: {}", res.to_string()); + (*self.inner.lock().unwrap()).exp_time = 0; } } @@ -246,7 +341,7 @@ impl ZeroIDC { .add_extra_param("network_id", network_id) .url(); - println!("URL: {}", auth_url); + // println!("URL: {}", auth_url); return AuthInfo { url: auth_url, From 5095d73de3a8b67f6a34fd7750b14ec88a81b95a Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 3 Dec 2021 16:32:27 -0800 Subject: [PATCH 47/68] moar better error handling --- zeroidc/src/lib.rs | 86 +++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 025585566..c9251950b 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -158,57 +158,65 @@ impl ZeroIDC { .request(http_client); res - }); if let Some(res) = token_response { - if let Ok(res) = res { - let id_token = res.id_token(); + match res { + Ok(res) => { + match res.id_token() { + Some(id_token) => { - if let Some(id_token) = id_token { - let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; - let client = reqwest::blocking::Client::new(); - let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) - .form(¶ms) - .send(); + let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; + let client = reqwest::blocking::Client::new(); + let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); - match r { - Ok(r) => { - if r.status().is_success() { - println!("hit url: {}", r.url().as_str()); - println!("status: {}", r.status()); + match r { + Ok(r) => { + if r.status().is_success() { + println!("hit url: {}", r.url().as_str()); + println!("status: {}", r.status()); - let access_token = res.access_token(); - let at = access_token.secret(); - let exp = dangerous_insecure_decode::(&at); - - if let Ok(e) = exp { - (*inner_local.lock().unwrap()).exp_time = e.claims.exp + let access_token = res.access_token(); + let at = access_token.secret(); + let exp = dangerous_insecure_decode::(&at); + + if let Ok(e) = exp { + (*inner_local.lock().unwrap()).exp_time = e.claims.exp + } + + (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); + if let Some(t) = res.refresh_token() { + println!("New Refresh Token: {}", t.secret()); + (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); + } + println!("Central post succeeded"); + } else { + println!("Central post failed: {}", r.status().to_string()); + println!("hit url: {}", r.url().as_str()); + println!("Status: {}", r.status()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; + } + }, + Err(e) => { + println!("Central post failed: {}", e.to_string()); + println!("hit url: {}", e.url().unwrap().as_str()); + println!("Status: {}", e.status().unwrap()); + // (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; } - - (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); - if let Some(t) = res.refresh_token() { - println!("New Refresh Token: {}", t.secret()); - (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); - } - println!("Central post succeeded"); - } else { - println!("Central post failed: {}", r.status().to_string()); - println!("hit url: {}", r.url().as_str()); - println!("Status: {}", r.status()); - (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; } }, - Err(e) => { - println!("Central post failed: {}", e.to_string()); - println!("hit url: {}", e.url().unwrap().as_str()); - println!("Status: {}", e.status().unwrap()); - // (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; + None => { + println!("No id token???"); } } + }, + Err(e) => { + println!("Error posting refresh token: {}", e) } } } From 48b39ab005e6ce3537a9f719015f915f2eb5f548 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 3 Dec 2021 17:46:37 -0800 Subject: [PATCH 48/68] removing comments --- node/IncomingPacket.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 62320a7d1..5fc38be02 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -204,7 +204,6 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar uint64_t authVer = authInfo.getUI(ZT_AUTHINFO_DICT_KEY_VERSION, 0ULL); if (authVer == 0) { - fprintf(stderr, "authVer == 1\n"); char authenticationURL[2048]; if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) { @@ -212,7 +211,6 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar network->setAuthenticationRequired(tPtr, authenticationURL); } } else if (authVer == 1) { - fprintf(stderr, "authVer == 2\n"); char issuerURL[2048] = { 0 }; char centralAuthURL[2048] = { 0 }; char ssoNonce[64] = { 0 }; From b3fbbd3124970308b52f82c9ccede12780e24703 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 7 Dec 2021 16:29:50 -0800 Subject: [PATCH 49/68] refresh tokens now working Still investigating the best way to do a couple things, but we have something working --- controller/EmbeddedNetworkController.cpp | 13 ++++++++++--- controller/PostgreSQL.cpp | 2 +- ext/central-controller-docker/Dockerfile.builder | 2 +- make-linux.mk | 2 +- zeroidc/src/lib.rs | 3 ++- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 6a8c9dd4b..054bcddc5 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1339,14 +1339,18 @@ void EmbeddedNetworkController::_request( bool memberSSOExempt = OSUtils::jsonBool(member["ssoExempt"], false); AuthInfo info; if (networkSSOEnabled && !memberSSOExempt) { + // TODO: Get expiry time if auth is still valid + + // else get new auth info & stuff info = _db.getSSOAuthInfo(member, _ssoRedirectURL); assert(info.enabled == networkSSOEnabled); std::string memberId = member["id"]; //fprintf(stderr, "ssoEnabled && !ssoExempt %s-%s\n", nwids, memberId.c_str()); uint64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0); - //fprintf(stderr, "authExpiryTime: %lld\n", authenticationExpiryTime); + fprintf(stderr, "authExpiryTime: %lld\n", authenticationExpiryTime); if (authenticationExpiryTime < now) { + fprintf(stderr, "Handling expired member\n"); if (info.version == 0) { if (!info.authenticationURL.empty()) { _db.networkMemberSSOHasExpired(nwid, now); @@ -1363,7 +1367,8 @@ void EmbeddedNetworkController::_request( _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); return; } - } else if (info.version == 1) { + } + else if (info.version == 1) { _db.networkMemberSSOHasExpired(nwid, now); onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); @@ -1381,10 +1386,12 @@ void EmbeddedNetworkController::_request( fprintf(stderr, "Sending NC_ERROR_AUTHENTICATION_REQUIRED\n"); _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); return; - } else { + } + else { fprintf(stderr, "invalid sso info.version %llu\n", info.version); } } else if (authorized) { + fprintf(stderr, "Setting member will expire to: %lld\n", authenticationExpiryTime); _db.memberWillExpire(authenticationExpiryTime, nwid, identity.address().toInt()); } } diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 66075fd1e..e199f05bb 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -463,7 +463,7 @@ AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::str info.centralAuthURL = redirectURL; fprintf( stderr, - "ssoClientID: %s\nissuerURL: %s\nssoNonce: %s\nssoState: %s\ncentralAuthURL: %s", + "ssoClientID: %s\nissuerURL: %s\nssoNonce: %s\nssoState: %s\ncentralAuthURL: %s\n", info.ssoClientID.c_str(), info.issuerURL.c_str(), info.ssoNonce.c_str(), diff --git a/ext/central-controller-docker/Dockerfile.builder b/ext/central-controller-docker/Dockerfile.builder index 573b1ef69..5c2787570 100644 --- a/ext/central-controller-docker/Dockerfile.builder +++ b/ext/central-controller-docker/Dockerfile.builder @@ -9,5 +9,5 @@ RUN yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x RUN dnf -qy module disable postgresql RUN yum -y install epel-release && yum -y update && yum clean all RUN yum groupinstall -y "Development Tools" && yum clean all -RUN yum install -y bash cmake postgresql10 postgresql10-devel clang jemalloc jemalloc-devel libpqxx libpqxx-devel && yum clean all +RUN yum install -y bash cmake postgresql10 postgresql10-devel clang jemalloc jemalloc-devel libpqxx libpqxx-devel openssl-devel && yum clean all RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/make-linux.mk b/make-linux.mk index 95567f2ea..b001d4ce2 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -274,7 +274,7 @@ endif ifeq ($(ZT_CONTROLLER),1) override CXXFLAGS+=-Wall -Wno-deprecated -std=c++17 -pthread $(INCLUDES) -DNDEBUG $(DEFS) - override LDLIBS+=-L/usr/pgsql-10/lib/ -lpqxx -lpq ext/hiredis-0.14.1/lib/centos8/libhiredis.a ext/redis-plus-plus-1.1.1/install/centos8/lib/libredis++.a + override LDLIBS+=-L/usr/pgsql-10/lib/ -lpqxx -lpq ext/hiredis-0.14.1/lib/centos8/libhiredis.a ext/redis-plus-plus-1.1.1/install/centos8/lib/libredis++.a -lssl -lcrypto override DEFS+=-DZT_CONTROLLER_USE_LIBPQ override INCLUDES+=-I/usr/pgsql-10/include -Iext/hiredis-0.14.1/include/ -Iext/redis-plus-plus-1.1.1/install/centos8/include/sw/ endif diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index c9251950b..bd9f96f3c 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -152,7 +152,7 @@ impl ZeroIDC { println!("refresh token thread tick, now: {}, exp: {}", systemtime_strftime(now, "[year]-[month]-[day] [hour]:[minute]:[second]"), systemtime_strftime(exp, "[year]-[month]-[day] [hour]:[minute]:[second]")); let refresh_token = (*inner_local.lock().unwrap()).refresh_token.clone(); if let Some(refresh_token) = refresh_token { - if now >= (exp - Duration::from_secs(15)) { + if now >= (exp - Duration::from_secs(30)) { let token_response = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { let res = c.exchange_refresh_token(&refresh_token) .request(http_client); @@ -167,6 +167,7 @@ impl ZeroIDC { Some(id_token) => { let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; + println!("New ID token: {}", id_token.to_string()); let client = reqwest::blocking::Client::new(); let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) .form(¶ms) From dc12bde0682cef8fb49a453b873549b4255e2c3c Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 8 Dec 2021 09:49:29 -0800 Subject: [PATCH 50/68] add vs makefile project to build oidc rust library --- windows/ZeroTierOne.sln | 2 + zeroidc/zeroidc.vcxproj | 106 ++++++++++++++++++++++++++++++++ zeroidc/zeroidc.vcxproj.filters | 25 ++++++++ 3 files changed, 133 insertions(+) create mode 100644 zeroidc/zeroidc.vcxproj create mode 100644 zeroidc/zeroidc.vcxproj.filters diff --git a/windows/ZeroTierOne.sln b/windows/ZeroTierOne.sln index ff142c600..7cb5f4b65 100644 --- a/windows/ZeroTierOne.sln +++ b/windows/ZeroTierOne.sln @@ -7,6 +7,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroTierOne", "ZeroTierOne\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TapDriver6", "TapDriver6\TapDriver6.vcxproj", "{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zeroidc", "..\zeroidc\zeroidc.vcxproj", "{175C340F-F5BA-4CB1-88AD-533B102E3799}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/zeroidc/zeroidc.vcxproj b/zeroidc/zeroidc.vcxproj new file mode 100644 index 000000000..9ab794c36 --- /dev/null +++ b/zeroidc/zeroidc.vcxproj @@ -0,0 +1,106 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {175C340F-F5BA-4CB1-88AD-533B102E3799} + Win32Proj + + + + Makefile + true + v142 + + + Makefile + false + v142 + + + Makefile + true + v142 + + + Makefile + false + v142 + + + + + + + + + + + + + + + + + + + + + cargo build --release --target=x86_64-pc-windows-msvc + + + cargo clean + cargo clean & cargo build --release --target=x86_64-pc-windows-msvc + NDEBUG;$(NMakePreprocessorDefinitions) + + + cargo build --target=i686-pc-windows-msvc + + + cargo clean + cargo clean & cargo build --target=i686-pc-windows-msvc + WIN32;_DEBUG;$(NMakePreprocessorDefinitions) + + + cargo build --target=x86_64-pc-windows-msvc + + + cargo clean + cargo clean & cargo build --target=x86_64-pc-windows-msvc + _DEBUG;$(NMakePreprocessorDefinitions) + + + cargo build --release --target=i686-pc-windows-msvc + + + cargo clean + cargo clean & cargo build --release --target=i686-pc-windows-msvc + WIN32;NDEBUG;$(NMakePreprocessorDefinitions) + + + + + + + + + + + \ No newline at end of file diff --git a/zeroidc/zeroidc.vcxproj.filters b/zeroidc/zeroidc.vcxproj.filters new file mode 100644 index 000000000..840b57a0d --- /dev/null +++ b/zeroidc/zeroidc.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + \ No newline at end of file From 2d4fe9e6d0c067559a612b275f89ceb191ffb28c Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 8 Dec 2021 12:14:15 -0800 Subject: [PATCH 51/68] Get oidc lib compiled into main binary on Windows --- service/OneService.cpp | 2 +- windows/ZeroTierOne.sln | 3 +++ windows/ZeroTierOne/ZeroTierOne.vcxproj | 36 ++++++++++++------------- zeroidc/zeroidc.vcxproj | 3 +++ zeroidc/zeroidc.vcxproj.filters | 5 ++++ 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 04442ab9b..1d5e8c564 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -3334,7 +3334,7 @@ public: return false; } #ifdef _WIN32 - if (n->second.tap->friendlyName() == ifname) + if (n->second.tap()->friendlyName() == ifname) return false; #endif } diff --git a/windows/ZeroTierOne.sln b/windows/ZeroTierOne.sln index 7cb5f4b65..8c33b612e 100644 --- a/windows/ZeroTierOne.sln +++ b/windows/ZeroTierOne.sln @@ -4,6 +4,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 16.0.30517.126 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroTierOne", "ZeroTierOne\ZeroTierOne.vcxproj", "{B00A4957-5977-4AC1-9EF4-571DC27EADA2}" + ProjectSection(ProjectDependencies) = postProject + {175C340F-F5BA-4CB1-88AD-533B102E3799} = {175C340F-F5BA-4CB1-88AD-533B102E3799} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TapDriver6", "TapDriver6\TapDriver6.vcxproj", "{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}" EndProject diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index 508dd99fb..6d370555f 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -328,8 +328,7 @@ Level3 Disabled true - - + $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) 4996 false @@ -338,8 +337,9 @@ true - wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;zeroidc.lib;bcrypt.lib;userenv.lib;crypt32.lib;secur32.lib;ncrypt.lib;ntdll.lib;%(AdditionalDependencies) false + $(SolutionDir)\..\zeroidc\target\i686-pc-windows-msvc\debug\;%(AdditionalLibraryDirectories) @@ -347,8 +347,7 @@ Level3 Disabled true - - + $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) 4996 false @@ -365,8 +364,7 @@ Level3 Disabled true - - + $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) true 4996 @@ -375,9 +373,10 @@ true - wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;zeroidc.lib;bcrypt.lib;userenv.lib;crypt32.lib;secur32.lib;ncrypt.lib;ntdll.lib;%(AdditionalDependencies) false "notelemetry.obj" %(AdditionalOptions) + $(SolutionDir)..\zeroidc\target\x86_64-pc-windows-msvc\debug\;%(AdditionalLibraryDirectories) @@ -385,8 +384,7 @@ Level3 Disabled true - - + $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) false 4996 @@ -407,10 +405,9 @@ true true true - - + $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions) - MultiThreaded + MultiThreadedDLL StreamingSIMDExtensions2 true AnySuitable @@ -429,8 +426,9 @@ false true true - wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;zeroidc.lib;bcrypt.lib;userenv.lib;crypt32.lib;secur32.lib;ncrypt.lib;ntdll.lib;%(AdditionalDependencies) false + $(SolutionDir)..\zeroidc\target\i686-pc-windows-msvc\release\;%(AdditionalLibraryDirectories) @@ -440,10 +438,9 @@ true true true - - + $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions) - MultiThreaded + MultiThreadedDLL StreamingSIMDExtensions2 true AnySuitable @@ -452,7 +449,7 @@ 4996 Guard false - VectorCall + Cdecl false stdcpp14 None @@ -464,8 +461,9 @@ false true true - wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;zeroidc.lib;bcrypt.lib;userenv.lib;crypt32.lib;secur32.lib;ncrypt.lib;ntdll.lib;%(AdditionalDependencies) false + $(SolutionDir)..\zeroidc\target\x86_64-pc-windows-msvc\release\;%(AdditionalLibraryDirectories) diff --git a/zeroidc/zeroidc.vcxproj b/zeroidc/zeroidc.vcxproj index 9ab794c36..7410d1570 100644 --- a/zeroidc/zeroidc.vcxproj +++ b/zeroidc/zeroidc.vcxproj @@ -100,6 +100,9 @@ + + + diff --git a/zeroidc/zeroidc.vcxproj.filters b/zeroidc/zeroidc.vcxproj.filters index 840b57a0d..432c20f81 100644 --- a/zeroidc/zeroidc.vcxproj.filters +++ b/zeroidc/zeroidc.vcxproj.filters @@ -22,4 +22,9 @@ Source Files + + + Header Files + + \ No newline at end of file From df9a7497b1353912f236fccd7cb9730b97a6d9ee Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 15 Dec 2021 14:16:17 -0800 Subject: [PATCH 52/68] refactor out the separate AuthInfo struct consolidated everything into the single IDC struct. Should help keep from rotating the pkce token as often & causing issues with the login window flapping --- service/OneService.cpp | 31 ++--- zeroidc/src/ext.rs | 47 +++----- zeroidc/src/lib.rs | 250 +++++++++++++++++++++++++---------------- 3 files changed, 177 insertions(+), 151 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 1d5e8c564..91abef27b 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -155,7 +155,6 @@ public: : _webPort(9993) , _tap((EthernetTap *)0) , _idc(nullptr) - , _ainfo(nullptr) { // Real defaults are in network 'up' code in network event handler _settings.allowManaged = true; @@ -170,11 +169,6 @@ public: this->_managedRoutes.clear(); this->_tap.reset(); - if (_ainfo) { - zeroidc::zeroidc_auth_info_delete(_ainfo); - _ainfo = nullptr; - } - if (_idc) { zeroidc::zeroidc_stop(_idc); zeroidc::zeroidc_delete(_idc); @@ -284,18 +278,13 @@ public: // fprintf(stderr, "idc created (%s, %s, %s)\n", _config.issuerURL, _config.ssoClientID, _config.centralAuthURL); } - if (_ainfo != nullptr) { - zeroidc::zeroidc_auth_info_delete(_ainfo); - _ainfo = nullptr; - } - - _ainfo = zeroidc::zeroidc_get_auth_info( + zeroidc::zeroidc_set_nonce_and_csrf( _idc, _config.ssoState, _config.ssoNonce ); - const char* url = zeroidc::zeroidc_get_auth_url(_ainfo); + const char* url = zeroidc::zeroidc_get_auth_url(_idc); memcpy(_config.authenticationURL, url, strlen(url)); _config.authenticationURL[strlen(url)] = 0; } @@ -314,28 +303,27 @@ public: } const char* getAuthURL() { - if (_ainfo != nullptr) { - return zeroidc::zeroidc_get_auth_url(_ainfo); + if (_idc != nullptr) { + return zeroidc::zeroidc_get_auth_url(_idc); } - fprintf(stderr, "_ainfo is null\n"); + fprintf(stderr, "_idc is null\n"); return ""; } void doTokenExchange(const char *code) { - if (_ainfo == nullptr || _idc == nullptr) { + if (_idc == nullptr) { fprintf(stderr, "ainfo or idc null\n"); return; } - zeroidc::zeroidc_token_exchange(_idc, _ainfo, code); - zeroidc::zeroidc_auth_info_delete(_ainfo); - _ainfo = zeroidc::zeroidc_get_auth_info( + zeroidc::zeroidc_token_exchange(_idc, code); + zeroidc::zeroidc_set_nonce_and_csrf( _idc, _config.ssoState, _config.ssoNonce ); - const char* url = zeroidc::zeroidc_get_auth_url(_ainfo); + const char* url = zeroidc::zeroidc_get_auth_url(_idc); memcpy(_config.authenticationURL, url, strlen(url)); _config.authenticationURL[strlen(url)] = 0; } @@ -357,7 +345,6 @@ private: std::map< InetAddress, SharedPtr > _managedRoutes; OneService::NetworkSettings _settings; zeroidc::ZeroIDC *_idc; - zeroidc::AuthInfo *_ainfo; }; namespace { diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index f79f0f6f5..740cced29 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -2,7 +2,7 @@ use std::ffi::{CStr, CString}; use std::os::raw::c_char; use url::{Url}; -use crate::{AuthInfo, ZeroIDC}; +use crate::ZeroIDC; #[no_mangle] pub extern "C" fn zeroidc_new( @@ -120,11 +120,10 @@ pub extern "C" fn zeroidc_get_exp_time(ptr: *mut ZeroIDC) -> u64 { // } #[no_mangle] -pub extern "C" fn zeroidc_get_auth_info( +pub extern "C" fn zeroidc_set_nonce_and_csrf( ptr: *mut ZeroIDC, csrf_token: *const c_char, - nonce: *const c_char, -) -> *mut AuthInfo { + nonce: *const c_char) { let idc = unsafe { assert!(!ptr.is_null()); &mut *ptr @@ -132,12 +131,12 @@ pub extern "C" fn zeroidc_get_auth_info( if csrf_token.is_null() { println!("csrf_token is null"); - return std::ptr::null_mut(); + return; } if nonce.is_null() { println!("nonce is null"); - return std::ptr::null_mut(); + return; } let csrf_token = unsafe { CStr::from_ptr(csrf_token) } @@ -148,47 +147,31 @@ pub extern "C" fn zeroidc_get_auth_info( .to_str() .unwrap() .to_string(); - - match idc.get_auth_info(csrf_token, nonce) { - Some(a) => Box::into_raw(Box::new(a)), - None => std::ptr::null_mut(), - } + + idc.set_nonce_and_csrf(csrf_token, nonce); } #[no_mangle] -pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) { - if ptr.is_null() { - return; - } - unsafe { - Box::from_raw(ptr); - } -} - -#[no_mangle] -pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char { +pub extern "C" fn zeroidc_get_auth_url(ptr: *mut ZeroIDC) -> *const c_char { if ptr.is_null() { println!("passed a null object"); return std::ptr::null_mut(); } - let ai = unsafe { + let idc = unsafe { &mut *ptr }; - let s = CString::new(ai.url.to_string()).unwrap(); + let s = CString::new(idc.auth_url()).unwrap(); return s.into_raw(); } #[no_mangle] -pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, ai: *mut AuthInfo, code: *const c_char ) { +pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char ) { if idc.is_null() { println!("idc is null"); return } - if ai.is_null() { - println!("ai is null"); - return - } + if code.is_null() { println!("code is null"); return @@ -196,12 +179,10 @@ pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, ai: *mut AuthInfo, c let idc = unsafe { &mut *idc }; - let ai = unsafe { - &mut *ai - }; + let code = unsafe{CStr::from_ptr(code)}.to_str().unwrap(); - idc.do_token_exchange(ai, code); + idc.do_token_exchange( code); } #[no_mangle] diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index bd9f96f3c..888b6d976 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -30,6 +30,18 @@ struct Inner { access_token: Option, refresh_token: Option, exp_time: u64, + + url: Option, + csrf_token: Option, + nonce: Option, + pkce_verifier: Option, +} + +impl Inner { + #[inline] + fn as_opt(&mut self) -> Option<&mut Inner> { + Some(self) + } } #[derive(Debug, Serialize, Deserialize)] @@ -45,13 +57,6 @@ fn nonce_func(nonce: String) -> Box Nonce> { return Box::new(move || Nonce::new(nonce.to_string())); } -pub struct AuthInfo { - url: Url, - csrf_token: CsrfToken, - nonce: Nonce, - pkce_verifier: Option, -} - fn systemtime_strftime(dt: T, format: &str) -> String where T: Into { @@ -87,6 +92,11 @@ impl ZeroIDC { access_token: None, refresh_token: None, exp_time: 0, + + url: None, + csrf_token: None, + nonce: None, + pkce_verifier: None, })), }; @@ -264,102 +274,150 @@ impl ZeroIDC { return (*self.inner.lock().unwrap()).exp_time; } - fn do_token_exchange(&mut self, auth_info: &mut AuthInfo, code: &str) { - if let Some(verifier) = auth_info.pkce_verifier.take() { - let token_response = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { - let r = c.exchange_code(AuthorizationCode::new(code.to_string())) - .set_pkce_verifier(verifier) - .request(http_client); - match r { - Ok(res) =>{ - return Some(res); - }, - Err(e) => { - println!("token response error: {}", e.to_string()); - - return None; - }, - } - }); - // TODO: do stuff with token response - if let Some(Some(tok)) = token_response { - let id_token = tok.id_token().unwrap(); - println!("ID token: {}", id_token.to_string()); - - let split = auth_info.csrf_token.secret().split("_"); - let split = split.collect::>(); - - let params = [("id_token", id_token.to_string()),("state", split[0].to_string())]; - let client = reqwest::blocking::Client::new(); - let res = client.post((*self.inner.lock().unwrap()).auth_endpoint.clone()) - .form(¶ms) - .send(); - - match res { - Ok(res) => { - println!("hit url: {}", res.url().as_str()); - println!("Status: {}", res.status()); - - let at = tok.access_token().secret(); - let exp = dangerous_insecure_decode::(&at); - if let Ok(e) = exp { - (*self.inner.lock().unwrap()).exp_time = e.claims.exp - } - - (*self.inner.lock().unwrap()).access_token = Some(tok.access_token().clone()); - if let Some(t) = tok.refresh_token() { - (*self.inner.lock().unwrap()).refresh_token = Some(t.clone()); - self.start(); - } - }, - Err(res) => { - println!("hit url: {}", res.url().unwrap().as_str()); - println!("Status: {}", res.status().unwrap()); - println!("Post error: {}", res.to_string()); - (*self.inner.lock().unwrap()).exp_time = 0; - } - } - - let access_token = tok.access_token(); - println!("Access Token: {}", access_token.secret()); - - let refresh_token = tok.refresh_token(); - println!("Refresh Token: {}", refresh_token.unwrap().secret()); + fn set_nonce_and_csrf(&mut self, csrf_token: String, nonce: String) { + let local = Arc::clone(&self.inner); + (*local.lock().expect("can't lock inner")).as_opt().map(|i| { + let mut csrf_diff = false; + let mut nonce_diff = false; + let mut need_verifier = false; + + match i.pkce_verifier { + None => { + need_verifier = true; + }, + _ => (), } - } else { - println!("No pkce verifier! Can't exchange tokens!!!"); + if let Some(csrf) = i.csrf_token.clone() { + if *csrf.secret() != csrf_token { + csrf_diff = true; + } + } + if let Some(n) = i.nonce.clone() { + if *n.secret() != nonce { + nonce_diff = true; + } + } + + if need_verifier || csrf_diff || nonce_diff { + let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + let r = i.oidc_client.as_ref().map(|c| { + let (auth_url, csrf_token, nonce) = c + .authorize_url( + AuthenticationFlow::::AuthorizationCode, + csrf_func(csrf_token), + nonce_func(nonce), + ) + .add_scope(Scope::new("profile".to_string())) + .add_scope(Scope::new("email".to_string())) + .add_scope(Scope::new("offline_access".to_string())) + .add_scope(Scope::new("openid".to_string())) + .set_pkce_challenge(pkce_challenge) + .url(); + + (auth_url, csrf_token, nonce) + }); + + if let Some(r) = r { + i.url = Some(r.0); + i.csrf_token = Some(r.1); + i.nonce = Some(r.2); + i.pkce_verifier = Some(pkce_verifier); + } + } + }); + } + + fn auth_url(&self) -> String { + let url = (*self.inner.lock().expect("can't lock inner")).as_opt().map(|i| { + match i.url.clone() { + Some(u) => u.to_string(), + _ => "".to_string(), + } + }); + + match url { + Some(url) => url.to_string(), + None => "".to_string(), } } - fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option { - let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); - let network_id = self.get_network_id(); + fn do_token_exchange(&mut self, code: &str) { + let local = Arc::clone(&self.inner); + (*local.lock().unwrap()).as_opt().map(|i| { + if let Some(verifier) = i.pkce_verifier.take() { + let token_response = i.oidc_client.as_ref().map(|c| { + let r = c.exchange_code(AuthorizationCode::new(code.to_string())) + .set_pkce_verifier(verifier) + .request(http_client); + match r { + Ok(res) =>{ + return Some(res); + }, + Err(e) => { + println!("token response error: {}", e.to_string()); + + return None; + }, + } + }); + // TODO: do stuff with token response + if let Some(Some(tok)) = token_response { + let id_token = tok.id_token().unwrap(); + println!("ID token: {}", id_token.to_string()); - let r = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| { - let (auth_url, csrf_token, nonce) = c - .authorize_url( - AuthenticationFlow::::AuthorizationCode, - csrf_func(csrf_token), - nonce_func(nonce), - ) - .add_scope(Scope::new("profile".to_string())) - .add_scope(Scope::new("email".to_string())) - .add_scope(Scope::new("offline_access".to_string())) - .add_scope(Scope::new("openid".to_string())) - .set_pkce_challenge(pkce_challenge) - .add_extra_param("network_id", network_id) - .url(); + let mut split = "".to_string(); + match i.csrf_token.clone() { + Some(csrf_token) => { + split = csrf_token.secret().to_owned(); + }, + _ => (), + } - // println!("URL: {}", auth_url); + let split = split.split("_").collect::>(); + + if split.len() == 2 { + let params = [("id_token", id_token.to_string()),("state", split[0].to_string())]; + let client = reqwest::blocking::Client::new(); + let res = client.post((*self.inner.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); - return AuthInfo { - url: auth_url, - pkce_verifier: Some(pkce_verifier), - csrf_token, - nonce, - }; + match res { + Ok(res) => { + println!("hit url: {}", res.url().as_str()); + println!("Status: {}", res.status()); + + let at = tok.access_token().secret(); + let exp = dangerous_insecure_decode::(&at); + if let Ok(e) = exp { + (*self.inner.lock().unwrap()).exp_time = e.claims.exp + } + + (*self.inner.lock().unwrap()).access_token = Some(tok.access_token().clone()); + if let Some(t) = tok.refresh_token() { + (*self.inner.lock().unwrap()).refresh_token = Some(t.clone()); + self.start(); + } + }, + Err(res) => { + println!("hit url: {}", res.url().unwrap().as_str()); + println!("Status: {}", res.status().unwrap()); + println!("Post error: {}", res.to_string()); + (*self.inner.lock().unwrap()).exp_time = 0; + } + } + + let access_token = tok.access_token(); + println!("Access Token: {}", access_token.secret()); + + let refresh_token = tok.refresh_token(); + println!("Refresh Token: {}", refresh_token.unwrap().secret()); + } else { + println!("invalid split length?!?"); + } + } + } }); - - r } } + From b567e91f687b73c43690fea01a3bc82119802424 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 15 Dec 2021 14:36:50 -0800 Subject: [PATCH 53/68] fix --- service/OneService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index f904e780f..9648c5dd8 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -2249,7 +2249,7 @@ public: } #ifdef __APPLE__ - if (!MacDNSHelper::addIps(n.config.nwid, n.config.mac, n.tap->deviceName().c_str(), newManagedIps)) + if (!MacDNSHelper::addIps(n.config().nwid, n.config().mac, n.tap()->deviceName().c_str(), newManagedIps)) fprintf(stderr, "ERROR: unable to add v6 addresses to system configuration" ZT_EOL_S); #endif #endif From 0ab5cce8789f0e6d45c405fe2e5a6433a53957d7 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 15 Dec 2021 14:37:06 -0800 Subject: [PATCH 54/68] TIL: Its not as hard as I thought to make Rust deadlock --- zeroidc/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 888b6d976..3e48c11c0 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -343,6 +343,7 @@ impl ZeroIDC { fn do_token_exchange(&mut self, code: &str) { let local = Arc::clone(&self.inner); + let mut should_start = false; (*local.lock().unwrap()).as_opt().map(|i| { if let Some(verifier) = i.pkce_verifier.take() { let token_response = i.oidc_client.as_ref().map(|c| { @@ -378,7 +379,7 @@ impl ZeroIDC { if split.len() == 2 { let params = [("id_token", id_token.to_string()),("state", split[0].to_string())]; let client = reqwest::blocking::Client::new(); - let res = client.post((*self.inner.lock().unwrap()).auth_endpoint.clone()) + let res = client.post(i.auth_endpoint.clone()) .form(¶ms) .send(); @@ -390,20 +391,20 @@ impl ZeroIDC { let at = tok.access_token().secret(); let exp = dangerous_insecure_decode::(&at); if let Ok(e) = exp { - (*self.inner.lock().unwrap()).exp_time = e.claims.exp + i.exp_time = e.claims.exp } - (*self.inner.lock().unwrap()).access_token = Some(tok.access_token().clone()); + i.access_token = Some(tok.access_token().clone()); if let Some(t) = tok.refresh_token() { - (*self.inner.lock().unwrap()).refresh_token = Some(t.clone()); - self.start(); + i.refresh_token = Some(t.clone()); + should_start = true; } }, Err(res) => { println!("hit url: {}", res.url().unwrap().as_str()); println!("Status: {}", res.status().unwrap()); println!("Post error: {}", res.to_string()); - (*self.inner.lock().unwrap()).exp_time = 0; + i.exp_time = 0; } } @@ -418,6 +419,9 @@ impl ZeroIDC { } } }); + if should_start { + self.start(); + } } } From 1375e3e2f593585a30d514496b32b33f5afc9a58 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 15 Dec 2021 15:53:48 -0800 Subject: [PATCH 55/68] allow debug & release builds of Rust --- make-linux.mk | 14 +++++++++++--- make-mac.mk | 10 +++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/make-linux.mk b/make-linux.mk index b001d4ce2..fe1f08e38 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -11,14 +11,14 @@ endif INCLUDES?=-Izeroidc/target DEFS?= -LDLIBS?=zeroidc/target/release/libzeroidc.a -ldl +LDLIBS?= DESTDIR?= include objects.mk ONE_OBJS+=osdep/LinuxEthernetTap.o ONE_OBJS+=osdep/LinuxNetLink.o -# for central controller builds +# for central controller buildsk TIMESTAMP=$(shell date +"%Y%m%d%H%M") # Auto-detect miniupnpc and nat-pmp as well and use system libs if present, @@ -41,6 +41,12 @@ else override DEFS+=-DZT_USE_SYSTEM_NATPMP endif +ifeq ($(ZT_DEBUG),1) + LDLIBS+=zeroidc/target/debug/libzeroidc.a -ldl +else + LDLIBS+=zeroidc/target/release/libzeroidc.a -ldl +endif + # Use bundled http-parser since distribution versions are NOT API-stable or compatible! # Trying to use dynamically linked libhttp-parser causes tons of compatibility problems. ONE_OBJS+=ext/http-parser/http_parser.o @@ -64,6 +70,7 @@ ifeq ($(ZT_DEBUG),1) override CFLAGS+=-Wall -Wno-deprecated -g -O -pthread $(INCLUDES) $(DEFS) override CXXFLAGS+=-Wall -Wno-deprecated -g -O -std=c++11 -pthread $(INCLUDES) $(DEFS) ZT_TRACE=1 + RUSTFLAGS= # The following line enables optimization for the crypto code, since # C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box! node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CXXFLAGS=-Wall -O2 -g -pthread $(INCLUDES) $(DEFS) @@ -73,6 +80,7 @@ else CXXFLAGS?=-O3 -fstack-protector -fPIE override CXXFLAGS+=-Wall -Wno-deprecated -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS) LDFLAGS=-pie -Wl,-z,relro,-z,now + RUSTFLAGS=--release endif ifeq ($(ZT_QNAP), 1) @@ -364,7 +372,7 @@ debug: FORCE make ZT_DEBUG=1 selftest zeroidc: FORCE - cd zeroidc && cargo build --release + cd zeroidc && cargo build $(RUSTFLAGS) # Note: keep the symlinks in /var/lib/zerotier-one to the binaries since these # provide backward compatibility with old releases where the binaries actually diff --git a/make-mac.mk b/make-mac.mk index 784c167e7..a128aa41b 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -75,6 +75,8 @@ ifeq ($(ZT_DEBUG),1) ARCH_FLAGS= CFLAGS+=-Wall -g $(INCLUDES) $(DEFS) $(ARCH_FLAGS) STRIP=echo + RUSTFLAGS= + RUST_VARIANT=debug # The following line enables optimization for the crypto code, since # C25519 in particular is almost UNUSABLE in heavy testing without it. node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g $(INCLUDES) $(DEFS) @@ -82,6 +84,8 @@ else CFLAGS?=-Ofast -fstack-protector-strong CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -mmacosx-version-min=$(MACOS_VERSION_MIN) -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS) STRIP=strip + RUSTFLAGS=--release + RUST_VARIANT=release endif ifeq ($(ZT_TRACE),1) @@ -117,9 +121,9 @@ zerotier-one: one zeroidc: zeroidc/target/libzeroidc.a zeroidc/target/libzeroidc.a: FORCE - cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=x86_64-apple-darwin --release - cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=aarch64-apple-darwin --release - cd zeroidc && lipo -create target/x86_64-apple-darwin/release/libzeroidc.a target/aarch64-apple-darwin/release/libzeroidc.a -output target/libzeroidc.a + cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=x86_64-apple-darwin $(RUSTFLAGS) + cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=aarch64-apple-darwin $(RUSTFLAGS) + cd zeroidc && lipo -create target/x86_64-apple-darwin/$(RUST_VARIANT)/libzeroidc.a target/aarch64-apple-darwin/$(RUST_VARIANT)/libzeroidc.a -output target/libzeroidc.a central-controller: make ARCH_FLAGS="-arch x86_64" ZT_CONTROLLER=1 one From 2293b0703f9e9ace1e7245cab0368756e8a5f1b8 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Dec 2021 11:37:58 -0800 Subject: [PATCH 56/68] Can get Central JSON bundle back to zerotier-one window --- service/OneService.cpp | 9 +++-- zeroidc/Cargo.lock | 1 + zeroidc/Cargo.toml | 1 + zeroidc/src/ext.rs | 10 +++-- zeroidc/src/lib.rs | 85 ++++++++++++++++++++++++++++-------------- 5 files changed, 69 insertions(+), 37 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 9648c5dd8..4291402bb 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -310,13 +310,13 @@ public: return ""; } - void doTokenExchange(const char *code) { + const char* doTokenExchange(const char *code) { if (_idc == nullptr) { fprintf(stderr, "ainfo or idc null\n"); - return; + return ""; } - zeroidc::zeroidc_token_exchange(_idc, code); + const char *ret = zeroidc::zeroidc_token_exchange(_idc, code); zeroidc::zeroidc_set_nonce_and_csrf( _idc, _config.ssoState, @@ -326,6 +326,7 @@ public: const char* url = zeroidc::zeroidc_get_auth_url(_idc); memcpy(_config.authenticationURL, url, strlen(url)); _config.authenticationURL[strlen(url)] = 0; + return ret; } uint64_t getExpiryTime() { @@ -1674,7 +1675,7 @@ public: if (_nets.find(id) != _nets.end()) { NetworkState& ns = _nets[id]; const char* code = zeroidc::zeroidc_get_url_param_value("code", path.c_str()); - ns.doTokenExchange(code); + res = json::parse(ns.doTokenExchange(code)); scode = 200; } else { scode = 404; diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index 2447d8d21..24c126e8c 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -1494,6 +1494,7 @@ name = "zeroidc" version = "0.1.0" dependencies = [ "base64 0.13.0", + "bytes", "cbindgen", "jsonwebtoken", "openidconnect", diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 058564be5..e74c9efaa 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -19,6 +19,7 @@ reqwest = "0.11.7" jsonwebtoken = "7.2.0" serde = "1.0.130" time = { version = "0.3.5", features = ["formatting"] } +bytes = "1.1.0" [build-dependencies] cbindgen = "0.20.0" diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index 740cced29..d39f82452 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -166,15 +166,15 @@ pub extern "C" fn zeroidc_get_auth_url(ptr: *mut ZeroIDC) -> *const c_char { } #[no_mangle] -pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char ) { +pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char ) -> *const c_char { if idc.is_null() { println!("idc is null"); - return + return std::ptr::null(); } if code.is_null() { println!("code is null"); - return + return std::ptr::null(); } let idc = unsafe { &mut *idc @@ -182,7 +182,9 @@ pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char let code = unsafe{CStr::from_ptr(code)}.to_str().unwrap(); - idc.do_token_exchange( code); + let ret = idc.do_token_exchange( code); + let ret = CString::new(ret).unwrap(); + return ret.into_raw(); } #[no_mangle] diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 3e48c11c0..5b4458249 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -1,21 +1,26 @@ pub mod ext; extern crate base64; +extern crate bytes; extern crate openidconnect; extern crate time; extern crate url; -use std::time::{SystemTime, UNIX_EPOCH, Duration}; -use time::{OffsetDateTime, format_description}; -use std::sync::{Arc, Mutex}; -use std::thread::{sleep, spawn, JoinHandle}; -use serde::{Deserialize, Serialize}; +use bytes::Bytes; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; use openidconnect::{AccessToken, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; +use serde::{Deserialize, Serialize}; +use std::str::from_utf8; +use std::sync::{Arc, Mutex}; +use std::thread::{sleep, spawn, JoinHandle}; +use std::time::{SystemTime, UNIX_EPOCH, Duration}; +use time::{OffsetDateTime, format_description}; + use jsonwebtoken::{dangerous_insecure_decode}; use url::Url; +use time::ext::NumericalDuration; pub struct ZeroIDC { inner: Arc>, @@ -277,26 +282,30 @@ impl ZeroIDC { fn set_nonce_and_csrf(&mut self, csrf_token: String, nonce: String) { let local = Arc::clone(&self.inner); (*local.lock().expect("can't lock inner")).as_opt().map(|i| { - let mut csrf_diff = false; - let mut nonce_diff = false; - let mut need_verifier = false; - - match i.pkce_verifier { - None => { - need_verifier = true; - }, - _ => (), - } - if let Some(csrf) = i.csrf_token.clone() { + let need_verifier = match i.pkce_verifier { + None => true, + _ => false, + }; + + let csrf_diff = if let Some(csrf) = i.csrf_token.clone() { if *csrf.secret() != csrf_token { - csrf_diff = true; + true + } else { + false } - } - if let Some(n) = i.nonce.clone() { + } else { + false + }; + + let nonce_diff = if let Some(n) = i.nonce.clone() { if *n.secret() != nonce { - nonce_diff = true; + true + } else { + false } - } + } else { + false + }; if need_verifier || csrf_diff || nonce_diff { let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); @@ -341,10 +350,10 @@ impl ZeroIDC { } } - fn do_token_exchange(&mut self, code: &str) { + fn do_token_exchange(&mut self, code: &str) -> String { let local = Arc::clone(&self.inner); let mut should_start = false; - (*local.lock().unwrap()).as_opt().map(|i| { + let res = (*local.lock().unwrap()).as_opt().map(|i| { if let Some(verifier) = i.pkce_verifier.take() { let token_response = i.oidc_client.as_ref().map(|c| { let r = c.exchange_code(AuthorizationCode::new(code.to_string())) @@ -361,7 +370,7 @@ impl ZeroIDC { }, } }); - // TODO: do stuff with token response + if let Some(Some(tok)) = token_response { let id_token = tok.id_token().unwrap(); println!("ID token: {}", id_token.to_string()); @@ -399,6 +408,23 @@ impl ZeroIDC { i.refresh_token = Some(t.clone()); should_start = true; } + let access_token = tok.access_token(); + println!("Access Token: {}", access_token.secret()); + + let refresh_token = tok.refresh_token(); + println!("Refresh Token: {}", refresh_token.unwrap().secret()); + + let bytes = match res.bytes() { + Ok(bytes) => bytes, + Err(_) => Bytes::from(""), + }; + + let bytes = match from_utf8(bytes.as_ref()) { + Ok(bytes) => bytes.to_string(), + Err(_) => "".to_string(), + }; + + return bytes; }, Err(res) => { println!("hit url: {}", res.url().unwrap().as_str()); @@ -408,20 +434,21 @@ impl ZeroIDC { } } - let access_token = tok.access_token(); - println!("Access Token: {}", access_token.secret()); - - let refresh_token = tok.refresh_token(); - println!("Refresh Token: {}", refresh_token.unwrap().secret()); + } else { println!("invalid split length?!?"); } } } + "".to_string() }); if should_start { self.start(); } + return match res { + Some(res) => res, + _ => "".to_string(), + }; } } From 2642fa1ee2d2531d7f590a0fba411ae4e6839a2a Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Dec 2021 12:06:16 -0800 Subject: [PATCH 57/68] Return HTML instead of blank page when auth is successful! --- service/OneService.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 4291402bb..52514bb4a 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1675,8 +1675,47 @@ public: if (_nets.find(id) != _nets.end()) { NetworkState& ns = _nets[id]; const char* code = zeroidc::zeroidc_get_url_param_value("code", path.c_str()); - res = json::parse(ns.doTokenExchange(code)); + ns.doTokenExchange(code); scode = 200; + responseBody = "\ +\ +\ +\ +\ +





\ +
\ +
\ +
Authentication Successful
\ +
\ +\ +"; + responseContentType = "text/html"; + return scode; } else { scode = 404; } From f489862500b2f0137fcac2e19d31163e0acfc6e0 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Dec 2021 12:20:31 -0800 Subject: [PATCH 58/68] update final page text a smidge --- service/OneService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 52514bb4a..21652ef39 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1710,7 +1710,7 @@ div.icon {\





\
\
\ -
Authentication Successful
\ +
Authentication Successful. You may now access the network.
\
\ \ "; From 2435ab70ab7542927baa4d8e99cc9708bc31e3df Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Dec 2021 12:23:09 -0800 Subject: [PATCH 59/68] remove some extra verbose logging ... and baby you got a full flow goin on --- service/OneService.cpp | 2 +- zeroidc/src/lib.rs | 56 ++++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 21652ef39..418921536 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -458,7 +458,7 @@ static void _networkToJson(nlohmann::json &nj,NetworkState &ns) nj["dns"] = m; if (ns.config().ssoEnabled) { const char* authURL = ns.getAuthURL(); - fprintf(stderr, "Auth URL: %s\n", authURL); + //fprintf(stderr, "Auth URL: %s\n", authURL); nj["authenticationURL"] = authURL; nj["authenticationExpiryTime"] = (ns.getExpiryTime()*1000); nj["ssoEnabled"] = ns.config().ssoEnabled; diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 5b4458249..0671c9365 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -7,7 +7,7 @@ extern crate time; extern crate url; use bytes::Bytes; -use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; +use jsonwebtoken::{dangerous_insecure_decode};use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; use openidconnect::{AccessToken, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; use serde::{Deserialize, Serialize}; @@ -17,10 +17,8 @@ use std::thread::{sleep, spawn, JoinHandle}; use std::time::{SystemTime, UNIX_EPOCH, Duration}; use time::{OffsetDateTime, format_description}; -use jsonwebtoken::{dangerous_insecure_decode}; use url::Url; -use time::ext::NumericalDuration; pub struct ZeroIDC { inner: Arc>, @@ -164,7 +162,9 @@ impl ZeroIDC { let exp = UNIX_EPOCH + Duration::from_secs((*inner_local.lock().unwrap()).exp_time); let now = SystemTime::now(); - println!("refresh token thread tick, now: {}, exp: {}", systemtime_strftime(now, "[year]-[month]-[day] [hour]:[minute]:[second]"), systemtime_strftime(exp, "[year]-[month]-[day] [hour]:[minute]:[second]")); + #[cfg(debug_assertions)] { + println!("refresh token thread tick, now: {}, exp: {}", systemtime_strftime(now, "[year]-[month]-[day] [hour]:[minute]:[second]"), systemtime_strftime(exp, "[year]-[month]-[day] [hour]:[minute]:[second]")); + } let refresh_token = (*inner_local.lock().unwrap()).refresh_token.clone(); if let Some(refresh_token) = refresh_token { if now >= (exp - Duration::from_secs(30)) { @@ -182,7 +182,9 @@ impl ZeroIDC { Some(id_token) => { let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; - println!("New ID token: {}", id_token.to_string()); + #[cfg(debug_assertions)] { + println!("New ID token: {}", id_token.to_string()); + } let client = reqwest::blocking::Client::new(); let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) .form(¶ms) @@ -191,9 +193,10 @@ impl ZeroIDC { match r { Ok(r) => { if r.status().is_success() { - println!("hit url: {}", r.url().as_str()); - println!("status: {}", r.status()); - + #[cfg(debug_assertions)] { + println!("hit url: {}", r.url().as_str()); + println!("status: {}", r.status()); + } let access_token = res.access_token(); let at = access_token.secret(); @@ -205,10 +208,12 @@ impl ZeroIDC { (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); if let Some(t) = res.refresh_token() { - println!("New Refresh Token: {}", t.secret()); + // println!("New Refresh Token: {}", t.secret()); (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); } - println!("Central post succeeded"); + #[cfg(debug_assertions)] { + println!("Central post succeeded"); + } } else { println!("Central post failed: {}", r.status().to_string()); println!("hit url: {}", r.url().as_str()); @@ -218,10 +223,11 @@ impl ZeroIDC { } }, Err(e) => { + println!("Central post failed: {}", e.to_string()); println!("hit url: {}", e.url().unwrap().as_str()); println!("Status: {}", e.status().unwrap()); - // (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).exp_time = 0; (*inner_local.lock().unwrap()).running = false; } } @@ -363,9 +369,11 @@ impl ZeroIDC { Ok(res) =>{ return Some(res); }, - Err(e) => { - println!("token response error: {}", e.to_string()); - + Err(_e) => { + #[cfg(debug_assertions)] { + println!("token response error: {}", _e.to_string()); + } + return None; }, } @@ -373,7 +381,9 @@ impl ZeroIDC { if let Some(Some(tok)) = token_response { let id_token = tok.id_token().unwrap(); - println!("ID token: {}", id_token.to_string()); + #[cfg(debug_assertions)] { + println!("ID token: {}", id_token.to_string()); + } let mut split = "".to_string(); match i.csrf_token.clone() { @@ -394,8 +404,10 @@ impl ZeroIDC { match res { Ok(res) => { - println!("hit url: {}", res.url().as_str()); - println!("Status: {}", res.status()); + #[cfg(debug_assertions)] { + println!("hit url: {}", res.url().as_str()); + println!("Status: {}", res.status()); + } let at = tok.access_token().secret(); let exp = dangerous_insecure_decode::(&at); @@ -408,11 +420,13 @@ impl ZeroIDC { i.refresh_token = Some(t.clone()); should_start = true; } - let access_token = tok.access_token(); - println!("Access Token: {}", access_token.secret()); + #[cfg(debug_assertions)] { + let access_token = tok.access_token(); + println!("Access Token: {}", access_token.secret()); - let refresh_token = tok.refresh_token(); - println!("Refresh Token: {}", refresh_token.unwrap().secret()); + let refresh_token = tok.refresh_token(); + println!("Refresh Token: {}", refresh_token.unwrap().secret()); + } let bytes = match res.bytes() { Ok(bytes) => bytes, From 8fccf3136c49c8d0c148797f3db1ea572e054f36 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Dec 2021 18:44:36 -0800 Subject: [PATCH 60/68] Enable validation of token hashes as part of the oidc process --- zeroidc/src/lib.rs | 205 ++++++++++++++++++++++++++++++++------------- 1 file changed, 145 insertions(+), 60 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 0671c9365..18c7f6bab 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -7,9 +7,10 @@ extern crate time; extern crate url; use bytes::Bytes; -use jsonwebtoken::{dangerous_insecure_decode};use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; +use jsonwebtoken::{dangerous_insecure_decode}; +use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; -use openidconnect::{AccessToken, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; +use openidconnect::{AccessToken, AccessTokenHash, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, NonceVerifier, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; use serde::{Deserialize, Serialize}; use std::str::from_utf8; use std::sync::{Arc, Mutex}; @@ -168,80 +169,122 @@ impl ZeroIDC { let refresh_token = (*inner_local.lock().unwrap()).refresh_token.clone(); if let Some(refresh_token) = refresh_token { if now >= (exp - Duration::from_secs(30)) { + let nonce = (*inner_local.lock().unwrap()).nonce.clone(); let token_response = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { let res = c.exchange_refresh_token(&refresh_token) .request(http_client); - res - }); - - if let Some(res) = token_response { match res { Ok(res) => { - match res.id_token() { - Some(id_token) => { - - let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; - #[cfg(debug_assertions)] { - println!("New ID token: {}", id_token.to_string()); + let n = match nonce { + Some(n) => n, + None => { + return None; + } + }; + + let id = match res.id_token() { + Some(t) => t, + None => { + return None; + } + }; + + let claims = match id.claims(&c.id_token_verifier(), &n) { + Ok(c) => c, + Err(_e) => { + return None; + } + }; + + let signing_algo = match id.signing_alg() { + Ok(s) => s, + Err(_) => { + return None; + } + }; + + if let Some(expected_hash) = claims.access_token_hash() { + let actual_hash = match AccessTokenHash::from_token(res.access_token(), &signing_algo) { + Ok(h) => h, + Err(e) => { + println!("Error hashing access token: {}", e); + return None; } - let client = reqwest::blocking::Client::new(); - let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) - .form(¶ms) - .send(); + }; + + if actual_hash != *expected_hash { + println!("token hash error"); + return None; + } + } + return Some(res); + }, + Err(_e) => { + return None; + } + }; + }); - match r { - Ok(r) => { - if r.status().is_success() { - #[cfg(debug_assertions)] { - println!("hit url: {}", r.url().as_str()); - println!("status: {}", r.status()); - } + if let Some(Some(res)) = token_response{ + match res.id_token() { + Some(id_token) => { + let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; + #[cfg(debug_assertions)] { + println!("New ID token: {}", id_token.to_string()); + } + let client = reqwest::blocking::Client::new(); + let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); - let access_token = res.access_token(); - let at = access_token.secret(); - let exp = dangerous_insecure_decode::(&at); - - if let Ok(e) = exp { - (*inner_local.lock().unwrap()).exp_time = e.claims.exp - } - - (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); - if let Some(t) = res.refresh_token() { - // println!("New Refresh Token: {}", t.secret()); - (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); - } - #[cfg(debug_assertions)] { - println!("Central post succeeded"); - } - } else { - println!("Central post failed: {}", r.status().to_string()); - println!("hit url: {}", r.url().as_str()); - println!("Status: {}", r.status()); - (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; - } - }, - Err(e) => { - - println!("Central post failed: {}", e.to_string()); - println!("hit url: {}", e.url().unwrap().as_str()); - println!("Status: {}", e.status().unwrap()); - (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; + match r { + Ok(r) => { + if r.status().is_success() { + #[cfg(debug_assertions)] { + println!("hit url: {}", r.url().as_str()); + println!("status: {}", r.status()); } + + let access_token = res.access_token(); + let at = access_token.secret(); + let exp = dangerous_insecure_decode::(&at); + + if let Ok(e) = exp { + (*inner_local.lock().unwrap()).exp_time = e.claims.exp + } + + (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); + if let Some(t) = res.refresh_token() { + // println!("New Refresh Token: {}", t.secret()); + (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); + } + #[cfg(debug_assertions)] { + println!("Central post succeeded"); + } + } else { + println!("Central post failed: {}", r.status().to_string()); + println!("hit url: {}", r.url().as_str()); + println!("Status: {}", r.status()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; } }, - None => { - println!("No id token???"); + Err(e) => { + + println!("Central post failed: {}", e.to_string()); + println!("hit url: {}", e.url().unwrap().as_str()); + println!("Status: {}", e.status().unwrap()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; } } }, - Err(e) => { - println!("Error posting refresh token: {}", e) + None => { + println!("no id token?!?"); } } - } + } } else { println!("waiting to refresh"); } @@ -367,7 +410,49 @@ impl ZeroIDC { .request(http_client); match r { Ok(res) =>{ - return Some(res); + let n = match i.nonce.clone() { + Some(n) => n, + None => { + return None; + } + }; + + let id = match res.id_token() { + Some(t) => t, + None => { + return None; + } + }; + + let claims = match id.claims(&c.id_token_verifier(), &n) { + Ok(c) => c, + Err(_e) => { + return None; + } + }; + + let signing_algo = match id.signing_alg() { + Ok(s) => s, + Err(_) => { + return None; + } + }; + + if let Some(expected_hash) = claims.access_token_hash() { + let actual_hash = match AccessTokenHash::from_token(res.access_token(), &signing_algo) { + Ok(h) => h, + Err(e) => { + println!("Error hashing access token: {}", e); + return None; + } + }; + + if actual_hash != *expected_hash { + println!("token hash error"); + return None; + } + } + Some(res) }, Err(_e) => { #[cfg(debug_assertions)] { From a69e91c541a632feea4f0d4a2cdb963566554573 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Dec 2021 19:49:15 -0800 Subject: [PATCH 61/68] Fix hash verification on refresh --- zeroidc/src/lib.rs | 200 ++++++++++++++++++++++++++------------------- 1 file changed, 115 insertions(+), 85 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 18c7f6bab..188064312 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -159,6 +159,10 @@ impl ZeroIDC { (*inner_local.lock().unwrap()).running = true; let mut running = true; + // Keep a copy of the initial nonce used to get the tokens + // Will be needed later when verifying the responses from refresh tokens + let nonce = (*inner_local.lock().unwrap()).nonce.clone(); + while running { let exp = UNIX_EPOCH + Duration::from_secs((*inner_local.lock().unwrap()).exp_time); let now = SystemTime::now(); @@ -169,121 +173,147 @@ impl ZeroIDC { let refresh_token = (*inner_local.lock().unwrap()).refresh_token.clone(); if let Some(refresh_token) = refresh_token { if now >= (exp - Duration::from_secs(30)) { - let nonce = (*inner_local.lock().unwrap()).nonce.clone(); let token_response = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { let res = c.exchange_refresh_token(&refresh_token) .request(http_client); + res + }); + + if let Some(res) = token_response { match res { Ok(res) => { - let n = match nonce { + + let n = match nonce.clone() { Some(n) => n, None => { - return None; + println!("err: no nonce"); + continue; } }; let id = match res.id_token() { Some(t) => t, None => { - return None; + println!("err: no id_token"); + continue; } }; - let claims = match id.claims(&c.id_token_verifier(), &n) { - Ok(c) => c, - Err(_e) => { - return None; - } - }; - - let signing_algo = match id.signing_alg() { - Ok(s) => s, - Err(_) => { - return None; - } - }; - - if let Some(expected_hash) = claims.access_token_hash() { - let actual_hash = match AccessTokenHash::from_token(res.access_token(), &signing_algo) { - Ok(h) => h, + let verified = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { + let claims = match id.claims(&c.id_token_verifier(), &n) { + Ok(c) => c, Err(e) => { - println!("Error hashing access token: {}", e); - return None; + println!("claims err: {}", e); + return false; } }; - - if actual_hash != *expected_hash { - println!("token hash error"); - return None; + + let signing_algo = match id.signing_alg() { + Ok(s) => s, + Err(e) => { + println!("alg err: {}", e); + return false; + } + }; + + if let Some(expected_hash) = claims.access_token_hash() { + let actual_hash = match AccessTokenHash::from_token(res.access_token(), &signing_algo) { + Ok(h) => h, + Err(e) => { + println!("Error hashing access token: {}", e); + return false; + } + }; + + if actual_hash != *expected_hash { + println!("token hash error"); + return false; + } } - } - return Some(res); - }, - Err(_e) => { - return None; - } - }; - }); - - if let Some(Some(res)) = token_response{ - match res.id_token() { - Some(id_token) => { - let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; - #[cfg(debug_assertions)] { - println!("New ID token: {}", id_token.to_string()); - } - let client = reqwest::blocking::Client::new(); - let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) - .form(¶ms) - .send(); - - match r { - Ok(r) => { - if r.status().is_success() { - #[cfg(debug_assertions)] { - println!("hit url: {}", r.url().as_str()); - println!("status: {}", r.status()); - } - - let access_token = res.access_token(); - let at = access_token.secret(); - let exp = dangerous_insecure_decode::(&at); - - if let Ok(e) = exp { - (*inner_local.lock().unwrap()).exp_time = e.claims.exp - } - - (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); - if let Some(t) = res.refresh_token() { - // println!("New Refresh Token: {}", t.secret()); - (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); - } - #[cfg(debug_assertions)] { - println!("Central post succeeded"); - } - } else { - println!("Central post failed: {}", r.status().to_string()); - println!("hit url: {}", r.url().as_str()); - println!("Status: {}", r.status()); - (*inner_local.lock().unwrap()).exp_time = 0; + return true; + }); + + match verified { + Some(verified) => { + if !verified { + println!("not verified."); (*inner_local.lock().unwrap()).running = false; + break; } }, - Err(e) => { - - println!("Central post failed: {}", e.to_string()); - println!("hit url: {}", e.url().unwrap().as_str()); - println!("Status: {}", e.status().unwrap()); - (*inner_local.lock().unwrap()).exp_time = 0; + None => { + println!("no verification performed?"); (*inner_local.lock().unwrap()).running = false; + break; + } + } + + + match res.id_token() { + Some(id_token) => { + let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; + #[cfg(debug_assertions)] { + println!("New ID token: {}", id_token.to_string()); + } + let client = reqwest::blocking::Client::new(); + let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); + + match r { + Ok(r) => { + if r.status().is_success() { + #[cfg(debug_assertions)] { + println!("hit url: {}", r.url().as_str()); + println!("status: {}", r.status()); + } + + let access_token = res.access_token(); + let at = access_token.secret(); + let exp = dangerous_insecure_decode::(&at); + + if let Ok(e) = exp { + (*inner_local.lock().unwrap()).exp_time = e.claims.exp + } + + (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); + if let Some(t) = res.refresh_token() { + // println!("New Refresh Token: {}", t.secret()); + (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); + } + #[cfg(debug_assertions)] { + println!("Central post succeeded"); + } + } else { + println!("Central post failed: {}", r.status().to_string()); + println!("hit url: {}", r.url().as_str()); + println!("Status: {}", r.status()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; + } + }, + Err(e) => { + + println!("Central post failed: {}", e.to_string()); + println!("hit url: {}", e.url().unwrap().as_str()); + println!("Status: {}", e.status().unwrap()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; + } + } + }, + None => { + println!("no id token?!?"); } } }, - None => { - println!("no id token?!?"); + Err(e) => { + println!("token error: {}", e); } } + } else { + println!("token response??"); } } else { println!("waiting to refresh"); From 057356a725ada153b3a132bc78369620bd0b3dc6 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 16 Dec 2021 22:07:17 -0800 Subject: [PATCH 62/68] Just some comments --- service/OneService.cpp | 2 -- zeroidc/src/ext.rs | 38 ++++++++++++-------------------------- zeroidc/src/lib.rs | 30 ++++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 418921536..78dc65bd3 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -261,9 +261,7 @@ public: // fprintf(stderr, "Client ID: %s\n", _config.ssoClientID); // fprintf(stderr, "Central Auth URL: %s\n", _config.centralAuthURL); - char buf[17] = {}; _idc = zeroidc::zeroidc_new( - Utils::hex(_config.nwid, buf), _config.issuerURL, _config.ssoClientID, _config.centralAuthURL, diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index d39f82452..9bf0181bc 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -1,3 +1,15 @@ +/* + * Copyright (c)2021 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: 2025-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. + */ + use std::ffi::{CStr, CString}; use std::os::raw::c_char; use url::{Url}; @@ -6,17 +18,11 @@ use crate::ZeroIDC; #[no_mangle] pub extern "C" fn zeroidc_new( - network_id: *const c_char, issuer: *const c_char, client_id: *const c_char, auth_endpoint: *const c_char, web_listen_port: u16, ) -> *mut ZeroIDC { - if network_id.is_null() { - println!("network_id is null"); - return std::ptr::null_mut(); - - } if issuer.is_null() { println!("issuer is null"); return std::ptr::null_mut(); @@ -32,12 +38,10 @@ pub extern "C" fn zeroidc_new( return std::ptr::null_mut(); } - let network_id = unsafe {CStr::from_ptr(network_id) }; let issuer = unsafe { CStr::from_ptr(issuer) }; let client_id = unsafe { CStr::from_ptr(client_id) }; let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) }; match ZeroIDC::new( - network_id.to_str().unwrap(), issuer.to_str().unwrap(), client_id.to_str().unwrap(), auth_endpoint.to_str().unwrap(), @@ -101,24 +105,6 @@ pub extern "C" fn zeroidc_get_exp_time(ptr: *mut ZeroIDC) -> u64 { id.get_exp_time() } -// #[no_mangle] -// pub extern "C" fn zeroidc_process_form_post(ptr: *mut ZeroIDC, body: *const c_char) -> bool { -// let idc = unsafe { -// assert!(!ptr.is_null()); -// &mut *ptr -// }; - -// if body.is_null() { -// println!("body is null"); -// return false -// } - -// let body = unsafe { CStr::from_ptr(body) } -// .to_str().unwrap().to_string(); - -// false -// } - #[no_mangle] pub extern "C" fn zeroidc_set_nonce_and_csrf( ptr: *mut ZeroIDC, diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 188064312..cedaf90c1 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -1,3 +1,15 @@ +/* + * Copyright (c)2021 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: 2025-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. + */ + pub mod ext; extern crate base64; @@ -27,7 +39,6 @@ pub struct ZeroIDC { struct Inner { running: bool, - network_id: String, auth_endpoint: String, oidc_thread: Option>, oidc_client: Option, @@ -80,7 +91,6 @@ fn systemtime_strftime(dt: T, format: &str) -> String impl ZeroIDC { fn new( - network_id: &str, issuer: &str, client_id: &str, auth_ep: &str, @@ -89,7 +99,6 @@ impl ZeroIDC { let idc = ZeroIDC { inner: Arc::new(Mutex::new(Inner { running: false, - network_id: network_id.to_string(), auth_endpoint: auth_ep.to_string(), oidc_thread: None, oidc_client: None, @@ -162,7 +171,7 @@ impl ZeroIDC { // Keep a copy of the initial nonce used to get the tokens // Will be needed later when verifying the responses from refresh tokens let nonce = (*inner_local.lock().unwrap()).nonce.clone(); - + while running { let exp = UNIX_EPOCH + Duration::from_secs((*inner_local.lock().unwrap()).exp_time); let now = SystemTime::now(); @@ -200,6 +209,7 @@ impl ZeroIDC { } }; + // verify & validate claims let verified = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { let claims = match id.claims(&c.id_token_verifier(), &n) { Ok(c) => c, @@ -271,6 +281,10 @@ impl ZeroIDC { let access_token = res.access_token(); let at = access_token.secret(); + // yes this function is called `dangerous_insecure_decode` + // and it doesn't validate the jwt token signature, + // but if we've gotten this far, our claims have already + // been validated up above let exp = dangerous_insecure_decode::(&at); if let Ok(e) = exp { @@ -350,10 +364,6 @@ impl ZeroIDC { } } - fn get_network_id(&mut self) -> String { - return (*self.inner.lock().unwrap()).network_id.clone(); - } - fn get_exp_time(&mut self) -> u64 { return (*self.inner.lock().unwrap()).exp_time; } @@ -438,6 +448,8 @@ impl ZeroIDC { let r = c.exchange_code(AuthorizationCode::new(code.to_string())) .set_pkce_verifier(verifier) .request(http_client); + + // validate the token hashes match r { Ok(res) =>{ let n = match i.nonce.clone() { @@ -525,6 +537,8 @@ impl ZeroIDC { } let at = tok.access_token().secret(); + + // see previous note about this function's use let exp = dangerous_insecure_decode::(&at); if let Ok(e) = exp { i.exp_time = e.claims.exp From cf411efd1deca25e76223a13a4a678b7009e3852 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 3 Jan 2022 08:36:16 -0800 Subject: [PATCH 63/68] a little cleanup --- zeroidc/src/lib.rs | 116 ++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index cedaf90c1..5083b44ce 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -22,7 +22,7 @@ use bytes::Bytes; use jsonwebtoken::{dangerous_insecure_decode}; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; -use openidconnect::{AccessToken, AccessTokenHash, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, NonceVerifier, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; +use openidconnect::{AccessToken, AccessTokenHash, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; use serde::{Deserialize, Serialize}; use std::str::from_utf8; use std::sync::{Arc, Mutex}; @@ -72,6 +72,7 @@ fn nonce_func(nonce: String) -> Box Nonce> { return Box::new(move || Nonce::new(nonce.to_string())); } +#[cfg(debug_assertions)] fn systemtime_strftime(dt: T, format: &str) -> String where T: Into { @@ -244,82 +245,87 @@ impl ZeroIDC { return true; }); - match verified { + let v = match verified { Some(verified) => { if !verified { println!("not verified."); (*inner_local.lock().unwrap()).running = false; - break; + false + } else { + true } }, None => { println!("no verification performed?"); (*inner_local.lock().unwrap()).running = false; - break; + false } - } + }; + if v { + match res.id_token() { + Some(id_token) => { + let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; + #[cfg(debug_assertions)] { + println!("New ID token: {}", id_token.to_string()); + } + let client = reqwest::blocking::Client::new(); + let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); - match res.id_token() { - Some(id_token) => { - let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; - #[cfg(debug_assertions)] { - println!("New ID token: {}", id_token.to_string()); - } - let client = reqwest::blocking::Client::new(); - let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) - .form(¶ms) - .send(); + match r { + Ok(r) => { + if r.status().is_success() { + #[cfg(debug_assertions)] { + println!("hit url: {}", r.url().as_str()); + println!("status: {}", r.status()); + } - match r { - Ok(r) => { - if r.status().is_success() { - #[cfg(debug_assertions)] { + let access_token = res.access_token(); + let at = access_token.secret(); + // yes this function is called `dangerous_insecure_decode` + // and it doesn't validate the jwt token signature, + // but if we've gotten this far, our claims have already + // been validated up above + let exp = dangerous_insecure_decode::(&at); + + if let Ok(e) = exp { + (*inner_local.lock().unwrap()).exp_time = e.claims.exp + } + + (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); + if let Some(t) = res.refresh_token() { + // println!("New Refresh Token: {}", t.secret()); + (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); + } + #[cfg(debug_assertions)] { + println!("Central post succeeded"); + } + } else { + println!("Central post failed: {}", r.status().to_string()); println!("hit url: {}", r.url().as_str()); - println!("status: {}", r.status()); + println!("Status: {}", r.status()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; } - - let access_token = res.access_token(); - let at = access_token.secret(); - // yes this function is called `dangerous_insecure_decode` - // and it doesn't validate the jwt token signature, - // but if we've gotten this far, our claims have already - // been validated up above - let exp = dangerous_insecure_decode::(&at); + }, + Err(e) => { - if let Ok(e) = exp { - (*inner_local.lock().unwrap()).exp_time = e.claims.exp - } - - (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); - if let Some(t) = res.refresh_token() { - // println!("New Refresh Token: {}", t.secret()); - (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); - } - #[cfg(debug_assertions)] { - println!("Central post succeeded"); - } - } else { - println!("Central post failed: {}", r.status().to_string()); - println!("hit url: {}", r.url().as_str()); - println!("Status: {}", r.status()); + println!("Central post failed: {}", e.to_string()); + println!("hit url: {}", e.url().unwrap().as_str()); + println!("Status: {}", e.status().unwrap()); (*inner_local.lock().unwrap()).exp_time = 0; (*inner_local.lock().unwrap()).running = false; } - }, - Err(e) => { - - println!("Central post failed: {}", e.to_string()); - println!("hit url: {}", e.url().unwrap().as_str()); - println!("Status: {}", e.status().unwrap()); - (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; } + }, + None => { + println!("no id token?!?"); } - }, - None => { - println!("no id token?!?"); } + } else { + println!("claims not verified"); } }, Err(e) => { From 50b866c5cfb96a93225fcf70de7f01f3f6e597da Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 3 Jan 2022 17:25:30 -0800 Subject: [PATCH 64/68] clean up & pass along errors from ZeroIDC::new() method --- zeroidc/Cargo.lock | 1 + zeroidc/Cargo.toml | 1 + zeroidc/src/error.rs | 23 +++++++++++++++++++++++ zeroidc/src/lib.rs | 38 ++++++++------------------------------ 4 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 zeroidc/src/error.rs diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock index 24c126e8c..889142175 100644 --- a/zeroidc/Cargo.lock +++ b/zeroidc/Cargo.lock @@ -1500,6 +1500,7 @@ dependencies = [ "openidconnect", "reqwest", "serde", + "thiserror", "time 0.3.5", "url", ] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index e74c9efaa..1ffe3abb7 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -20,6 +20,7 @@ jsonwebtoken = "7.2.0" serde = "1.0.130" time = { version = "0.3.5", features = ["formatting"] } bytes = "1.1.0" +thiserror = "1" [build-dependencies] cbindgen = "0.20.0" diff --git a/zeroidc/src/error.rs b/zeroidc/src/error.rs new file mode 100644 index 000000000..8c2bdf6ac --- /dev/null +++ b/zeroidc/src/error.rs @@ -0,0 +1,23 @@ +/* + * Copyright (c)2022 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: 2025-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. + */ + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ZeroIDCError +{ + #[error(transparent)] + DiscoveryError(#[from] openidconnect::DiscoveryError>), + + #[error(transparent)] + ParseError(#[from] url::ParseError), +} diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 5083b44ce..f9ceaabb4 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -10,6 +10,7 @@ * of this software will be governed by version 2.0 of the Apache License. */ +pub mod error; pub mod ext; extern crate base64; @@ -18,6 +19,8 @@ extern crate openidconnect; extern crate time; extern crate url; +use crate::error::ZeroIDCError; + use bytes::Bytes; use jsonwebtoken::{dangerous_insecure_decode}; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; @@ -96,7 +99,7 @@ impl ZeroIDC { client_id: &str, auth_ep: &str, local_web_port: u16, - ) -> Result { + ) -> Result { let idc = ZeroIDC { inner: Arc::new(Mutex::new(Inner { running: false, @@ -114,39 +117,14 @@ impl ZeroIDC { })), }; - let iss = match IssuerUrl::new(issuer.to_string()) { - Ok(i) => i, - Err(e) => { - println!("Error generating Issuer URL"); - return Err(e.to_string()); - } + let iss = IssuerUrl::new(issuer.to_string())?; - }; - - let provider_meta = match CoreProviderMetadata::discover(&iss, http_client) { - Ok(m) => m, - Err(e) => { - println!("Error discovering provider metadata"); - return Err(e.to_string()); - }, - }; + let provider_meta = CoreProviderMetadata::discover(&iss, http_client)?; let r = format!("http://localhost:{}/sso", local_web_port); - let redir_url = match Url::parse(&r) { - Ok(s) => s, - Err(e) => { - println!("Error generating redirect URL"); - return Err(e.to_string()); - } - }; + let redir_url = Url::parse(&r)?; - let redirect = match RedirectUrl::new(redir_url.to_string()) { - Ok(s) => s, - Err(e) => { - println!("Error generating RedirectURL instance from string: {}", redir_url.to_string()); - return Err(e.to_string()); - } - }; + let redirect = RedirectUrl::new(redir_url.to_string())?; (*idc.inner.lock().unwrap()).oidc_client = Some( CoreClient::from_provider_metadata( From 8ea192fcabc496769aca074c2f18878aebaa0673 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 5 Jan 2022 08:44:01 -0800 Subject: [PATCH 65/68] update dependency specs --- zeroidc/Cargo.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 1ffe3abb7..e630bcb01 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -12,15 +12,15 @@ crate-type = ["staticlib","rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -openidconnect = "2.1.2" -base64 = "0.13.0" -url = "2.2.2" -reqwest = "0.11.7" -jsonwebtoken = "7.2.0" -serde = "1.0.130" -time = { version = "0.3.5", features = ["formatting"] } -bytes = "1.1.0" +openidconnect = "2.1" +base64 = "0.13" +url = "2.2" +reqwest = "0.11" +jsonwebtoken = "7.2" +serde = "1.0" +time = { version = "0.3", features = ["formatting"] } +bytes = "1.1" thiserror = "1" [build-dependencies] -cbindgen = "0.20.0" +cbindgen = "0.20" From 084727c54acf9c67f50310a0204d3d9523072b1e Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 5 Jan 2022 11:55:25 -0800 Subject: [PATCH 66/68] link zeroidc to selftest --- make-mac.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make-mac.mk b/make-mac.mk index a128aa41b..4655c8618 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -145,7 +145,7 @@ core: libzerotiercore.a # $(STRIP) zerotier selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o - $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LIBS) + $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LIBS) zeroidc/target/libzeroidc.a $(STRIP) zerotier-selftest zerotier-selftest: selftest From 953e62f1033ed45afd97cf5f143973a25af118ae Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 5 Jan 2022 11:55:47 -0800 Subject: [PATCH 67/68] Clean up some extraneous output in release mode --- service/OneService.cpp | 8 +------- zeroidc/src/lib.rs | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index b9cbfea1c..6c4be6b54 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1659,15 +1659,9 @@ public: } else if (ps[0] == "sso") { // SSO redirect handling - fprintf(stderr, "sso get\n"); - fprintf(stderr, "path: %s\n", path.c_str()); - fprintf(stderr, "body: %s\n", body.c_str()); - const char* state = zeroidc::zeroidc_get_url_param_value("state", path.c_str()); const char* nwid = zeroidc::zeroidc_network_id_from_state(state); - fprintf(stderr, "state: %s\n", state); - fprintf(stderr, "nwid: %s\n", nwid); - + const uint64_t id = Utils::hexStrToU64(nwid); Mutex::Lock l(_nets_m); if (_nets.find(id) != _nets.end()) { diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index f9ceaabb4..a8fc9cfb4 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -314,6 +314,7 @@ impl ZeroIDC { println!("token response??"); } } else { + #[cfg(debug_assertions)] println!("waiting to refresh"); } } else { From 3d4d087f070912342cef88f4f24023103618d017 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 5 Jan 2022 15:43:45 -0800 Subject: [PATCH 68/68] make some impl functions pub --- zeroidc/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index a8fc9cfb4..0bca86890 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -94,7 +94,7 @@ fn systemtime_strftime(dt: T, format: &str) -> String } impl ZeroIDC { - fn new( + pub fn new( issuer: &str, client_id: &str, auth_ep: &str, @@ -330,7 +330,7 @@ impl ZeroIDC { } } - fn stop(&mut self) { + pub fn stop(&mut self) { let local = self.inner.clone(); if (*local.lock().unwrap()).running { if let Some(u) = (*local.lock().unwrap()).oidc_thread.take() { @@ -339,7 +339,7 @@ impl ZeroIDC { } } - fn is_running(&mut self) -> bool { + pub fn is_running(&mut self) -> bool { let local = Arc::clone(&self.inner); if (*local.lock().unwrap()).running { @@ -349,11 +349,11 @@ impl ZeroIDC { } } - fn get_exp_time(&mut self) -> u64 { + pub fn get_exp_time(&mut self) -> u64 { return (*self.inner.lock().unwrap()).exp_time; } - fn set_nonce_and_csrf(&mut self, csrf_token: String, nonce: String) { + pub fn set_nonce_and_csrf(&mut self, csrf_token: String, nonce: String) { let local = Arc::clone(&self.inner); (*local.lock().expect("can't lock inner")).as_opt().map(|i| { let need_verifier = match i.pkce_verifier { @@ -410,7 +410,7 @@ impl ZeroIDC { }); } - fn auth_url(&self) -> String { + pub fn auth_url(&self) -> String { let url = (*self.inner.lock().expect("can't lock inner")).as_opt().map(|i| { match i.url.clone() { Some(u) => u.to_string(), @@ -424,7 +424,7 @@ impl ZeroIDC { } } - fn do_token_exchange(&mut self, code: &str) -> String { + pub fn do_token_exchange(&mut self, code: &str) -> String { let local = Arc::clone(&self.inner); let mut should_start = false; let res = (*local.lock().unwrap()).as_opt().map(|i| {