1 /*
2 * Copyright (c)2019 ZeroTier, Inc.
3 *
4 * Use of this software is governed by the Business Source License included
5 * in the LICENSE.TXT file in the project's root directory.
6 *
7 * Change Date: 2025-01-01
8 *
9 * On the date above, in accordance with the Business Source License, use
10 * of this software will be governed by version 2.0 of the Apache License.
11 */
12 /****/
13
14 #include <algorithm>
15
16 #include "Membership.hpp"
17 #include "RuntimeEnvironment.hpp"
18 #include "Peer.hpp"
19 #include "Topology.hpp"
20 #include "Switch.hpp"
21 #include "Packet.hpp"
22 #include "Node.hpp"
23 #include "Trace.hpp"
24
25 namespace ZeroTier {
26
Membership()27 Membership::Membership() :
28 _lastUpdatedMulticast(0),
29 _comRevocationThreshold(0),
30 _lastPushedCredentials(0),
31 _revocations(4),
32 _remoteTags(4),
33 _remoteCaps(4),
34 _remoteCoos(4)
35 {
36 }
37
pushCredentials(const RuntimeEnvironment * RR,void * tPtr,const int64_t now,const Address & peerAddress,const NetworkConfig & nconf)38 void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf)
39 {
40 const Capability *sendCaps[ZT_MAX_NETWORK_CAPABILITIES];
41 unsigned int sendCapCount = 0;
42 for(unsigned int c=0;c<nconf.capabilityCount;++c)
43 sendCaps[sendCapCount++] = &(nconf.capabilities[c]);
44
45 const Tag *sendTags[ZT_MAX_NETWORK_TAGS];
46 unsigned int sendTagCount = 0;
47 for(unsigned int t=0;t<nconf.tagCount;++t)
48 sendTags[sendTagCount++] = &(nconf.tags[t]);
49
50 const CertificateOfOwnership *sendCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
51 unsigned int sendCooCount = 0;
52 for(unsigned int c=0;c<nconf.certificateOfOwnershipCount;++c)
53 sendCoos[sendCooCount++] = &(nconf.certificatesOfOwnership[c]);
54
55 unsigned int capPtr = 0;
56 unsigned int tagPtr = 0;
57 unsigned int cooPtr = 0;
58 bool sendCom = (bool)(nconf.com);
59 while ((capPtr < sendCapCount)||(tagPtr < sendTagCount)||(cooPtr < sendCooCount)||(sendCom)) {
60 Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
61
62 if (sendCom) {
63 sendCom = false;
64 nconf.com.serialize(outp);
65 }
66 outp.append((uint8_t)0x00);
67
68 const unsigned int capCountAt = outp.size();
69 outp.addSize(2);
70 unsigned int thisPacketCapCount = 0;
71 while ((capPtr < sendCapCount)&&((outp.size() + sizeof(Capability) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
72 sendCaps[capPtr++]->serialize(outp);
73 ++thisPacketCapCount;
74 }
75 outp.setAt(capCountAt,(uint16_t)thisPacketCapCount);
76
77 const unsigned int tagCountAt = outp.size();
78 outp.addSize(2);
79 unsigned int thisPacketTagCount = 0;
80 while ((tagPtr < sendTagCount)&&((outp.size() + sizeof(Tag) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
81 sendTags[tagPtr++]->serialize(outp);
82 ++thisPacketTagCount;
83 }
84 outp.setAt(tagCountAt,(uint16_t)thisPacketTagCount);
85
86 // No revocations, these propagate differently
87 outp.append((uint16_t)0);
88
89 const unsigned int cooCountAt = outp.size();
90 outp.addSize(2);
91 unsigned int thisPacketCooCount = 0;
92 while ((cooPtr < sendCooCount)&&((outp.size() + sizeof(CertificateOfOwnership) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
93 sendCoos[cooPtr++]->serialize(outp);
94 ++thisPacketCooCount;
95 }
96 outp.setAt(cooCountAt,(uint16_t)thisPacketCooCount);
97
98 outp.compress();
99 RR->sw->send(tPtr,outp,true);
100 }
101
102 _lastPushedCredentials = now;
103 }
104
addCredential(const RuntimeEnvironment * RR,void * tPtr,const NetworkConfig & nconf,const CertificateOfMembership & com)105 Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com)
106 {
107 const int64_t newts = com.timestamp();
108 if (newts <= _comRevocationThreshold) {
109 RR->t->credentialRejected(tPtr,com,"revoked");
110 return ADD_REJECTED;
111 }
112
113 const int64_t oldts = _com.timestamp();
114 if (newts < oldts) {
115 RR->t->credentialRejected(tPtr,com,"old");
116 return ADD_REJECTED;
117 }
118 if ((newts == oldts)&&(_com == com))
119 return ADD_ACCEPTED_REDUNDANT;
120
121 switch(com.verify(RR,tPtr)) {
122 default:
123 RR->t->credentialRejected(tPtr,com,"invalid");
124 return ADD_REJECTED;
125 case 0:
126 _com = com;
127 return ADD_ACCEPTED_NEW;
128 case 1:
129 return ADD_DEFERRED_FOR_WHOIS;
130 }
131 }
132
133 // Template out addCredential() for many cred types to avoid copypasta
134 template<typename C>
_addCredImpl(Hashtable<uint32_t,C> & remoteCreds,const Hashtable<uint64_t,int64_t> & revocations,const RuntimeEnvironment * RR,void * tPtr,const NetworkConfig & nconf,const C & cred)135 static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remoteCreds,const Hashtable<uint64_t,int64_t> &revocations,const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const C &cred)
136 {
137 C *rc = remoteCreds.get(cred.id());
138 if (rc) {
139 if (rc->timestamp() > cred.timestamp()) {
140 RR->t->credentialRejected(tPtr,cred,"old");
141 return Membership::ADD_REJECTED;
142 }
143 if (*rc == cred)
144 return Membership::ADD_ACCEPTED_REDUNDANT;
145 }
146
147 const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
148 if ((rt)&&(*rt >= cred.timestamp())) {
149 RR->t->credentialRejected(tPtr,cred,"revoked");
150 return Membership::ADD_REJECTED;
151 }
152
153 switch(cred.verify(RR,tPtr)) {
154 default:
155 RR->t->credentialRejected(tPtr,cred,"invalid");
156 return Membership::ADD_REJECTED;
157 case 0:
158 if (!rc)
159 rc = &(remoteCreds[cred.id()]);
160 *rc = cred;
161 return Membership::ADD_ACCEPTED_NEW;
162 case 1:
163 return Membership::ADD_DEFERRED_FOR_WHOIS;
164 }
165 }
166
addCredential(const RuntimeEnvironment * RR,void * tPtr,const NetworkConfig & nconf,const Tag & tag)167 Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag) { return _addCredImpl<Tag>(_remoteTags,_revocations,RR,tPtr,nconf,tag); }
addCredential(const RuntimeEnvironment * RR,void * tPtr,const NetworkConfig & nconf,const Capability & cap)168 Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap) { return _addCredImpl<Capability>(_remoteCaps,_revocations,RR,tPtr,nconf,cap); }
addCredential(const RuntimeEnvironment * RR,void * tPtr,const NetworkConfig & nconf,const CertificateOfOwnership & coo)169 Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo) { return _addCredImpl<CertificateOfOwnership>(_remoteCoos,_revocations,RR,tPtr,nconf,coo); }
170
addCredential(const RuntimeEnvironment * RR,void * tPtr,const NetworkConfig & nconf,const Revocation & rev)171 Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev)
172 {
173 int64_t *rt;
174 switch(rev.verify(RR,tPtr)) {
175 default:
176 RR->t->credentialRejected(tPtr,rev,"invalid");
177 return ADD_REJECTED;
178 case 0: {
179 const Credential::Type ct = rev.type();
180 switch(ct) {
181 case Credential::CREDENTIAL_TYPE_COM:
182 if (rev.threshold() > _comRevocationThreshold) {
183 _comRevocationThreshold = rev.threshold();
184 return ADD_ACCEPTED_NEW;
185 }
186 return ADD_ACCEPTED_REDUNDANT;
187 case Credential::CREDENTIAL_TYPE_CAPABILITY:
188 case Credential::CREDENTIAL_TYPE_TAG:
189 case Credential::CREDENTIAL_TYPE_COO:
190 rt = &(_revocations[credentialKey(ct,rev.credentialId())]);
191 if (*rt < rev.threshold()) {
192 *rt = rev.threshold();
193 _comRevocationThreshold = rev.threshold();
194 return ADD_ACCEPTED_NEW;
195 }
196 return ADD_ACCEPTED_REDUNDANT;
197 default:
198 RR->t->credentialRejected(tPtr,rev,"invalid");
199 return ADD_REJECTED;
200 }
201 }
202 case 1:
203 return ADD_DEFERRED_FOR_WHOIS;
204 }
205 }
206
clean(const int64_t now,const NetworkConfig & nconf)207 void Membership::clean(const int64_t now,const NetworkConfig &nconf)
208 {
209 _cleanCredImpl<Tag>(nconf,_remoteTags);
210 _cleanCredImpl<Capability>(nconf,_remoteCaps);
211 _cleanCredImpl<CertificateOfOwnership>(nconf,_remoteCoos);
212 }
213
214 } // namespace ZeroTier
215