diff options
Diffstat (limited to 'node/Network.cpp')
| -rw-r--r-- | node/Network.cpp | 184 |
1 files changed, 149 insertions, 35 deletions
diff --git a/node/Network.cpp b/node/Network.cpp index 82fa6398..cfc1204a 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -30,6 +30,9 @@ #include <stdlib.h> #include <math.h> +#include <algorithm> +#include <utility> + #include "Constants.hpp" #include "RuntimeEnvironment.hpp" #include "NodeConfig.hpp" @@ -40,73 +43,135 @@ namespace ZeroTier { -void Network::CertificateOfMembership::addParameter(uint64_t id,uint64_t value,uint64_t maxDelta) +void Network::CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta) { - _params.push_back(_Parameter(id,value,maxDelta)); - std::sort(_params.begin(),_params.end(),_SortByIdComparison()); + _signedBy.zero(); + + for(std::vector<_Qualifier>::iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) { + if (q->id == id) { + q->value = value; + q->maxDelta = maxDelta; + return; + } + } + + _qualifiers.push_back(_Qualifier(id,value,maxDelta)); + std::sort(_qualifiers.begin(),_qualifiers.end()); } std::string Network::CertificateOfMembership::toString() const { - uint64_t tmp[3000]; - unsigned long n = 0; - for(std::vector<_Parameter>::const_iterator p(_params.begin());p!=_params.end();++p) { - tmp[n++] = Utils::hton(p->id); - tmp[n++] = Utils::hton(p->value); - tmp[n++] = Utils::hton(p->maxDelta); - if (n >= 3000) - break; // sanity check -- certificates will never even approach this size + std::string s; + + uint64_t *buf = new uint64_t[_qualifiers.size() * 3]; + try { + unsigned int ptr = 0; + for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) { + buf[ptr++] = Utils::hton(q->id); + buf[ptr++] = Utils::hton(q->value); + buf[ptr++] = Utils::hton(q->maxDelta); + } + s.append(Utils::hex(buf,ptr * sizeof(uint64_t))); + delete [] buf; + } catch ( ... ) { + delete [] buf; + throw; + } + + s.push_back(':'); + + s.append(_signedBy.toString()); + + if (_signedBy) { + s.push_back(':'); + s.append(Utils::hex(_signature.data,_signature.size())); } - return Utils::hex(tmp,sizeof(uint64_t) * n); + + return s; } void Network::CertificateOfMembership::fromString(const char *s) { - std::string tmp(Utils::unhex(s)); - _params.clear(); - const char *ptr = tmp.data(); - unsigned long remaining = tmp.length(); - while (remaining >= 24) { - _Parameter p; - p.id = Utils::ntoh(*((const uint64_t *)(ptr))); - p.value = Utils::ntoh(*((const uint64_t *)(ptr + 8))); - p.maxDelta = Utils::ntoh(*((const uint64_t *)(ptr + 16))); - _params.push_back(p); - ptr += 24; - remaining -= 24; + _qualifiers.clear(); + _signedBy.zero(); + + unsigned int colonAt = 0; + while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt; + + if (colonAt) { + unsigned int buflen = colonAt / 2; + char *buf = new char[buflen]; + unsigned int bufactual = Utils::unhex(s,colonAt,buf,buflen); + char *bufptr = buf; + try { + while (bufactual >= 24) { + _qualifiers.push_back(_Qualifier()); + _qualifiers.back().id = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8; + _qualifiers.back().value = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8; + _qualifiers.back().maxDelta = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8; + bufactual -= 24; + } + } catch ( ... ) {} + delete [] buf; } + + if (s[colonAt]) { + s += colonAt + 1; + colonAt = 0; + while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt; + + if (colonAt) { + char addrbuf[ZT_ADDRESS_LENGTH]; + if (Utils::unhex(s,colonAt,addrbuf,sizeof(addrbuf)) == ZT_ADDRESS_LENGTH) + _signedBy.setTo(addrbuf,ZT_ADDRESS_LENGTH); + + if ((_signedBy)&&(s[colonAt])) { + s += colonAt + 1; + colonAt = 0; + while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt; + + if (colonAt) { + if (Utils::unhex(s,colonAt,_signature.data,_signature.size()) != _signature.size()) + _signedBy.zero(); + } else _signedBy.zero(); + } else _signedBy.zero(); + } + } + + std::sort(_qualifiers.begin(),_qualifiers.end()); + std::unique(_qualifiers.begin(),_qualifiers.end()); } -bool Network::CertificateOfMembership::compare(const CertificateOfMembership &other) const +bool Network::CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const throw() { unsigned long myidx = 0; unsigned long otheridx = 0; - while (myidx < _params.size()) { + while (myidx < _qualifiers.size()) { // Fail if we're at the end of other, since this means the field is // missing. - if (otheridx >= other._params.size()) + if (otheridx >= other._qualifiers.size()) return false; // Seek to corresponding tuple in other, ignoring tuples that // we may not have. If we run off the end of other, the tuple is // missing. This works because tuples are sorted by ID. - while (other._params[otheridx].id != _params[myidx].id) { + while (other._qualifiers[otheridx].id != _qualifiers[myidx].id) { ++otheridx; - if (otheridx >= other._params.size()) + if (otheridx >= other._qualifiers.size()) return false; } // Compare to determine if the absolute value of the difference // between these two parameters is within our maxDelta. - uint64_t a = _params[myidx].value; - uint64_t b = other._params[myidx].value; + uint64_t a = _qualifiers[myidx].value; + uint64_t b = other._qualifiers[myidx].value; if (a >= b) { - if ((a - b) > _params[myidx].maxDelta) + if ((a - b) > _qualifiers[myidx].maxDelta) return false; } else { - if ((b - a) > _params[myidx].maxDelta) + if ((b - a) > _qualifiers[myidx].maxDelta) return false; } @@ -116,6 +181,55 @@ bool Network::CertificateOfMembership::compare(const CertificateOfMembership &ot return true; } +bool Network::CertificateOfMembership::sign(const Identity &with) +{ + uint64_t *buf = new uint64_t[_qualifiers.size() * 3]; + unsigned int ptr = 0; + for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) { + buf[ptr++] = Utils::hton(q->id); + buf[ptr++] = Utils::hton(q->value); + buf[ptr++] = Utils::hton(q->maxDelta); + } + + try { + _signature = with.sign(buf,ptr * sizeof(uint64_t)); + _signedBy = with.address(); + delete [] buf; + return true; + } catch ( ... ) { + _signedBy.zero(); + delete [] buf; + return false; + } +} + +bool Network::CertificateOfMembership::verify(const Identity &id) const +{ + if (!_signedBy) + return false; + if (id.address() != _signedBy) + return false; + + uint64_t *buf = new uint64_t[_qualifiers.size() * 3]; + unsigned int ptr = 0; + for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) { + buf[ptr++] = Utils::hton(q->id); + buf[ptr++] = Utils::hton(q->value); + buf[ptr++] = Utils::hton(q->maxDelta); + } + + bool valid = false; + try { + valid = id.verify(buf,ptr * sizeof(uint64_t),_signature); + delete [] buf; + } catch ( ... ) { + delete [] buf; + } + return valid; +} + +// --------------------------------------------------------------------------- + const Network::MulticastRates::Rate Network::MulticastRates::GLOBAL_DEFAULT_RATE(65535,65535,64); const char *Network::statusString(const Status s) @@ -253,7 +367,7 @@ bool Network::isAllowed(const Address &peer) const std::map<Address,CertificateOfMembership>::const_iterator pc(_membershipCertificates.find(peer)); if (pc == _membershipCertificates.end()) return false; - return _myCertificate.compare(pc->second); + return _myCertificate.agreesWith(pc->second); } catch (std::exception &exc) { TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer.toString().c_str(),exc.what()); } catch ( ... ) { @@ -282,7 +396,7 @@ void Network::clean() } for(std::map<Address,CertificateOfMembership>::iterator i=(_membershipCertificates.begin());i!=_membershipCertificates.end();) { - if (_myCertificate.compare(i->second)) { + if (_myCertificate.agreesWith(i->second)) { if ((!writeError)&&(mcdb)) { char tmp[ZT_ADDRESS_LENGTH]; i->first.copyTo(tmp,ZT_ADDRESS_LENGTH); |
