mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-20 09:46:13 +00:00
Merge branch 'zeroidc' into dev
This commit is contained in:
commit
f8d7796099
2
.gitignore
vendored
2
.gitignore
vendored
@ -122,6 +122,8 @@ attic/world/*.c25519
|
||||
attic/world/mkworld
|
||||
workspace/
|
||||
workspace2/
|
||||
zeroidc/target/
|
||||
tmp/
|
||||
|
||||
#snapcraft specifics
|
||||
/parts/
|
||||
|
@ -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<Connection> conn = m_factory->create();
|
||||
m_borrowed.insert(conn);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <libpq-fe.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <climits>
|
||||
#include <chrono>
|
||||
|
||||
@ -80,6 +81,28 @@ std::vector<std::string> 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<std::string>();
|
||||
authorization_endpoint = r.at(0)[1].as<std::string>();
|
||||
issuer = r.at(0)[2].as<std::string>();
|
||||
sso_version = r.at(0)[3].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(),
|
||||
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<PostgresConnection> 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<int>();
|
||||
|
||||
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<std::string> 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<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_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();
|
||||
|
@ -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
|
||||
|
@ -2,7 +2,7 @@
|
||||
FROM registry.zerotier.com/zerotier/controller-builder:latest as builder
|
||||
MAINTAINER Adam Ierymekno <adam.ierymenko@zerotier.com>, Grant Limberg <grant.limberg@zerotier.com>
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
27
make-mac.mk
27
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
|
||||
|
||||
|
@ -142,7 +142,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar
|
||||
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
|
||||
const SharedPtr<Network> network(RR->node->network(at<uint64_t>(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> network(RR->node->network(at<uint64_t>(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> network(RR->node->network(at<uint64_t>(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> network(RR->node->network(at<uint64_t>(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<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(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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<MulticastGroup> &allMulticastGroups);
|
||||
std::vector<MulticastGroup> _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
|
||||
|
@ -182,12 +182,25 @@ 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_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 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_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -53,6 +53,8 @@
|
||||
#include "OneService.hpp"
|
||||
#include "SoftwareUpdater.hpp"
|
||||
|
||||
#include <zeroidc.h>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
@ -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<EthernetTap> tap) {
|
||||
this->_tap = tap;
|
||||
}
|
||||
|
||||
std::shared_ptr<EthernetTap> 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<InetAddress> 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<InetAddress>& managedIps() {
|
||||
return _managedIps;
|
||||
}
|
||||
|
||||
void setManagedIps(const std::vector<InetAddress> &managedIps) {
|
||||
_managedIps = managedIps;
|
||||
}
|
||||
|
||||
std::map< InetAddress, SharedPtr<ManagedRoute> >& 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<EthernetTap> _tap;
|
||||
ZT_VirtualNetworkConfig _config; // memcpy() of raw config from core
|
||||
std::vector<InetAddress> _managedIps;
|
||||
std::map< InetAddress, SharedPtr<ManagedRoute> > _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;i<nc->assignedAddressCount;++i) {
|
||||
aa.push_back(reinterpret_cast<const InetAddress *>(&(nc->assignedAddresses[i]))->toString(tmp));
|
||||
for(unsigned int i=0;i<ns.config().assignedAddressCount;++i) {
|
||||
aa.push_back(reinterpret_cast<const InetAddress *>(&(ns.config().assignedAddresses[i]))->toString(tmp));
|
||||
}
|
||||
nj["assignedAddresses"] = aa;
|
||||
|
||||
nlohmann::json ra = nlohmann::json::array();
|
||||
for(unsigned int i=0;i<nc->routeCount;++i) {
|
||||
for(unsigned int i=0;i<ns.config().routeCount;++i) {
|
||||
nlohmann::json rj;
|
||||
rj["target"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].target))->toString(tmp);
|
||||
if (nc->routes[i].via.ss_family == nc->routes[i].target.ss_family)
|
||||
rj["via"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].via))->toIpString(tmp);
|
||||
rj["target"] = reinterpret_cast<const InetAddress *>(&(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<const InetAddress *>(&(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;i<nc->multicastSubscriptionCount;++i) {
|
||||
for(unsigned int i=0;i<ns.config().multicastSubscriptionCount;++i) {
|
||||
nlohmann::json m;
|
||||
m["mac"] = MAC(nc->multicastSubscriptions[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;j<ZT_MAX_DNS_SERVERS;++j) {
|
||||
|
||||
InetAddress a(nc->dns.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<EthernetTap> tap;
|
||||
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
|
||||
std::vector<InetAddress> managedIps;
|
||||
std::map< InetAddress, SharedPtr<ManagedRoute> > managedRoutes;
|
||||
NetworkSettings settings;
|
||||
};
|
||||
std::map<uint64_t,NetworkState> _nets;
|
||||
Mutex _nets_m;
|
||||
|
||||
@ -888,7 +1071,7 @@ public:
|
||||
{
|
||||
Mutex::Lock _l(_nets_m);
|
||||
for(std::map<uint64_t,NetworkState>::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<uint64_t,NetworkState>::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<MulticastGroup>,std::vector<MulticastGroup> > >(n->first,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> >()));
|
||||
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<uint64_t,NetworkState>::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<uint64_t,NetworkState>::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<uint64_t,NetworkState>::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;i<nws->networkCount;++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;i<nws->networkCount;++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 = "<html>\
|
||||
<head>\
|
||||
<style type=\"text/css\">\
|
||||
html,body {\
|
||||
background: #eeeeee;\
|
||||
margin: 0;\
|
||||
padding: 0;\
|
||||
font-family: \"Helvetica\";\
|
||||
font-weight: bold;\
|
||||
font-size: 12pt;\
|
||||
height: 100%;\
|
||||
width: 100%;\
|
||||
}\
|
||||
div.icon {\
|
||||
background: #ffb354;\
|
||||
color: #000000;\
|
||||
font-size: 120pt;\
|
||||
border-radius: 2.5rem;\
|
||||
display: inline-block;\
|
||||
width: 1.3em;\
|
||||
height: 1.3em;\
|
||||
padding: 0;\
|
||||
margin: 15;\
|
||||
line-height: 1.4em;\
|
||||
vertical-align: middle;\
|
||||
text-align: center;\
|
||||
}\
|
||||
</style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<br><br><br><br><br><br>\
|
||||
<center>\
|
||||
<div class=\"icon\">⏁</div>\
|
||||
<div class=\"text\">Authentication Successful. You may now access the network.</div>\
|
||||
</center>\
|
||||
</body>\
|
||||
</html>";
|
||||
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;i<nws->networkCount;++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<std::string>(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<std::string>(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<InetAddress> newManagedIps;
|
||||
newManagedIps.reserve(n.config.assignedAddressCount);
|
||||
for(unsigned int i=0;i<n.config.assignedAddressCount;++i) {
|
||||
const InetAddress *ii = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[i]));
|
||||
newManagedIps.reserve(n.config().assignedAddressCount);
|
||||
for(unsigned int i=0;i<n.config().assignedAddressCount;++i) {
|
||||
const InetAddress *ii = reinterpret_cast<const InetAddress *>(&(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<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
|
||||
for(std::vector<InetAddress>::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<InetAddress>::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<InetAddress> tapIps(n.tap->ips());
|
||||
std::vector<InetAddress> tapIps(n.tap()->ips());
|
||||
std::set<InetAddress> myIps(tapIps.begin(), tapIps.end());
|
||||
for(unsigned int i=0;i<n.config.assignedAddressCount;++i)
|
||||
myIps.insert(InetAddress(n.config.assignedAddresses[i]));
|
||||
for(unsigned int i=0;i<n.config().assignedAddressCount;++i)
|
||||
myIps.insert(InetAddress(n.config().assignedAddresses[i]));
|
||||
|
||||
std::set<InetAddress> haveRouteTargets;
|
||||
for(unsigned int i=0;i<n.config.routeCount;++i) {
|
||||
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
|
||||
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
|
||||
for(unsigned int i=0;i<n.config().routeCount;++i) {
|
||||
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config().routes[i].target));
|
||||
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(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<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
|
||||
for(std::vector<InetAddress>::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<ManagedRoute> &mr = n.managedRoutes[*target];
|
||||
SharedPtr<ManagedRoute> &mr = n.managedRoutes()[*target];
|
||||
if (!mr)
|
||||
mr.set(new ManagedRoute(*target, *via, *src, tapdev.c_str()));
|
||||
#endif
|
||||
}
|
||||
|
||||
for(std::map< InetAddress, SharedPtr<ManagedRoute> >::iterator r(n.managedRoutes.begin());r!=n.managedRoutes.end();) {
|
||||
for(std::map< InetAddress, SharedPtr<ManagedRoute> >::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<ManagedRoute> >::iterator r(n.managedRoutes.begin());r!=n.managedRoutes.end();++r) {
|
||||
for(std::map< InetAddress, SharedPtr<ManagedRoute> >::iterator r(n.managedRoutes().begin());r!=n.managedRoutes().end();++r) {
|
||||
if (!r->second->via())
|
||||
r->second->sync();
|
||||
}
|
||||
for(std::map< InetAddress, SharedPtr<ManagedRoute> >::iterator r(n.managedRoutes.begin());r!=n.managedRoutes.end();++r) {
|
||||
for(std::map< InetAddress, SharedPtr<ManagedRoute> >::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<InetAddress> 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<NetworkState *>(*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<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
|
||||
if (n->second.tap) {
|
||||
std::vector<InetAddress> ips(n->second.tap->ips());
|
||||
if (n->second.tap()) {
|
||||
std::vector<InetAddress> ips(n->second.tap()->ips());
|
||||
for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
|
||||
if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) {
|
||||
return 0;
|
||||
@ -3117,14 +3353,14 @@ public:
|
||||
{
|
||||
Mutex::Lock _l(_nets_m);
|
||||
for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
|
||||
if (n->second.tap) {
|
||||
std::vector<InetAddress> ips(n->second.tap->ips());
|
||||
if (n->second.tap()) {
|
||||
std::vector<InetAddress> ips(n->second.tap()->ips());
|
||||
for(std::vector<InetAddress>::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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -328,8 +328,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@ -338,8 +337,9 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<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)</AdditionalDependencies>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)\..\zeroidc\target\i686-pc-windows-msvc\debug\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
|
||||
@ -347,8 +347,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@ -365,8 +364,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>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)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
@ -375,9 +373,10 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<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)</AdditionalDependencies>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)..\zeroidc\target\x86_64-pc-windows-msvc\debug\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">
|
||||
@ -385,8 +384,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>false</MultiProcessorCompilation>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
@ -407,10 +405,9 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>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)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<StringPooling>true</StringPooling>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
@ -429,8 +426,9 @@
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<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)</AdditionalDependencies>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)..\zeroidc\target\i686-pc-windows-msvc\release\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@ -440,10 +438,9 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>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)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<StringPooling>true</StringPooling>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
@ -452,7 +449,7 @@
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
|
||||
<CallingConvention>VectorCall</CallingConvention>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<LanguageStandard>stdcpp14</LanguageStandard>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
@ -464,8 +461,9 @@
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>wbemuuid.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<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)</AdditionalDependencies>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)..\zeroidc\target\x86_64-pc-windows-msvc\release\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
5
zeroidc/.cargo/config.toml
Normal file
5
zeroidc/.cargo/config.toml
Normal 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"]
|
1506
zeroidc/Cargo.lock
generated
Normal file
1506
zeroidc/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
zeroidc/Cargo.toml
Normal file
26
zeroidc/Cargo.toml
Normal file
@ -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"
|
37
zeroidc/build.rs
Normal file
37
zeroidc/build.rs
Normal file
@ -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")
|
||||
}
|
||||
}
|
23
zeroidc/src/error.rs
Normal file
23
zeroidc/src/error.rs
Normal file
@ -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<openidconnect::reqwest::Error<reqwest::Error>>),
|
||||
|
||||
#[error(transparent)]
|
||||
ParseError(#[from] url::ParseError),
|
||||
}
|
220
zeroidc/src/ext.rs
Normal file
220
zeroidc/src/ext.rs
Normal file
@ -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::<Vec<&str>>();
|
||||
if split.len() != 2 {
|
||||
return std::ptr::null();
|
||||
}
|
||||
|
||||
let s = CString::new(split[1]).unwrap();
|
||||
return s.into_raw();
|
||||
}
|
582
zeroidc/src/lib.rs
Normal file
582
zeroidc/src/lib.rs
Normal file
@ -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<Mutex<Inner>>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
running: bool,
|
||||
auth_endpoint: String,
|
||||
oidc_thread: Option<JoinHandle<()>>,
|
||||
oidc_client: Option<openidconnect::core::CoreClient>,
|
||||
access_token: Option<AccessToken>,
|
||||
refresh_token: Option<RefreshToken>,
|
||||
exp_time: u64,
|
||||
|
||||
url: Option<Url>,
|
||||
csrf_token: Option<CsrfToken>,
|
||||
nonce: Option<Nonce>,
|
||||
pkce_verifier: Option<PkceCodeVerifier>,
|
||||
}
|
||||
|
||||
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<dyn Fn() -> CsrfToken> {
|
||||
return Box::new(move || CsrfToken::new(csrf_token.to_string()));
|
||||
}
|
||||
|
||||
fn nonce_func(nonce: String) -> Box<dyn Fn() -> Nonce> {
|
||||
return Box::new(move || Nonce::new(nonce.to_string()));
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn systemtime_strftime<T>(dt: T, format: &str) -> String
|
||||
where T: Into<OffsetDateTime>
|
||||
{
|
||||
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<ZeroIDC, ZeroIDCError> {
|
||||
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::<Exp>(&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::<CoreResponseType>::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::<Vec<&str>>();
|
||||
|
||||
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::<Exp>(&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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
109
zeroidc/zeroidc.vcxproj
Normal file
109
zeroidc/zeroidc.vcxproj
Normal file
@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{175C340F-F5BA-4CB1-88AD-533B102E3799}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>cargo build --release --target=x86_64-pc-windows-msvc</NMakeBuildCommandLine>
|
||||
<NMakeOutput>
|
||||
</NMakeOutput>
|
||||
<NMakeCleanCommandLine>cargo clean</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cargo clean & cargo build --release --target=x86_64-pc-windows-msvc</NMakeReBuildCommandLine>
|
||||
<NMakePreprocessorDefinitions>NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<NMakeBuildCommandLine>cargo build --target=i686-pc-windows-msvc</NMakeBuildCommandLine>
|
||||
<NMakeOutput>
|
||||
</NMakeOutput>
|
||||
<NMakeCleanCommandLine>cargo clean</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cargo clean & cargo build --target=i686-pc-windows-msvc</NMakeReBuildCommandLine>
|
||||
<NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>cargo build --target=x86_64-pc-windows-msvc</NMakeBuildCommandLine>
|
||||
<NMakeOutput>
|
||||
</NMakeOutput>
|
||||
<NMakeCleanCommandLine>cargo clean</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cargo clean & cargo build --target=x86_64-pc-windows-msvc</NMakeReBuildCommandLine>
|
||||
<NMakePreprocessorDefinitions>_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<NMakeBuildCommandLine>cargo build --release --target=i686-pc-windows-msvc</NMakeBuildCommandLine>
|
||||
<NMakeOutput>
|
||||
</NMakeOutput>
|
||||
<NMakeCleanCommandLine>cargo clean</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cargo clean & cargo build --release --target=i686-pc-windows-msvc</NMakeReBuildCommandLine>
|
||||
<NMakePreprocessorDefinitions>WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<None Include="src\ext.rs" />
|
||||
<None Include="src\lib.rs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="target\zeroidc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
30
zeroidc/zeroidc.vcxproj.filters
Normal file
30
zeroidc/zeroidc.vcxproj.filters
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="src\ext.rs">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="src\lib.rs">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="target\zeroidc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user