diff --git a/.gitignore b/.gitignore index cb91b8a62..cca0cd643 100755 --- a/.gitignore +++ b/.gitignore @@ -122,6 +122,8 @@ attic/world/*.c25519 attic/world/mkworld workspace/ workspace2/ +zeroidc/target/ +tmp/ #snapcraft specifics /parts/ 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/DB.hpp b/controller/DB.hpp index 0a5d784c2..d0641d72e 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -40,6 +40,32 @@ namespace ZeroTier { +struct AuthInfo +{ +public: + AuthInfo() + : enabled(false) + , version(0) + , authenticationURL() + , authenticationExpiryTime(0) + , issuerURL() + , centralAuthURL() + , ssoNonce() + , ssoState() + , ssoClientID() + {} + + bool enabled; + uint64_t version; + std::string authenticationURL; + uint64_t authenticationExpiryTime; + std::string issuerURL; + std::string centralAuthURL; + std::string ssoNonce; + std::string ssoState; + std::string ssoClientID; +}; + /** * Base class with common infrastructure for all controller DB implementations */ @@ -108,7 +134,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..054bcddc5 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) @@ -1360,29 +1337,61 @@ 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; + AuthInfo info; if (networkSSOEnabled && !memberSSOExempt) { - authenticationURL = _db.getSSOAuthURL(member, _ssoRedirectURL); + // 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) { - if (!authenticationURL.empty()) { + fprintf(stderr, "Handling expired member\n"); + if (info.version == 0) { + if (!info.authenticationURL.empty()) { + _db.networkMemberSSOHasExpired(nwid, now); + onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt()); + + Dictionary<4096> authInfo; + 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()); + + DB::cleanMember(member); + _db.save(member,true); + + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); + return; + } + } + else if (info.version == 1) { _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<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.save(member, true); + 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); + } } else if (authorized) { + fprintf(stderr, "Setting member will expire to: %lld\n", authenticationExpiryTime); _db.memberWillExpire(authenticationExpiryTime, nwid, identity.address().toInt()); } } @@ -1452,9 +1461,36 @@ 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.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()) { + 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 5f71a3699..e199f05bb 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -80,6 +81,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 @@ -336,7 +359,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 +370,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 +415,61 @@ 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.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(); + 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 { 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(), + url_encode(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.issuerURL = issuer; + info.ssoNonce = nonce; + info.ssoState = std::string(state_hex) + "_" +networkId; + info.centralAuthURL = redirectURL; + fprintf( + stderr, + "ssoClientID: %s\nissuerURL: %s\nssoNonce: %s\nssoState: %s\ncentralAuthURL: %s\n", + 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()); } @@ -432,7 +480,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() @@ -1025,23 +1073,47 @@ 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); - 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 { - auto c = _pool->borrow(); 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, " @@ -1058,21 +1130,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", @@ -1080,7 +1152,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()) { @@ -1095,21 +1167,21 @@ void PostgreSQL::commitThread() } if (ipAssignError) { 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); + 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); @@ -1117,24 +1189,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()); - auto c = _pool->borrow(); 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 @@ -1163,25 +1233,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"]; @@ -1194,7 +1264,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"]; @@ -1221,12 +1291,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 << "{"; @@ -1244,12 +1312,11 @@ void PostgreSQL::commitThread() id, domain, s); w.commit(); - _pool->unborrow(c); - 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); @@ -1257,23 +1324,20 @@ 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()); } } 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"]; + std::string networkId = config["nwid"]; pqxx::result res = w.exec_params0("UPDATE ztc_network SET deleted = true WHERE id = $1", 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,18 +1345,16 @@ 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"]; - 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", 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 +1364,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)); } @@ -1321,6 +1385,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; @@ -1330,15 +1395,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; @@ -1355,16 +1421,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; } @@ -1372,32 +1432,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(); 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/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..5c2787570 100644 --- a/ext/central-controller-docker/Dockerfile.builder +++ b/ext/central-controller-docker/Dockerfile.builder @@ -9,4 +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/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/include/ZeroTierOne.h b/include/ZeroTierOne.h index fee3b6939..4fa1a4310 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,31 @@ typedef struct * Time that current authentication expires. only valid if ssoEnabled is true */ uint64_t authenticationExpiryTime; + + /** + * OIDC issuer URL. + */ + char issuerURL[2048]; + + /** + * central base URL. + */ + char centralAuthURL[2048]; + + /** + * sso nonce + */ + char ssoNonce[128]; + + /** + * sso state + */ + char ssoState[256]; + + /** + * oidc client id + */ + char ssoClientID[256]; } ZT_VirtualNetworkConfig; /** diff --git a/make-linux.mk b/make-linux.mk index 6023069e2..fe1f08e38 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -9,7 +9,7 @@ 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?= DESTDIR?= @@ -18,7 +18,7 @@ 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) @@ -274,7 +282,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 @@ -321,6 +329,8 @@ zerotier-idtool: zerotier-one zerotier-cli: zerotier-one ln -sf zerotier-one zerotier-cli +$(ONE_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) @@ -339,7 +349,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 @@ -361,6 +371,9 @@ debug: FORCE make ZT_DEBUG=1 one make ZT_DEBUG=1 selftest +zeroidc: FORCE + 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 # lived here. Folks got scripts. diff --git a/make-mac.mk b/make-mac.mk index 7438ad9ed..4655c8618 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) @@ -73,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) @@ -80,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) @@ -103,8 +109,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: 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 ln -sf zerotier-one zerotier-cli @@ -112,6 +118,13 @@ one: $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent 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 $(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 @@ -119,6 +132,8 @@ zerotier-idtool: one zerotier-cli: one +$(ONE_OBJS): zeroidc + libzerotiercore.a: $(CORE_OBJS) ar rcs libzerotiercore.a $(CORE_OBJS) ranlib libzerotiercore.a @@ -130,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 @@ -157,7 +172,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/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5a2a94642..5fc38be02 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: { @@ -191,25 +191,55 @@ 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); 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(tPtr, authenticationURL); + } + } else if (authVer == 1) { + char issuerURL[2048] = { 0 }; + char centralAuthURL[2048] = { 0 }; + char ssoNonce[64] = { 0 }; + char ssoState[128] = {0}; + char ssoClientID[256] = { 0 }; + + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, issuerURL, sizeof(issuerURL)) > 0) { + issuerURL[sizeof(issuerURL) - 1] = 0; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, centralAuthURL, sizeof(centralAuthURL))>0) { + centralAuthURL[sizeof(centralAuthURL) - 1] = 0; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_NONCE, ssoNonce, sizeof(ssoNonce)) > 0) { + ssoNonce[sizeof(ssoNonce) - 1] = 0; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_STATE, ssoState, sizeof(ssoState)) > 0) { + ssoState[sizeof(ssoState) - 1] = 0; + } + if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, ssoClientID, sizeof(ssoClientID)) > 0) { + ssoClientID[sizeof(ssoClientID) - 1] = 0; + } + + network->setAuthenticationRequired(tPtr, issuerURL, centralAuthURL, ssoClientID, ssoNonce, ssoState); } } + } else { + fprintf(stderr, "authinfo??????\n"); + network->setAuthenticationRequired(tPtr, ""); } - if (noUrl) - network->setAuthenticationRequired(""); } } break; diff --git a/node/Network.cpp b/node/Network.cpp index f3138f3ac..c77f94a6d 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; } @@ -1434,8 +1434,14 @@ 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->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); } void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup) @@ -1542,4 +1548,25 @@ Membership &Network::_membership(const Address &a) return _memberships[a]; } +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; + + 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 33de16911..c201a6314 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -205,31 +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(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 @@ -413,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; @@ -459,6 +472,6 @@ private: AtomicCounter __refCount; }; -} // namespace ZeroTier +} // namespace ZeroTier #endif diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 6d148cc45..13a9313aa 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -182,12 +182,25 @@ 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_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; } delete tmp; @@ -374,18 +387,52 @@ 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_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; + } + 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->issuerURL[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..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 @@ -180,10 +180,39 @@ 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" +// oidc issuer URL +#define ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL "iurl" +// 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" +// issuer URL +#define ZT_AUTHINFO_DICT_KEY_ISSUER_URL "iU" +// 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 +271,12 @@ public: dnsCount(0), ssoEnabled(false), authenticationURL(), - authenticationExpiryTime(0) + authenticationExpiryTime(0), + issuerURL(), + centralAuthURL(), + ssoNonce(), + ssoState(), + ssoClientID() { name[0] = 0; memset(specialists, 0, sizeof(uint64_t)*ZT_MAX_NETWORK_SPECIALISTS); @@ -250,6 +284,12 @@ 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(issuerURL, 0, sizeof(issuerURL)); + memset(centralAuthURL, 0, sizeof(centralAuthURL)); + memset(ssoNonce, 0, sizeof(ssoNonce)); + memset(ssoState, 0, sizeof(ssoState)); + memset(ssoClientID, 0, sizeof(ssoClientID)); } /** @@ -619,6 +659,11 @@ public: */ bool ssoEnabled; + /** + * SSO verison + */ + uint64_t ssoVersion; + /** * Authentication URL if authentication is required */ @@ -626,8 +671,35 @@ public: /** * Time current authentication expires or 0 if external authentication is disabled + * + * Not used if authVersion >= 1 */ uint64_t authenticationExpiryTime; + + /** + * OIDC issuer URL + */ + char issuerURL[2048]; + + /** + * central base URL. + */ + char centralAuthURL[2048]; + + /** + * sso nonce + */ + char ssoNonce[128]; + + /** + * sso state + */ + char ssoState[256]; + + /** + * oidc client id + */ + char ssoClientID[256]; }; } // namespace ZeroTier diff --git a/node/Node.cpp b/node/Node.cpp index 5b0fa8cc5..a0dd03fc1 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -735,14 +735,16 @@ 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"); + break; + } default: break; } diff --git a/service/OneService.cpp b/service/OneService.cpp index 3745f0d30..6c4be6b54 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -53,6 +53,8 @@ #include "OneService.hpp" #include "SoftwareUpdater.hpp" +#include + #ifdef __WINDOWS__ #include #include @@ -145,6 +147,205 @@ 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) + { + // 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_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) { + char nwbuf[17] = {}; + 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) + { + 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); + + _idc = zeroidc::zeroidc_new( + _config.issuerURL, + _config.ssoClientID, + _config.centralAuthURL, + _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); + } + + zeroidc::zeroidc_set_nonce_and_csrf( + _idc, + _config.ssoState, + _config.ssoNonce + ); + + const char* url = zeroidc::zeroidc_get_auth_url(_idc); + 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; + } + + const char* getAuthURL() { + if (_idc != nullptr) { + return zeroidc::zeroidc_get_auth_url(_idc); + } + fprintf(stderr, "_idc is null\n"); + return ""; + } + + const char* doTokenExchange(const char *code) { + if (_idc == nullptr) { + fprintf(stderr, "ainfo or idc null\n"); + return ""; + } + + const char *ret = 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(_idc); + memcpy(_config.authenticationURL, url, strlen(url)); + _config.authenticationURL[strlen(url)] = 0; + return ret; + } + + 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; + ZT_VirtualNetworkConfig _config; // memcpy() of raw config from core + std::vector _managedIps; + std::map< InetAddress, SharedPtr > _managedRoutes; + OneService::NetworkSettings _settings; + zeroidc::ZeroIDC *_idc; +}; + namespace { static const InetAddress NULL_INET_ADDR; @@ -171,12 +372,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; @@ -185,75 +386,81 @@ 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)); } } nj["dns"] = m; - - nj["authenticationURL"] = nc->authenticationURL; - nj["authenticationExpiryTime"] = nc->authenticationExpiryTime; - nj["ssoEnabled"] = nc->ssoEnabled; + if (ns.config().ssoEnabled) { + const char* authURL = ns.getAuthURL(); + //fprintf(stderr, "Auth URL: %s\n", authURL); + nj["authenticationURL"] = authURL; + nj["authenticationExpiryTime"] = (ns.getExpiryTime()*1000); + nj["ssoEnabled"] = ns.config().ssoEnabled; + } } static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer) @@ -525,32 +732,8 @@ public: // Deadline for the next background task service function volatile int64_t _nextBackgroundTaskDeadline; - // Configured networks - struct NetworkState - { - NetworkState() : - tap((EthernetTap *)0) - { - // 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(); - } - - std::shared_ptr tap; - ZT_VirtualNetworkConfig config; // memcpy() of raw config from core - std::vector managedIps; - std::map< InetAddress, SharedPtr > managedRoutes; - NetworkSettings settings; - }; std::map _nets; Mutex _nets_m; @@ -888,7 +1071,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 +1096,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 +1297,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(); } @@ -1129,10 +1312,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)); } } @@ -1157,32 +1340,23 @@ 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; } 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.settings = 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)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; } @@ -1386,17 +1560,17 @@ 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); + _networkToJson(nj, ns); res.push_back(nj); } @@ -1405,19 +1579,20 @@ 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 { + 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) { @@ -1482,7 +1657,63 @@ public: } else scode = 404; } - } else scode = 401; // isAuth == false + } else if (ps[0] == "sso") { + // SSO redirect handling + const char* state = zeroidc::zeroidc_get_url_param_value("state", path.c_str()); + const char* nwid = zeroidc::zeroidc_network_id_from_state(state); + + 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; + responseBody = "\ +\ +\ +\ +\ +





\ +
\ +
\ +
Authentication Successful. You may now access the network.
\ +
\ +\ +"; + responseContentType = "text/html"; + return scode; + } else { + scode = 404; + } + } else { + scode = 401; // isAuth == false && !sso + } } else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) { if (isAuth) { if (ps[0] == "bond") { @@ -1579,37 +1810,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; @@ -1618,8 +1853,10 @@ public: scode = _controller->handleControlPlaneHttpPOST(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); else scode = 404; } - - } else scode = 401; // isAuth == false + } + else { + scode = 401; // isAuth == false + } } else if (httpMethod == HTTP_DELETE) { if (isAuth) { @@ -1650,7 +1887,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; @@ -1971,12 +2207,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; @@ -1986,7 +2222,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: @@ -1994,7 +2230,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; } @@ -2018,18 +2254,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)); } } @@ -2038,39 +2274,39 @@ 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)); } } #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 - n.managedIps.swap(newManagedIps); + n.setManagedIps(newManagedIps); } if (syncRoutes) { // 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()); + 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. @@ -2094,7 +2330,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; @@ -2107,48 +2343,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 } @@ -2417,16 +2653,16 @@ 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) { + 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), @@ -2435,7 +2671,7 @@ public: nwid, friendlyName, StapFrameHandler, - (void *)this); + (void *)this)); *nuptr = (void *)&n; char nlcpath[256]; @@ -2449,28 +2685,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__ @@ -2491,20 +2727,21 @@ 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 // // 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 syncManagedStuff(n,true,true,true); - n.tap->setMtu(nwc->mtu); + n.tap()->setMtu(nwc->mtu); } else { _nets.erase(nwid); return -999; // tap init failed @@ -2513,12 +2750,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()); + 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)) @@ -2533,7 +2770,6 @@ public: _nets.erase(nwid); } break; - } return 0; } @@ -2933,9 +3169,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) @@ -2944,8 +3180,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; @@ -3117,14 +3353,14 @@ 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; } #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 ff142c600..8c33b612e 100644 --- a/windows/ZeroTierOne.sln +++ b/windows/ZeroTierOne.sln @@ -4,9 +4,14 @@ 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 +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/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/.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"] diff --git a/zeroidc/Cargo.lock b/zeroidc/Cargo.lock new file mode 100644 index 000000000..889142175 --- /dev/null +++ b/zeroidc/Cargo.lock @@ -0,0 +1,1506 @@ +# 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 0.1.43", + "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 = "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" +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 = "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" +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-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-task", + "memchr", + "pin-project-lite", + "pin-utils", + "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.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "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.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[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]] +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 = "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" +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 = "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +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" +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" +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", + "http", + "rand", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d523cf32bdf7696f36bc4198a42c34b65f0227b97f2f501ebfbe016baa5bc52" +dependencies = [ + "base64 0.13.0", + "chrono", + "http", + "itertools", + "log", + "num-bigint 0.4.3", + "oauth2", + "rand", + "ring", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_path_to_error", + "thiserror", + "untrusted", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +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" +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 = "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-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.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bea77bc708afa10e59905c3d4af7c8fd43c9214251673095ff8b14345fcbc5" +dependencies = [ + "base64 0.13.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "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", + "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.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" +dependencies = [ + "log", + "ring", + "sct", + "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 0.13.0", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" +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 = "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" +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", + "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 = "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" +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-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 0.22.0", +] + +[[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 = "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" +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.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" +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 0.21.4", +] + +[[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 = [ + "base64 0.13.0", + "bytes", + "cbindgen", + "jsonwebtoken", + "openidconnect", + "reqwest", + "serde", + "thiserror", + "time 0.3.5", + "url", +] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml new file mode 100644 index 000000000..e630bcb01 --- /dev/null +++ b/zeroidc/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "zeroidc" +version = "0.1.0" +edition = "2018" +build = "build.rs" +publish = false + +[lib] +crate-type = ["staticlib","rlib"] + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +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" diff --git a/zeroidc/build.rs b/zeroidc/build.rs new file mode 100644 index 000000000..0b417c084 --- /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!("{}.h", 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/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/ext.rs b/zeroidc/src/ext.rs new file mode 100644 index 000000000..9bf0181bc --- /dev/null +++ b/zeroidc/src/ext.rs @@ -0,0 +1,220 @@ +/* + * 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}; + +use crate::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 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( + issuer.to_str().unwrap(), + client_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_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_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_set_nonce_and_csrf( + ptr: *mut ZeroIDC, + csrf_token: *const c_char, + nonce: *const c_char) { + let idc = unsafe { + assert!(!ptr.is_null()); + &mut *ptr + }; + + if csrf_token.is_null() { + println!("csrf_token is null"); + return; + } + + if nonce.is_null() { + println!("nonce is null"); + return; + } + + 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(); + + idc.set_nonce_and_csrf(csrf_token, nonce); +} + +#[no_mangle] +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 idc = unsafe { + &mut *ptr + }; + + let s = CString::new(idc.auth_url()).unwrap(); + return s.into_raw(); +} + +#[no_mangle] +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 std::ptr::null(); + } + + if code.is_null() { + println!("code is null"); + return std::ptr::null(); + } + let idc = unsafe { + &mut *idc + }; + + let code = unsafe{CStr::from_ptr(code)}.to_str().unwrap(); + + let ret = idc.do_token_exchange( code); + let ret = CString::new(ret).unwrap(); + return ret.into_raw(); +} + +#[no_mangle] +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; + let url = Url::parse(&url).unwrap(); + + let pairs = url.query_pairs(); + for p in pairs { + if p.0 == param { + 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(); +} diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs new file mode 100644 index 000000000..0bca86890 --- /dev/null +++ b/zeroidc/src/lib.rs @@ -0,0 +1,582 @@ +/* + * 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 error; +pub mod ext; + +extern crate base64; +extern crate bytes; +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}; +use openidconnect::reqwest::http_client; +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}; +use std::thread::{sleep, spawn, JoinHandle}; +use std::time::{SystemTime, UNIX_EPOCH, Duration}; +use time::{OffsetDateTime, format_description}; + + +use url::Url; + +pub struct ZeroIDC { + inner: Arc>, +} + +struct Inner { + running: bool, + auth_endpoint: String, + oidc_thread: Option>, + oidc_client: Option, + 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)] +struct Exp { + exp: u64 +} + +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())); +} + +#[cfg(debug_assertions)] +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 { + pub 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, + access_token: None, + refresh_token: None, + exp_time: 0, + + url: None, + csrf_token: None, + nonce: None, + pkce_verifier: None, + })), + }; + + let iss = IssuerUrl::new(issuer.to_string())?; + + let provider_meta = CoreProviderMetadata::discover(&iss, http_client)?; + + let r = format!("http://localhost:{}/sso", local_web_port); + let redir_url = Url::parse(&r)?; + + let redirect = RedirectUrl::new(redir_url.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) { + 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; + 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(); + + #[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)) { + 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.clone() { + Some(n) => n, + None => { + println!("err: no nonce"); + continue; + } + }; + + let id = match res.id_token() { + Some(t) => t, + None => { + println!("err: no id_token"); + continue; + } + }; + + // 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, + Err(e) => { + println!("claims err: {}", e); + return false; + } + }; + + 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 true; + }); + + let v = match verified { + Some(verified) => { + if !verified { + println!("not verified."); + (*inner_local.lock().unwrap()).running = false; + false + } else { + true + } + }, + None => { + println!("no verification performed?"); + (*inner_local.lock().unwrap()).running = false; + 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 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(); + // 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()); + (*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?!?"); + } + } + } else { + println!("claims not verified"); + } + }, + Err(e) => { + println!("token error: {}", e); + } + } + } else { + println!("token response??"); + } + } else { + #[cfg(debug_assertions)] + println!("waiting to refresh"); + } + } else { + println!("no refresh token?"); + } + + sleep(Duration::from_secs(1)); + running = (*inner_local.lock().unwrap()).running; + } + + println!("thread done!") + })); + } + } + + 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() { + u.join().expect("join failed"); + } + } + } + + pub fn is_running(&mut self) -> bool { + let local = Arc::clone(&self.inner); + + if (*local.lock().unwrap()).running { + true + } else { + false + } + } + + pub fn get_exp_time(&mut self) -> u64 { + return (*self.inner.lock().unwrap()).exp_time; + } + + 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 { + None => true, + _ => false, + }; + + let csrf_diff = if let Some(csrf) = i.csrf_token.clone() { + if *csrf.secret() != csrf_token { + true + } else { + false + } + } else { + false + }; + + let nonce_diff = if let Some(n) = i.nonce.clone() { + if *n.secret() != nonce { + true + } else { + false + } + } else { + false + }; + + 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); + } + } + }); + } + + 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(), + _ => "".to_string(), + } + }); + + match url { + Some(url) => url.to_string(), + None => "".to_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| { + 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); + + // validate the token hashes + match r { + Ok(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)] { + println!("token response error: {}", _e.to_string()); + } + + return None; + }, + } + }); + + if let Some(Some(tok)) = token_response { + let id_token = tok.id_token().unwrap(); + #[cfg(debug_assertions)] { + println!("ID token: {}", id_token.to_string()); + } + + let mut split = "".to_string(); + match i.csrf_token.clone() { + Some(csrf_token) => { + split = csrf_token.secret().to_owned(); + }, + _ => (), + } + + 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(i.auth_endpoint.clone()) + .form(¶ms) + .send(); + + match res { + Ok(res) => { + #[cfg(debug_assertions)] { + println!("hit url: {}", res.url().as_str()); + println!("Status: {}", res.status()); + } + + 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 + } + + i.access_token = Some(tok.access_token().clone()); + if let Some(t) = tok.refresh_token() { + i.refresh_token = Some(t.clone()); + should_start = true; + } + #[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 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()); + println!("Status: {}", res.status().unwrap()); + println!("Post error: {}", res.to_string()); + i.exp_time = 0; + } + } + + + } else { + println!("invalid split length?!?"); + } + } + } + "".to_string() + }); + if should_start { + self.start(); + } + return match res { + Some(res) => res, + _ => "".to_string(), + }; + } +} + diff --git a/zeroidc/zeroidc.vcxproj b/zeroidc/zeroidc.vcxproj new file mode 100644 index 000000000..7410d1570 --- /dev/null +++ b/zeroidc/zeroidc.vcxproj @@ -0,0 +1,109 @@ + + + + + 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..432c20f81 --- /dev/null +++ b/zeroidc/zeroidc.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {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 + + + + + Header Files + + + \ No newline at end of file