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"]