mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-20 09:46:13 +00:00
Basic plumbing for authentication requirement and piping through of URL information.
This commit is contained in:
parent
9c58308e6a
commit
b270d527f4
@ -67,6 +67,8 @@ void DB::initMember(nlohmann::json &member)
|
||||
if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
|
||||
if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json();
|
||||
if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json();
|
||||
if (!member.count("authenticationExpiryTime")) member["authenticationExpiryTime"] = -1LL;
|
||||
if (!member.count("authenticationURL")) member["authenticationURL"] = nlohmann::json();
|
||||
if (!member.count("vMajor")) member["vMajor"] = -1;
|
||||
if (!member.count("vMinor")) member["vMinor"] = -1;
|
||||
if (!member.count("vRev")) member["vRev"] = -1;
|
||||
|
@ -466,6 +466,14 @@ EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *ztPa
|
||||
_db(this),
|
||||
_rc(rc)
|
||||
{
|
||||
memset(_ssoPsk, 0, sizeof(_ssoPsk));
|
||||
char *const ssoPskHex = getenv("ZT_SSO_PSK");
|
||||
if (ssoPskHex) {
|
||||
// SECURITY: note that ssoPskHex will always be null-terminated if libc acatually
|
||||
// returns something non-NULL. If the hex encodes something shorter than 48 bytes,
|
||||
// it will be padded at the end with zeroes. If longer, it'll be truncated.
|
||||
Utils::unhex(ssoPskHex, _ssoPsk, sizeof(_ssoPsk));
|
||||
}
|
||||
}
|
||||
|
||||
EmbeddedNetworkController::~EmbeddedNetworkController()
|
||||
@ -1248,7 +1256,7 @@ void EmbeddedNetworkController::_request(
|
||||
Utils::hex(nwid,nwids);
|
||||
_db.get(nwid,network,identity.address().toInt(),member,ns);
|
||||
if ((!network.is_object())||(network.empty())) {
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND);
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
const bool newMember = ((!member.is_object())||(member.empty()));
|
||||
@ -1262,11 +1270,11 @@ void EmbeddedNetworkController::_request(
|
||||
// known member.
|
||||
try {
|
||||
if (Identity(haveIdStr.c_str()) != identity) {
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
} catch ( ... ) {
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1348,16 +1356,30 @@ void EmbeddedNetworkController::_request(
|
||||
ms.identity = identity;
|
||||
}
|
||||
}
|
||||
|
||||
const int64_t authenticationExpiryTime = member["authenticationExpiryTime"];
|
||||
if ((authenticationExpiryTime >= 0)&&(authenticationExpiryTime < now)) {
|
||||
const std::string authenticationURL = member["authenticationURL"];
|
||||
if (authenticationURL.empty()) {
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, nullptr, 0);
|
||||
return;
|
||||
} else {
|
||||
Dictionary<1024> authInfo;
|
||||
authInfo.add("aU", authenticationURL.c_str());
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If they are not authorized, STOP!
|
||||
DB::cleanMember(member);
|
||||
_db.save(member,true);
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// If we made it this far, they are authorized.
|
||||
// If we made it this far, they are authorized (and authenticated).
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
|
||||
@ -1734,7 +1756,7 @@ void EmbeddedNetworkController::_request(
|
||||
if (com.sign(_signingId)) {
|
||||
nc->com = com;
|
||||
} else {
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR);
|
||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,7 @@ private:
|
||||
Identity _signingId;
|
||||
std::string _signingIdAddressString;
|
||||
NetworkController::Sender *_sender;
|
||||
uint8_t _ssoPsk[48];
|
||||
|
||||
DBMirrorSet _db;
|
||||
BlockingQueue< _RQEntry * > _queue;
|
||||
|
@ -820,7 +820,12 @@ enum ZT_VirtualNetworkStatus
|
||||
/**
|
||||
* ZeroTier core version too old
|
||||
*/
|
||||
ZT_NETWORK_STATUS_CLIENT_TOO_OLD = 5
|
||||
ZT_NETWORK_STATUS_CLIENT_TOO_OLD = 5,
|
||||
|
||||
/**
|
||||
* External authentication is required (e.g. SSO)
|
||||
*/
|
||||
ZT_NETWORK_STATUS_AUTHENTICATION_REQUIRED = 6
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1339,6 +1344,16 @@ typedef struct
|
||||
* Network specific DNS configuration
|
||||
*/
|
||||
ZT_VirtualNetworkDNS dns;
|
||||
|
||||
/**
|
||||
* If the status us AUTHENTICATION_REQUIRED, this may contain a URL for authentication.
|
||||
*/
|
||||
char authenticationURL[256];
|
||||
|
||||
/**
|
||||
* Time that current authentication expires or -1 if external authentication is not required.
|
||||
*/
|
||||
int64_t authenticationExpiryTime;
|
||||
} ZT_VirtualNetworkConfig;
|
||||
|
||||
/**
|
||||
|
@ -1429,6 +1429,9 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||
}
|
||||
|
||||
memcpy(&ec->dns, &_config.dns, sizeof(ZT_VirtualNetworkDNS));
|
||||
|
||||
Utils::scopy(ec->authenticationURL, sizeof(ec->authenticationURL), _config.authenticationURL);
|
||||
ec->authenticationExpiryTime = _config.authenticationExpiryTime;
|
||||
}
|
||||
|
||||
void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)
|
||||
|
@ -220,6 +220,16 @@ public:
|
||||
_netconfFailure = NETCONF_FAILURE_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'authentication required' possibly with an authorization URL
|
||||
*/
|
||||
inline void setAuthenticationRequired(const char *url)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED;
|
||||
_authorizationURL = (url) ? url : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this network to request an updated configuration from its master node now
|
||||
*
|
||||
@ -435,9 +445,11 @@ private:
|
||||
NETCONF_FAILURE_NONE,
|
||||
NETCONF_FAILURE_ACCESS_DENIED,
|
||||
NETCONF_FAILURE_NOT_FOUND,
|
||||
NETCONF_FAILURE_INIT_FAILED
|
||||
NETCONF_FAILURE_INIT_FAILED,
|
||||
NETCONF_FAILURE_AUTHENTICATION_REQUIRED
|
||||
} _netconfFailure;
|
||||
int _portError; // return value from port config callback
|
||||
std::string _authorizationURL;
|
||||
|
||||
Hashtable<Address,Membership> _memberships;
|
||||
|
||||
|
@ -182,6 +182,13 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_DNS,*tmp)) return false;
|
||||
}
|
||||
|
||||
if (this->authenticationURL[0]) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false;
|
||||
}
|
||||
if (this->authenticationExpiryTime >= 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, this->authenticationExpiryTime)) return false;
|
||||
}
|
||||
|
||||
delete tmp;
|
||||
} catch ( ... ) {
|
||||
delete tmp;
|
||||
@ -365,6 +372,13 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
|
||||
unsigned int p = 0;
|
||||
DNS::deserializeDNS(*tmp, p, &dns);
|
||||
}
|
||||
|
||||
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, -1);
|
||||
}
|
||||
|
||||
//printf("~~~\n%s\n~~~\n",d.data());
|
||||
|
@ -178,6 +178,10 @@ namespace ZeroTier {
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
|
||||
// dns (binary blobs)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_DNS "DNS"
|
||||
// authentication URL
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL "aurl"
|
||||
// authentication expiry
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME "aexpt"
|
||||
|
||||
// Legacy fields -- these are obsoleted but are included when older clients query
|
||||
|
||||
@ -604,6 +608,16 @@ public:
|
||||
* ZT pushed DNS configuration
|
||||
*/
|
||||
ZT_VirtualNetworkDNS dns;
|
||||
|
||||
/**
|
||||
* Authentication URL if authentication is required
|
||||
*/
|
||||
char authenticationURL[256];
|
||||
|
||||
/**
|
||||
* Time current authentication expires or -1 if external authentication is disabled
|
||||
*/
|
||||
int64_t authenticationExpiryTime;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
@ -38,7 +38,8 @@ public:
|
||||
NC_ERROR_NONE = 0,
|
||||
NC_ERROR_OBJECT_NOT_FOUND = 1,
|
||||
NC_ERROR_ACCESS_DENIED = 2,
|
||||
NC_ERROR_INTERNAL_SERVER_ERROR = 3
|
||||
NC_ERROR_INTERNAL_SERVER_ERROR = 3,
|
||||
NC_ERROR_AUTHENTICATION_REQUIRED = 4
|
||||
};
|
||||
|
||||
/**
|
||||
@ -69,12 +70,17 @@ public:
|
||||
/**
|
||||
* Send a network configuration request error
|
||||
*
|
||||
* If errorData/errorDataSize are provided they must point to a valid serialized
|
||||
* Dictionary containing error data. They can be null/zero if not specified.
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param requestPacketId Request packet ID or 0 if none
|
||||
* @param destination Destination peer Address
|
||||
* @param errorCode Error code
|
||||
* @param errorData Data associated with error or NULL if none
|
||||
* @param errorDataSize Size of errorData in bytes
|
||||
*/
|
||||
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) = 0;
|
||||
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode, const void *errorData, unsigned int errorDataSize) = 0;
|
||||
};
|
||||
|
||||
NetworkController() {}
|
||||
|
@ -731,7 +731,7 @@ void Node::ncSendRevocation(const Address &destination,const Revocation &rev)
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode)
|
||||
void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode, const void *errorData, unsigned int errorDataSize)
|
||||
{
|
||||
if (destination == RR->identity.address()) {
|
||||
SharedPtr<Network> n(network(nwid));
|
||||
@ -744,6 +744,9 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des
|
||||
case NetworkController::NC_ERROR_ACCESS_DENIED:
|
||||
n->setAccessDenied();
|
||||
break;
|
||||
case NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED: {
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
@ -760,8 +763,16 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des
|
||||
case NetworkController::NC_ERROR_ACCESS_DENIED:
|
||||
outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
|
||||
break;
|
||||
case NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED:
|
||||
outp.append((unsigned char)Packet::ERROR_NETWORK_AUTHENTICATION_REQUIRED);
|
||||
break;
|
||||
}
|
||||
|
||||
outp.append(nwid);
|
||||
|
||||
if ((errorData)&&(errorDataSize > 0))
|
||||
outp.append(errorData, errorDataSize);
|
||||
|
||||
RR->sw->send((void *)0,outp,true);
|
||||
} // else we can't send an ERROR() in response to nothing, so discard
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ public:
|
||||
|
||||
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig);
|
||||
virtual void ncSendRevocation(const Address &destination,const Revocation &rev);
|
||||
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
|
||||
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode, const void *errorData, unsigned int errorDataSize);
|
||||
|
||||
inline const Address &remoteTraceTarget() const { return _remoteTraceTarget; }
|
||||
inline Trace::Level remoteTraceLevel() const { return _remoteTraceLevel; }
|
||||
|
@ -792,6 +792,12 @@ public:
|
||||
*
|
||||
* ERROR response payload:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[2] 16-bit length of error-related data (optional)>
|
||||
* <[...] error-related data (optional)>
|
||||
*
|
||||
* Error related data is a Dictionary containing things like a URL
|
||||
* for authentication or a human-readable error message, and is
|
||||
* optional and may be absent or empty.
|
||||
*/
|
||||
VERB_NETWORK_CONFIG_REQUEST = 0x0b,
|
||||
|
||||
@ -1076,7 +1082,10 @@ public:
|
||||
ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */
|
||||
|
||||
/* Multicasts to this group are not wanted */
|
||||
ERROR_UNWANTED_MULTICAST = 0x08
|
||||
ERROR_UNWANTED_MULTICAST = 0x08,
|
||||
|
||||
/* Network requires external or 2FA authentication (e.g. SSO). */
|
||||
ERROR_NETWORK_AUTHENTICATION_REQUIRED = 0x09
|
||||
};
|
||||
|
||||
template<unsigned int C2>
|
||||
|
@ -251,6 +251,8 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,
|
||||
}
|
||||
nj["dns"] = m;
|
||||
|
||||
nj["authenticationURL"] = nc->authenticationURL;
|
||||
nj["authenticationExpiryTime"] = nc->authenticationExpiryTime;
|
||||
}
|
||||
|
||||
static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
|
||||
|
Loading…
x
Reference in New Issue
Block a user