plumbing full flow from controller -> client network

This commit is contained in:
Grant Limberg 2021-11-04 15:40:08 -07:00
parent f8bf91426b
commit 8d39c9a861
No known key found for this signature in database
GPG Key ID: 2BA62CCABBB4095A
14 changed files with 400 additions and 70 deletions

View File

@ -40,6 +40,30 @@
namespace ZeroTier
{
struct AuthInfo
{
public:
AuthInfo()
: enabled(false)
, version(0)
, authenticationURL()
, authenticationExpiryTime(0)
, centralAuthURL()
, ssoNonce()
, ssoState()
, ssoClientID()
{}
bool enabled;
uint64_t version;
std::string authenticationURL;
uint64_t authenticationExpiryTime;
std::string centralAuthURL;
std::string ssoNonce;
std::string ssoState;
std::string ssoClientID;
};
/**
* Base class with common infrastructure for all controller DB implementations
*/
@ -108,7 +132,7 @@ public:
virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0;
virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) = 0;
virtual std::string getSSOAuthURL(const nlohmann::json &member, const std::string &redirectURL) { return ""; }
virtual AuthInfo getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL) { return AuthInfo(); }
virtual void networkMemberSSOHasExpired(uint64_t nwid, int64_t ts);
inline void addListener(DB::ChangeListener *const listener)

View File

@ -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<std::mutex> 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)

View File

@ -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> &db)

View File

@ -1360,27 +1360,53 @@ void EmbeddedNetworkController::_request(
// Otherwise no, we use standard auth logic.
bool networkSSOEnabled = OSUtils::jsonBool(network["ssoEnabled"], false);
bool memberSSOExempt = OSUtils::jsonBool(member["ssoExempt"], false);
std::string authenticationURL;
if (networkSSOEnabled && !memberSSOExempt) {
authenticationURL = _db.getSSOAuthURL(member, _ssoRedirectURL);
AuthInfo info;
if (networkSSOEnabled && ! memberSSOExempt) {
info = _db.getSSOAuthInfo(member, _ssoRedirectURL);
assert(info.enabled == networkSSOEnabled);
std::string memberId = member["id"];
//fprintf(stderr, "ssoEnabled && !ssoExempt %s-%s\n", nwids, memberId.c_str());
uint64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0);
//fprintf(stderr, "authExpiryTime: %lld\n", authenticationExpiryTime);
if (authenticationExpiryTime < now) {
if (!authenticationURL.empty()) {
_db.networkMemberSSOHasExpired(nwid, now);
onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt());
if (info.version == 0) {
if (!info.authenticationURL.empty()) {
_db.networkMemberSSOHasExpired(nwid, now);
onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt());
Dictionary<3072> authInfo;
authInfo.add("aU", authenticationURL.c_str());
//fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str());
Dictionary<4096> authInfo;
authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, 0ULL);
authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str());
//fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str());
DB::cleanMember(member);
_db.save(member,true);
DB::cleanMember(member);
_db.save(member,true);
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
return;
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
return;
}
} else if (info.version == 1) {
if (!info.authenticationURL.empty()) {
_db.networkMemberSSOHasExpired(nwid, now);
onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt());
Dictionary<8192> authInfo;
authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, info.version);
authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, info.centralAuthURL.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str());
DB::cleanMember(member);
_db.save(member, true);
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
return;
}
} else {
fprintf(stderr, "invalid sso info.version %llu\n", info.version);
}
} else if (authorized) {
_db.memberWillExpire(authenticationExpiryTime, nwid, identity.address().toInt());
@ -1452,9 +1478,32 @@ void EmbeddedNetworkController::_request(
nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
nc->ssoEnabled = OSUtils::jsonBool(network["ssoEnabled"], false);
nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL);
if (!authenticationURL.empty())
Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), authenticationURL.c_str());
nc->ssoVersion = info.version;
if (info.version == 0) {
nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL);
if (!info.authenticationURL.empty()) {
Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), info.authenticationURL.c_str());
}
}
else if (info.version == 1) {
nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL);
if (!info.authenticationURL.empty()) {
Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), info.authenticationURL.c_str());
}
if (!info.centralAuthURL.empty()) {
Utils::scopy(nc->centralAuthURL, sizeof(nc->centralAuthURL), info.centralAuthURL.c_str());
}
if (!info.ssoNonce.empty()) {
Utils::scopy(nc->ssoNonce, sizeof(nc->ssoNonce), info.ssoNonce.c_str());
}
if (!info.ssoState.empty()) {
Utils::scopy(nc->ssoState, sizeof(nc->ssoState), info.ssoState.c_str());
}
if (!info.ssoClientID.empty()) {
Utils::scopy(nc->ssoClientID, sizeof(nc->ssoClientID), info.ssoClientID.c_str());
}
}
std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],""));
if (rtt.length() == 10) {

View File

@ -336,7 +336,7 @@ void PostgreSQL::nodeIsOnline(const uint64_t networkId, const uint64_t memberId,
}
}
std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::string &redirectURL)
AuthInfo PostgreSQL::getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL)
{
// NONCE is just a random character string. no semantic meaning
// state = HMAC SHA384 of Nonce based on shared sso key
@ -347,10 +347,12 @@ std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::s
// how do we tell when a nonce is used? if auth_expiration_time is set
std::string networkId = member["nwid"];
std::string memberId = member["id"];
char authenticationURL[4096] = {0};
//fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str());
bool have_auth = false;
char authenticationURL[4096] = {0};
AuthInfo info;
info.enabled = true;
// fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str());
try {
auto c = _pool->borrow();
pqxx::work w(*c->c);
@ -390,38 +392,51 @@ std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::s
exit(6);
}
r = w.exec_params("SELECT org.client_id, org.authorization_endpoint "
r = w.exec_params("SELECT org.client_id, org.authorization_endpoint, org.sso_version "
"FROM ztc_network AS nw, ztc_org AS org "
"WHERE nw.id = $1 AND nw.sso_enabled = true AND org.owner_id = nw.owner_id", networkId);
std::string client_id = "";
std::string authorization_endpoint = "";
uint64_t sso_version = 0;
if (r.size() == 1) {
client_id = r.at(0)[0].as<std::string>();
authorization_endpoint = r.at(0)[1].as<std::string>();
sso_version = r.at(0)[2].as<uint64_t>();
} else if (r.size() > 1) {
fprintf(stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n", networkId.c_str());
} else {
fprintf(stderr, "No client or auth endpoint?!?\n");
}
info.version = sso_version;
// no catch all else because we don't actually care if no records exist here. just continue as normal.
if ((!client_id.empty())&&(!authorization_endpoint.empty())) {
have_auth = true;
uint8_t state[48];
HMACSHA384(_ssoPsk, nonceBytes, sizeof(nonceBytes), state);
char state_hex[256];
Utils::hex(state, 48, state_hex);
OSUtils::ztsnprintf(authenticationURL, sizeof(authenticationURL),
"%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s",
authorization_endpoint.c_str(),
redirectURL.c_str(),
nonce.c_str(),
state_hex,
client_id.c_str());
if (info.version == 0) {
char url[2048] = {0};
OSUtils::ztsnprintf(url, sizeof(authenticationURL),
"%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s",
authorization_endpoint.c_str(),
redirectURL.c_str(),
nonce.c_str(),
state_hex,
client_id.c_str());
info.authenticationURL = std::string(url);
} else if (info.version == 1) {
info.ssoClientID = client_id;
info.authenticationURL = authorization_endpoint;
info.ssoNonce = nonce;
info.ssoState = std::string(state_hex);
info.centralAuthURL = redirectURL;
}
} else {
fprintf(stderr, "client_id: %s\nauthorization_endpoint: %s\n", client_id.c_str(), authorization_endpoint.c_str());
}
@ -432,7 +447,7 @@ std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member, const std::s
fprintf(stderr, "ERROR: Error updating member on load: %s\n", e.what());
}
return std::string(authenticationURL);
return info; //std::string(authenticationURL);
}
void PostgreSQL::initializeNetworks()

View File

@ -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

View File

@ -1194,11 +1194,18 @@ typedef struct
*/
ZT_VirtualNetworkDNS dns;
/**
* sso enabled
*/
bool ssoEnabled;
/**
* SSO verison
*/
uint64_t ssoVersion;
/**
* If the status us AUTHENTICATION_REQUIRED, this may contain a URL for authentication.
*/
@ -1208,6 +1215,26 @@ typedef struct
* Time that current authentication expires. only valid if ssoEnabled is true
*/
uint64_t authenticationExpiryTime;
/**
* central base URL.
*/
char centralAuthURL[2048];
/**
* sso nonce
*/
char ssoNonce[64];
/**
* sso state
*/
char ssoState[128];
/**
* oidc client id
*/
char ssoClientID[256];
} ZT_VirtualNetworkConfig;
/**

View File

@ -1,6 +1,8 @@
CC=clang
CXX=clang++
INCLUDES=
TOPDIR=$(shell PWD)
INCLUDES=-I$(shell PWD)/zeroidc/target
DEFS=
LIBS=
ARCH_FLAGS=-arch x86_64 -arch arm64
@ -26,7 +28,7 @@ DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUIL
include objects.mk
ONE_OBJS+=osdep/MacEthernetTap.o osdep/MacKextEthernetTap.o osdep/MacDNSHelper.o ext/http-parser/http_parser.o
LIBS+=-framework CoreServices -framework SystemConfiguration -framework CoreFoundation
LIBS+=-framework CoreServices -framework SystemConfiguration -framework CoreFoundation -framework Security
# Official releases are signed with our Apple cert and apply software updates by default
ifeq ($(ZT_OFFICIAL_RELEASE),1)
@ -103,7 +105,7 @@ mac-agent: FORCE
osdep/MacDNSHelper.o: osdep/MacDNSHelper.mm
$(CXX) $(CXXFLAGS) -c osdep/MacDNSHelper.mm -o osdep/MacDNSHelper.o
one: $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent zeroidc
one: zeroidc $(CORE_OBJS) $(ONE_OBJS) one.o mac-agent
$(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS) zeroidc/target/libzeroidc.a
# $(STRIP) zerotier-one
ln -sf zerotier-one zerotier-idtool
@ -115,8 +117,8 @@ zerotier-one: one
zeroidc: zeroidc/target/libzeroidc.a
zeroidc/target/libzeroidc.a:
cd zeroidc && cargo build --target=x86_64-apple-darwin --release
cd zeroidc && cargo build --target=aarch64-apple-darwin --release
cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=x86_64-apple-darwin --release
cd zeroidc && MACOSX_DEPLOYMENT_TARGET=$(MACOS_VERSION_MIN) cargo build --target=aarch64-apple-darwin --release
cd zeroidc && lipo -create target/x86_64-apple-darwin/release/libzeroidc.a target/aarch64-apple-darwin/release/libzeroidc.a -output target/libzeroidc.a
central-controller:
@ -126,6 +128,8 @@ zerotier-idtool: one
zerotier-cli: one
$(CORE_OBJS): zeroidc
libzerotiercore.a: $(CORE_OBJS)
ar rcs libzerotiercore.a $(CORE_OBJS)
ranlib libzerotiercore.a

View File

@ -199,17 +199,62 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar
const uint16_t errorDataSize = at<uint16_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8);
s -= 2;
if (s >= (int)errorDataSize) {
Dictionary<3072> authInfo(((const char *)this->data()) + (ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 10), errorDataSize);
char authenticationURL[2048];
if (authInfo.get("aU", authenticationURL, sizeof(authenticationURL)) > 0) {
authenticationURL[sizeof(authenticationURL) - 1] = 0; // ensure always zero terminated
network->setAuthenticationRequired(authenticationURL);
noUrl = false;
Dictionary<8192> authInfo(((const char *)this->data()) + (ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 10), errorDataSize);
uint64_t authVer = authInfo.getUI(ZT_AUTHINFO_DICT_KEY_VERSION, 0ULL);
if (authVer == 0) {
char authenticationURL[2048];
if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) {
authenticationURL[sizeof(authenticationURL) - 1] = 0; // ensure always zero terminated
network->setAuthenticationRequired(authenticationURL);
noUrl = false;
}
} else if (authVer == 1) {
bool haveAuthURL = false;
char authenticationURL[2048] = { 0 };
bool haveCentralURL = false;
char centralAuthURL[2048] = { 0 };
bool haveNonce = false;
char ssoNonce[64] = { 0 };
bool haveState = false;
char ssoState[128] = {0};
bool haveClientID = false;
char ssoClientID[256] = { 0 };
if (authInfo.get(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, authenticationURL, sizeof(authenticationURL)) > 0) {
authenticationURL[sizeof(authenticationURL) - 1] = 0;
haveAuthURL = true;
}
if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, centralAuthURL, sizeof(centralAuthURL))>0) {
centralAuthURL[sizeof(centralAuthURL) - 1] = 0;
haveCentralURL = true;
}
if (authInfo.get(ZT_AUTHINFO_DICT_KEY_NONCE, ssoNonce, sizeof(ssoNonce)) > 0) {
ssoNonce[sizeof(ssoNonce) - 1] = 0;
haveNonce = true;
}
if (authInfo.get(ZT_AUTHINFO_DICT_KEY_STATE, ssoState, sizeof(ssoState)) > 0) {
ssoState[sizeof(ssoState) - 1] = 0;
haveState = true;
}
if (authInfo.get(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, ssoClientID, sizeof(ssoClientID)) > 0) {
ssoClientID[sizeof(ssoClientID) - 1] = 0;
haveClientID = true;
}
noUrl = ! (haveAuthURL && haveCentralURL && haveNonce && haveState && haveClientID);
if (!noUrl) {
network->setAuthenticationRequired(authenticationURL, centralAuthURL, ssoClientID, ssoNonce, ssoState);
}
}
}
}
if (noUrl)
if (noUrl) {
network->setAuthenticationRequired("");
}
}
} break;

View File

@ -32,6 +32,7 @@
#include "Node.hpp"
#include "Peer.hpp"
#include "Trace.hpp"
#include "zeroidc.h"
#include <set>
@ -550,7 +551,8 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u
_lastConfigUpdate(0),
_destroyed(false),
_netconfFailure(NETCONF_FAILURE_NONE),
_portError(0)
_portError(0),
_idc(nullptr)
{
for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i)
_incomingConfigChunks[i].ts = 0;
@ -591,6 +593,12 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u
_portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
_portInitialized = true;
}
if (nconf->ssoEnabled) {
if (_config.ssoVersion == 1) {
// start sso handling for network
}
}
}
Network::~Network()
@ -598,6 +606,12 @@ Network::~Network()
ZT_VirtualNetworkConfig ctmp;
_externalConfig(&ctmp);
if (_idc) {
zeroidc::zeroidc_stop(_idc);
zeroidc::zeroidc_delete(_idc);
_idc = nullptr;
}
if (_destroyed) {
// This is done in Node::leave() so we can pass tPtr properly
//RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
@ -1434,8 +1448,13 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
memcpy(&ec->dns, &_config.dns, sizeof(ZT_VirtualNetworkDNS));
Utils::scopy(ec->authenticationURL, sizeof(ec->authenticationURL), _authenticationURL.c_str());
ec->ssoVersion = _config.ssoVersion;
ec->authenticationExpiryTime = _config.authenticationExpiryTime;
ec->ssoEnabled = _config.ssoEnabled;
Utils::scopy(ec->centralAuthURL, sizeof(ec->centralAuthURL), _config.centralAuthURL);
Utils::scopy(ec->ssoNonce, sizeof(ec->ssoNonce), _config.ssoNonce);
Utils::scopy(ec->ssoState, sizeof(ec->ssoState), _config.ssoState);
Utils::scopy(ec->ssoClientID, sizeof(ec->ssoClientID), _config.ssoClientID);
}
void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)
@ -1542,4 +1561,30 @@ Membership &Network::_membership(const Address &a)
return _memberships[a];
}
void Network::setAuthenticationRequired(const char* authEndpoint, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state)
{
Mutex::Lock _l(_lock);
_netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED;
_config.ssoEnabled = true;
_config.ssoVersion = 1;
Utils::scopy(_config.authenticationURL, sizeof(_config.authenticationURL), authEndpoint);
Utils::scopy(_config.centralAuthURL, sizeof(_config.centralAuthURL), centralEndpoint);
Utils::scopy(_config.ssoClientID, sizeof(_config.ssoClientID), clientID);
Utils::scopy(_config.ssoNonce, sizeof(_config.ssoNonce), nonce);
Utils::scopy(_config.ssoState, sizeof(_config.ssoState), state);
// TODO: propaagte to full flow module
if (!this->_idc) {
this->_idc = zeroidc::zeroidc_new(_config.authenticationURL, _config.ssoClientID, _config.centralAuthURL, 9993);
zeroidc::zeroidc_start(this->_idc);
}
zeroidc::AuthInfo *info = zeroidc::zeroidc_get_auth_info(this->_idc, _config.ssoState, _config.ssoNonce);
const char* url = zeroidc::zeroidc_get_auth_url(info);
if (url != NULL) {
fprintf(stderr, "full auth URL: %s\n", url);
}
}
} // namespace ZeroTier

View File

@ -41,6 +41,10 @@
#define ZT_NETWORK_MAX_INCOMING_UPDATES 3
#define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1)
namespace zeroidc {
typedef struct ZeroIDC ZeroIDC;
}
namespace ZeroTier {
class RuntimeEnvironment;
@ -229,7 +233,14 @@ public:
_netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED;
_authenticationURL = (url) ? url : "";
_config.ssoEnabled = true;
}
_config.ssoVersion = 0;
}
/**
* set netconf failure to 'authentication required' along with info needed
* for sso full flow authentication.
*/
void setAuthenticationRequired(const char* authEndpoint, const char* centralEndpoint, const char* clientID, const char* nonce, const char* state);
/**
* Causes this network to request an updated configuration from its master node now
@ -457,8 +468,10 @@ private:
Mutex _lock;
AtomicCounter __refCount;
zeroidc::ZeroIDC *_idc;
};
} // namespace ZeroTier
} // namespace ZeroTier
#endif

View File

@ -182,12 +182,24 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_DNS,*tmp)) return false;
}
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false;
if (this->ssoEnabled) {
if (this->authenticationURL[0]) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false;
if (this->ssoVersion == 0) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false;
if (this->ssoEnabled) {
if (this->authenticationURL[0]) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false;
}
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, this->authenticationExpiryTime)) return false;
}
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, this->authenticationExpiryTime)) return false;
} else if(this->ssoVersion == 1) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID)) return false;
}
delete tmp;
@ -374,18 +386,48 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
DNS::deserializeDNS(*tmp, p, &dns);
}
this->ssoVersion = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, 0ULL);
this->ssoEnabled = d.getB(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, false);
if (this->ssoEnabled) {
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) {
this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0; // ensure null terminated
if (this->ssoVersion == 0) {
// implicit flow
if (this->ssoEnabled) {
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) {
this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0; // ensure null terminated
} else {
this->authenticationURL[0] = 0;
}
this->authenticationExpiryTime = d.getI(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, 0);
} else {
this->authenticationURL[0] = 0;
this->authenticationExpiryTime = 0;
}
} else if (this->ssoVersion == 1) {
// full flow
if (this->ssoEnabled) {
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) {
this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0;
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL, (unsigned int)sizeof(this->centralAuthURL)) > 0) {
this->centralAuthURL[sizeof(this->centralAuthURL) - 1] = 0;
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce, (unsigned int)sizeof(this->ssoNonce)) > 0) {
this->ssoNonce[sizeof(this->ssoNonce) - 1] = 0;
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState, (unsigned int)sizeof(this->ssoState)) > 0) {
this->ssoState[sizeof(this->ssoState) - 1] = 0;
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID, (unsigned int)sizeof(this->ssoClientID)) > 0) {
this->ssoClientID[sizeof(this->ssoClientID) - 1] = 0;
}
} else {
this->authenticationURL[0] = 0;
this->authenticationExpiryTime = 0;
this->centralAuthURL[0] = 0;
this->ssoNonce[0] = 0;
this->ssoState[0] = 0;
this->ssoClientID[0] = 0;
}
this->authenticationExpiryTime = d.getI(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, 0);
} else {
this->authenticationURL[0] = 0;
this->authenticationExpiryTime = 0;
}
}

View File

@ -180,10 +180,35 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_DNS "DNS"
// sso enabld
#define ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED "ssoe"
// so version
#define ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION "ssov"
// authentication URL
#define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL "aurl"
// authentication expiry
#define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME "aexpt"
// central endpoint
#define ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL "ssoce"
// nonce
#define ZT_NETWORKCONFIG_DICT_KEY_NONCE "sson"
// state
#define ZT_NETWORKCONFIG_DICT_KEY_STATE "ssos"
// client ID
#define ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID "ssocid"
// AuthInfo fields -- used by ncSendError for sso
// AuthInfo Version
#define ZT_AUTHINFO_DICT_KEY_VERSION "aV"
// authenticaiton URL
#define ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL "aU"
// Central endpoint URL
#define ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL "aCU"
// Nonce
#define ZT_AUTHINFO_DICT_KEY_NONCE "aN"
// State
#define ZT_AUTHINFO_DICT_KEY_STATE "aS"
// Client ID
#define ZT_AUTHINFO_DICT_KEY_CLIENT_ID "aCID"
// Legacy fields -- these are obsoleted but are included when older clients query
@ -242,7 +267,11 @@ public:
dnsCount(0),
ssoEnabled(false),
authenticationURL(),
authenticationExpiryTime(0)
authenticationExpiryTime(0),
centralAuthURL(),
ssoNonce(),
ssoState(),
ssoClientID()
{
name[0] = 0;
memset(specialists, 0, sizeof(uint64_t)*ZT_MAX_NETWORK_SPECIALISTS);
@ -250,6 +279,11 @@ public:
memset(staticIps, 0, sizeof(InetAddress)*ZT_MAX_ZT_ASSIGNED_ADDRESSES);
memset(rules, 0, sizeof(ZT_VirtualNetworkRule)*ZT_MAX_NETWORK_RULES);
memset(&dns, 0, sizeof(ZT_VirtualNetworkDNS));
memset(authenticationURL, 0, sizeof(authenticationURL));
memset(centralAuthURL, 0, sizeof(centralAuthURL));
memset(ssoNonce, 0, sizeof(ssoNonce));
memset(ssoState, 0, sizeof(ssoState));
memset(ssoClientID, 0, sizeof(ssoClientID));
}
/**
@ -619,15 +653,42 @@ public:
*/
bool ssoEnabled;
/**
* SSO verison
*/
uint64_t ssoVersion;
/**
* Authentication URL if authentication is required
*/
char authenticationURL[2048];
/**
/**
* Time current authentication expires or 0 if external authentication is disabled
*
* Not used if authVersion >= 1
*/
uint64_t authenticationExpiryTime;
/**
* central base URL.
*/
char centralAuthURL[2048];
/**
* sso nonce
*/
char ssoNonce[64];
/**
* sso state
*/
char ssoState[128];
/**
* oidc client id
*/
char ssoClientID[256];
};
} // namespace ZeroTier

View File

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