From ce14ba90045afa711506983c07ecc7e31c53f833 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 17 Oct 2013 06:41:52 -0400 Subject: [PATCH] Take the 0.6.0 opportunity to add flags to a few protocol verbs and do a bit more cleanup. Also fix it so certificates wont be accepted unless they are newer than existing ones. --- node/CertificateOfMembership.hpp | 6 ++-- node/Network.cpp | 56 +++++++++++++++++------------ node/Packet.cpp | 2 +- node/Packet.hpp | 23 ++++++------ node/PacketDecoder.cpp | 61 ++++++++++++++++---------------- node/PacketDecoder.hpp | 2 +- node/Switch.cpp | 2 ++ 7 files changed, 84 insertions(+), 68 deletions(-) diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp index 8f73f9e25..4cc2015bc 100644 --- a/node/CertificateOfMembership.hpp +++ b/node/CertificateOfMembership.hpp @@ -198,14 +198,14 @@ public: /** * @return Timestamp for this cert in ms since epoch (according to netconf's clock) */ - inline Address timestamp() const + inline uint64_t timestamp() const throw() { for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) { if (q->id == COM_RESERVED_ID_TIMESTAMP) - return Address(q->value); + return q->value; } - return Address(); + return 0ULL; } /** diff --git a/node/Network.cpp b/node/Network.cpp index 688477491..db84ada0d 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -178,10 +178,14 @@ void Network::requestConfiguration() void Network::addMembershipCertificate(const CertificateOfMembership &cert) { Mutex::Lock _l(_lock); + // We go ahead and accept certs provisionally even if _isOpen is true, since // that might be changed in short order if the user is fiddling in the UI. // These will be purged on clean() for open networks eventually. - _membershipCertificates[cert.issuedTo()] = cert; + + CertificateOfMembership &old = _membershipCertificates[cert.issuedTo()]; + if (cert.timestamp() >= old.timestamp()) + old = cert; } bool Network::isAllowed(const Address &peer) const @@ -299,30 +303,38 @@ void Network::_restoreState() _membershipCertificates.clear(); - try { - FILE *mcdb = fopen(mcdbPath.c_str(),"rb"); - if (mcdb) { - for(;;) { - long rlen = (long)fread(buf.data() + buf.size(),1,ZT_NETWORK_CERT_WRITE_BUF_SIZE - buf.size(),mcdb); - if (rlen <= 0) - break; - buf.setSize(buf.size() + (unsigned int)rlen); - unsigned int ptr = 0; - while ((ptr < (ZT_NETWORK_CERT_WRITE_BUF_SIZE / 2))&&(ptr < buf.size())) { - ptr += com.deserialize(buf,ptr); - if (com.issuedTo()) - _membershipCertificates[com.issuedTo()] = com; - } - if (ptr) { - memmove(buf.data(),buf.data() + ptr,buf.size() - ptr); - buf.setSize(buf.size() - ptr); + FILE *mcdb = fopen(mcdbPath.c_str(),"rb"); + if (mcdb) { + try { + char magic[6]; + if ((fread(magic,6,1,mcdb) == 1)&&(!memcmp("ZTMCD0",magic,6))) { + for(;;) { + long rlen = (long)fread(buf.data() + buf.size(),1,ZT_NETWORK_CERT_WRITE_BUF_SIZE - buf.size(),mcdb); + if (rlen <= 0) + break; + buf.setSize(buf.size() + (unsigned int)rlen); + unsigned int ptr = 0; + while ((ptr < (ZT_NETWORK_CERT_WRITE_BUF_SIZE / 2))&&(ptr < buf.size())) { + ptr += com.deserialize(buf,ptr); + if (com.issuedTo()) + _membershipCertificates[com.issuedTo()] = com; + } + if (ptr) { + memmove(buf.data(),buf.data() + ptr,buf.size() - ptr); + buf.setSize(buf.size() - ptr); + } } + fclose(mcdb); + } else { + fclose(mcdb); + Utils::rm(mcdbPath); } + } catch ( ... ) { + // Membership cert dump file invalid. We'll re-learn them off the net. + _membershipCertificates.clear(); + fclose(mcdb); + Utils::rm(mcdbPath); } - } catch ( ... ) { - // Membership cert dump file invalid. We'll re-learn them off the net. - _membershipCertificates.clear(); - Utils::rm(mcdbPath); } } } diff --git a/node/Packet.cpp b/node/Packet.cpp index 99cf39797..d809d4027 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -42,7 +42,7 @@ const char *Packet::verbString(Verb v) case VERB_WHOIS: return "WHOIS"; case VERB_RENDEZVOUS: return "RENDEZVOUS"; case VERB_FRAME: return "FRAME"; - case VERB_PROXY_FRAME: return "PROXY_FRAME"; + case VERB_BRIDGED_FRAME: return "BRIDGED_FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE"; diff --git a/node/Packet.hpp b/node/Packet.hpp index 4f139de84..486faebb8 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -155,7 +155,8 @@ #define ZT_PROTO_VERB_WHOIS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD) -#define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD) +#define ZT_PROTO_VERB_RENDEZVOUS_IDX_FLAGS (ZT_PACKET_IDX_PAYLOAD) +#define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_FLAGS + 1) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT (ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS + 5) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN (ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT + 2) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN + 1) @@ -199,8 +200,6 @@ #define ZT_PROTO_VERB_MULTICAST_FRAME_LEN_FRAME_LEN 2 #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME_LEN + ZT_PROTO_VERB_MULTICAST_FRAME_LEN_FRAME_LEN) -#define ZT_PROTO_VERB_NETWORK_MEMBERSHIP_CERTIFICATE_IDX_CERTIFICATE (ZT_PACKET_IDX_PAYLOAD) - #define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID + 8) #define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN + 2) @@ -434,11 +433,13 @@ public: * OK response payload: * <[...] binary serialized identity> * - * Error payload will be address queried. + * ERROR response payload: + * <[5] address> */ VERB_WHOIS = 4, /* Meet another node at a given protocol address: + * <[1] flags (unused, currently 0)> * <[5] ZeroTier address of peer that might be found at this address> * <[2] 16-bit protocol address port> * <[1] protocol address length (4 for IPv4, 16 for IPv6)> @@ -470,8 +471,7 @@ public: * <[...] ethernet payload> * * MAC addresses are derived from the packet's source and destination - * ZeroTier addresses. ZeroTier does not support VLANs or other extensions - * beyond core Ethernet. + * ZeroTier addresses. * * ERROR may be generated if a membership certificate is needed for a * closed network. Payload will be network ID. @@ -479,7 +479,7 @@ public: VERB_FRAME = 6, /* TODO: not implemented yet */ - VERB_PROXY_FRAME = 7, + VERB_BRIDGED_FRAME = 7, /* A multicast frame: * <[2] 16-bit propagation depth or 0xffff for "do not forward"> @@ -556,6 +556,7 @@ public: /* Network member certificate: * <[...] serialized certificate of membership> + * [ ... additional certificates may follow ...] * * Certificate contains network ID, peer it was issued for, etc. * @@ -583,9 +584,8 @@ public: * node can push to other peers to demonstrate its right to speak on * a given network. * - * ERROR may be NOT_FOUND if no such network is known, or - * UNSUPPORTED_OPERATION if the netconf service isn't available. The - * payload will be the network ID. + * ERROR response payload: + * <[8] 64-bit network ID> */ VERB_NETWORK_CONFIG_REQUEST = 11, @@ -594,7 +594,8 @@ public: * * This message can be sent by the network configuration master node * to request that nodes refresh their network configuration. It can - * thus be used to "push" updates. + * thus be used to "push" updates so that network config changes will + * take effect quickly. * * It does not generate an OK or ERROR message, and is treated only as * a hint to refresh now. diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index a8a55602e..f41000296 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -102,8 +102,8 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doRENDEZVOUS(_r,peer); case Packet::VERB_FRAME: return _doFRAME(_r,peer); - case Packet::VERB_PROXY_FRAME: - return _doPROXY_FRAME(_r,peer); + case Packet::VERB_BRIDGED_FRAME: + return _doBRIDGED_FRAME(_r,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(_r,peer); case Packet::VERB_MULTICAST_LIKE: @@ -151,9 +151,6 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr // if (_r->topology->isSupernode(source())) {} break; case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: { - // TODO: this allows anyone to request a membership cert, which is - // harmless until these contain possibly privacy-sensitive info. - // Then we'll need to be more careful. SharedPtr network(_r->nc->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if (network) network->pushMembershipCertificate(source(),true,Utils::now()); @@ -399,7 +396,7 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr return true; } -bool PacketDecoder::_doPROXY_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer) +bool PacketDecoder::_doBRIDGED_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer) { // TODO: bridging is not implemented yet return true; @@ -654,40 +651,44 @@ bool PacketDecoder::_doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedP bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer) { try { - CertificateOfMembership com(*this,ZT_PROTO_VERB_NETWORK_MEMBERSHIP_CERTIFICATE_IDX_CERTIFICATE); - if (!com.hasRequiredFields()) { - TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: at least one required field is missing",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } else if (com.signedBy()) { - SharedPtr signer(_r->topology->getPeer(com.signedBy())); - if (signer) { - if (com.verify(signer->identity())) { - uint64_t nwid = com.networkId(); - SharedPtr network(_r->nc->network(nwid)); - if (network) { - if (network->controller() == signer) { - network->addMembershipCertificate(com); - return true; + CertificateOfMembership com; + unsigned int ptr = ZT_PACKET_IDX_PAYLOAD; + while (ptr < size()) { + ptr += com.deserialize(*this,ptr); + if (!com.hasRequiredFields()) { + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: at least one required field is missing",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } else if (com.signedBy()) { + SharedPtr signer(_r->topology->getPeer(com.signedBy())); + if (signer) { + if (com.verify(signer->identity())) { + uint64_t nwid = com.networkId(); + SharedPtr network(_r->nc->network(nwid)); + if (network) { + if (network->controller() == signer) { + network->addMembershipCertificate(com); + return true; + } else { + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): signer %s is not the controller for network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str(),(unsigned long long)nwid); + return true; + } } else { - TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): signer %s is not the controller for network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str(),(unsigned long long)nwid); + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): not a member of network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)nwid); return true; } } else { - TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): not a member of network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)nwid); + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): failed signature verification for signer %s",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str()); return true; } } else { - TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): failed signature verification for signer %s",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str()); - return true; + _r->sw->requestWhois(com.signedBy()); + _step = DECODE_WAITING_FOR_NETWORK_MEMBERSHIP_CERTIFICATE_SIGNER_LOOKUP; + return false; } } else { - _r->sw->requestWhois(com.signedBy()); - _step = DECODE_WAITING_FOR_NETWORK_MEMBERSHIP_CERTIFICATE_SIGNER_LOOKUP; - return false; + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: no signature",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; } - } else { - TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: no signature",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; } } catch (std::exception &ex) { TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index 7a8aaca36..e50353178 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -117,7 +117,7 @@ private: bool _doWHOIS(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doRENDEZVOUS(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); - bool _doPROXY_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); + bool _doBRIDGED_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer); diff --git a/node/Switch.cpp b/node/Switch.cpp index f6fe4e3af..c6cd7987e 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -265,6 +265,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force) { // tell p1 where to find p2 Packet outp(p1,_r->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((unsigned char)0); p2.appendTo(outp); outp.append((uint16_t)cg.first.port()); if (cg.first.isV6()) { @@ -279,6 +280,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force) } { // tell p2 where to find p1 Packet outp(p2,_r->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((unsigned char)0); p1.appendTo(outp); outp.append((uint16_t)cg.second.port()); if (cg.second.isV6()) {