summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--node/Identity.cpp76
-rw-r--r--node/Identity.hpp34
-rw-r--r--node/Network.cpp83
-rw-r--r--node/Network.hpp36
-rw-r--r--node/NodeConfig.cpp8
5 files changed, 70 insertions, 167 deletions
diff --git a/node/Identity.cpp b/node/Identity.cpp
index e9cbef3d..1f5448a9 100644
--- a/node/Identity.cpp
+++ b/node/Identity.cpp
@@ -225,81 +225,5 @@ Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen)
return Address(dig,ZT_ADDRESS_LENGTH); // first 5 bytes of dig[]
}
-std::string Identity::encrypt(const Identity &to,const void *data,unsigned int len) const
-{
- unsigned char key[64];
- unsigned char mac[32];
- unsigned char iv[8];
-
- if (!agree(to,key,sizeof(key)))
- return std::string();
- Utils::getSecureRandom(iv,8);
- for(int i=0;i<8;++i)
- key[i + 32] ^= iv[i]; // perturb HMAC key with IV so IV is effectively included in HMAC
- Salsa20 s20(key,256,iv);
-
- std::string compressed;
- compressed.reserve(len);
- Utils::compress((const char *)data,(const char *)data + len,Utils::StringAppendOutput(compressed));
- if (!compressed.length())
- return std::string();
-
- char *encrypted = new char[compressed.length() + 16];
- try {
- s20.encrypt(compressed.data(),encrypted + 16,(unsigned int)compressed.length());
- HMAC::sha256(key + 32,32,encrypted + 16,(unsigned int)compressed.length(),mac);
- for(int i=0;i<8;++i)
- encrypted[i] = iv[i];
- for(int i=0;i<8;++i)
- encrypted[i + 8] = mac[i];
-
- std::string s(encrypted,compressed.length() + 16);
- delete [] encrypted;
- return s;
- } catch ( ... ) {
- delete [] encrypted;
- return std::string();
- }
-}
-
-std::string Identity::decrypt(const Identity &from,const void *cdata,unsigned int len) const
-{
- unsigned char key[64];
- unsigned char mac[32];
-
- if (len < 16)
- return std::string();
-
- if (!agree(from,key,sizeof(key)))
- return std::string();
-
- for(int i=0;i<8;++i)
- key[i + 32] ^= ((const unsigned char *)cdata)[i]; // apply IV to HMAC key
- HMAC::sha256(key + 32,32,((const char *)cdata) + 16,(unsigned int)(len - 16),mac);
- for(int i=0;i<8;++i) {
- if (((const unsigned char *)cdata)[i + 8] != mac[i])
- return std::string();
- }
-
- char *decbuf = new char[len - 16];
- try {
- Salsa20 s20(key,256,cdata); // first 8 bytes are IV
- len -= 16;
- s20.decrypt((const char *)cdata + 16,decbuf,len);
-
- std::string decompressed;
- if (Utils::decompress((const char *)decbuf,(const char *)decbuf + len,Utils::StringAppendOutput(decompressed))) {
- delete [] decbuf;
- return decompressed;
- } else {
- delete [] decbuf;
- return std::string();
- }
- } catch ( ... ) {
- delete [] decbuf;
- return std::string();
- }
-}
-
} // namespace ZeroTier
diff --git a/node/Identity.hpp b/node/Identity.hpp
index 1cce4fb0..a970d7f7 100644
--- a/node/Identity.hpp
+++ b/node/Identity.hpp
@@ -176,40 +176,6 @@ public:
inline bool hasPrivate() const throw() { return (_keyPair); }
/**
- * Encrypt a block of data to send to another identity
- *
- * This identity must have a secret key.
- *
- * The encrypted data format is:
- * <[8] Salsa20 initialization vector>
- * <[8] first 8 bytes of HMAC-SHA-256 of ciphertext>
- * <[...] encrypted compressed data>
- *
- * Keying is accomplished using agree() (KDF function is in the
- * EllipticCurveKeyPair.cpp source) to generate 64 bytes of key. The first
- * 32 bytes are used as the Salsa20 key, and the last 32 bytes are used
- * as the HMAC key.
- *
- * @param to Identity of recipient of encrypted message
- * @param data Data to encrypt
- * @param len Length of data
- * @return Encrypted data or empty string on failure
- */
- std::string encrypt(const Identity &to,const void *data,unsigned int len) const;
-
- /**
- * Decrypt a message encrypted with encrypt()
- *
- * This identity must have a secret key.
- *
- * @param from Identity of sender of encrypted message
- * @param cdata Encrypted message
- * @param len Length of encrypted message
- * @return Decrypted data or empty string on failure
- */
- std::string decrypt(const Identity &from,const void *cdata,unsigned int len) const;
-
- /**
* Shortcut method to perform key agreement with another identity
*
* This identity must have its private portion.
diff --git a/node/Network.cpp b/node/Network.cpp
index d075bc7c..127de917 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -104,20 +104,10 @@ bool Network::Certificate::qualifyMembership(const Network::Certificate &mc) con
return true;
}
-Network::Network(const RuntimeEnvironment *renv,uint64_t id)
- throw(std::runtime_error) :
- _r(renv),
- _tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this),
- _id(id),
- _lastConfigUpdate(0),
- _destroyOnDelete(false)
-{
- if (controller() == _r->identity.address())
- throw std::runtime_error("cannot add a network for which I am the netconf master");
-}
-
Network::~Network()
{
+ delete _tap;
+
if (_destroyOnDelete) {
std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".mcerts");
@@ -129,26 +119,25 @@ Network::~Network()
}
}
-void Network::restoreState()
+SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t id)
+ throw(std::runtime_error)
{
- std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
- std::string confs;
- if (Utils::readFile(confPath.c_str(),confs)) {
- try {
- if (confs.length()) {
- Config conf(confs);
- if (conf.containsAllFields())
- setConfiguration(conf);
- }
- } catch ( ... ) {} // ignore invalid config on disk, we will re-request
- } else {
- // If the conf file isn't present, "touch" it so we'll remember
- // the existence of this network.
- FILE *tmp = fopen(confPath.c_str(),"w");
- if (tmp)
- fclose(tmp);
- }
- // TODO: restore membership certs
+ // We construct Network via a static method to ensure that it is immediately
+ // wrapped in a SharedPtr<>. Otherwise if there is traffic on the Ethernet
+ // tap device, a SharedPtr<> wrap can occur in the Ethernet frame handler
+ // that then causes the Network instance to be deleted before it is finished
+ // being constructed. C++ edge cases, how I love thee.
+ SharedPtr<Network> nw(new Network());
+ nw->_r = renv;
+ nw->_tap = new EthernetTap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,nw.ptr());
+ nw->_id = id;
+ nw->_lastConfigUpdate = 0;
+ nw->_destroyOnDelete = false;
+ if (nw->controller() == renv->identity.address()) // sanity check, this isn't supported for now
+ throw std::runtime_error("cannot add a network for which I am the netconf master");
+ nw->_restoreState();
+ nw->requestConfiguration();
+ return nw;
}
void Network::setConfiguration(const Network::Config &conf)
@@ -160,7 +149,7 @@ void Network::setConfiguration(const Network::Config &conf)
_myCertificate = conf.certificateOfMembership();
_lastConfigUpdate = Utils::now();
- _tap.setIps(conf.staticAddresses());
+ _tap->setIps(conf.staticAddresses());
std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
if (!Utils::writeFile(confPath.c_str(),conf.toString())) {
@@ -210,11 +199,13 @@ bool Network::isAllowed(const Address &peer) const
void Network::clean()
{
+ std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".mcerts");
+
Mutex::Lock _l(_lock);
- if (_configuration.isOpen())
+ if (_configuration.isOpen()) {
_membershipCertificates.clear();
- else {
- std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".mcerts");
+ unlink(mcdbPath.c_str());
+ } else {
FILE *mcdb = fopen(mcdbPath.c_str(),"wb");
bool writeError = false;
if (!mcdb) {
@@ -263,4 +254,26 @@ void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned
}
}
+void Network::_restoreState()
+{
+ std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
+ std::string confs;
+ if (Utils::readFile(confPath.c_str(),confs)) {
+ try {
+ if (confs.length()) {
+ Config conf(confs);
+ if (conf.containsAllFields())
+ setConfiguration(conf);
+ }
+ } catch ( ... ) {} // ignore invalid config on disk, we will re-request
+ } else {
+ // If the conf file isn't present, "touch" it so we'll remember
+ // the existence of this network.
+ FILE *tmp = fopen(confPath.c_str(),"w");
+ if (tmp)
+ fclose(tmp);
+ }
+ // TODO: restore membership certs
+}
+
} // namespace ZeroTier
diff --git a/node/Network.hpp b/node/Network.hpp
index d5c091e8..6d35b961 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -267,26 +267,29 @@ public:
private:
// Only NodeConfig can create, only SharedPtr can delete
- Network(const RuntimeEnvironment *renv,uint64_t id)
- throw(std::runtime_error);
+
+ // Actual construction happens in newInstance()
+ Network()
+ throw() :
+ _tap((EthernetTap *)0)
+ {
+ }
~Network();
/**
- * Called by NodeConfig after create
+ * Create a new Network instance and restore any saved state
*
- * This is called separately to avoid a rather evil race condition.
- * If config is restored in the constructor, then it's possible that
- * the tap will be assigned an IP and will start getting packets
- * before SharedPtr<Network> has gotten the pointer from the initial
- * object construct. That causes SharedPtr<Network> in the static
- * method that handles tap traffic to delete the object, resulting
- * in all sorts of utter madness. C++ is crazy like that.
+ * If there is no saved state, a dummy .conf is created on disk to remember
+ * this network across restarts.
*
- * Actually the way we're using SharedPtr<Network> is hacky and
- * ugly, so it's our fault sorta.
+ * @param renv Runtime environment
+ * @param id Network ID
+ * @return Reference counted pointer to new network
+ * @throws std::runtime_error Unable to create tap device or other fatal error
*/
- void restoreState();
+ static SharedPtr<Network> newInstance(const RuntimeEnvironment *renv,uint64_t id)
+ throw(std::runtime_error);
/**
* Causes all persistent disk presence to be erased on delete
@@ -305,7 +308,7 @@ public:
/**
* @return Ethernet tap
*/
- inline EthernetTap &tap() throw() { return _tap; }
+ inline EthernetTap &tap() throw() { return *_tap; }
/**
* @return Address of network's controlling node
@@ -344,7 +347,7 @@ public:
inline bool updateMulticastGroups()
{
Mutex::Lock _l(_lock);
- return _tap.updateMulticastGroups(_multicastGroups);
+ return _tap->updateMulticastGroups(_multicastGroups);
}
/**
@@ -403,11 +406,12 @@ public:
private:
static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
+ void _restoreState();
const RuntimeEnvironment *_r;
// Tap and tap multicast memberships
- EthernetTap _tap;
+ EthernetTap *_tap;
std::set<MulticastGroup> _multicastGroups;
// Membership certificates supplied by peers
diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp
index a8599508..e87c7ec0 100644
--- a/node/NodeConfig.cpp
+++ b/node/NodeConfig.cpp
@@ -76,10 +76,8 @@ NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken)
for(std::set<uint64_t>::iterator nwid(nwids.begin());nwid!=nwids.end();++nwid) {
try {
- SharedPtr<Network> nw(new Network(_r,*nwid));
+ SharedPtr<Network> nw(Network::newInstance(_r,*nwid));
_networks[*nwid] = nw;
- nw->restoreState();
- nw->requestConfiguration();
} catch (std::exception &exc) {
LOG("unable to create network %.16llx: %s",(unsigned long long)*nwid,exc.what());
} catch ( ... ) {
@@ -181,10 +179,8 @@ std::vector<std::string> NodeConfig::execute(const char *command)
_P("400 already a member of %.16llx",(unsigned long long)nwid);
} else {
try {
- SharedPtr<Network> nw(new Network(_r,nwid));
+ SharedPtr<Network> nw(Network::newInstance(_r,nwid));
_networks[nwid] = nw;
- nw->restoreState();
- nw->requestConfiguration();
_P("200 join %.16llx OK",(unsigned long long)nwid);
} catch (std::exception &exc) {
_P("500 join %.16llx ERROR: %s",(unsigned long long)nwid,exc.what());