/* * Copyright (c)2013-2020 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: 2024-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. */ /****/ #include "Capability.hpp" #include "Utils.hpp" #include "Constants.hpp" #include "MAC.hpp" namespace ZeroTier { bool Capability::sign(const Identity &from,const Address &to) noexcept { uint8_t buf[ZT_CAPABILITY_MARSHAL_SIZE_MAX + 16]; _issuedTo = to; _signedBy = from.address(); _signatureLength = from.sign(buf,(unsigned int)marshal(buf,true),_signature,sizeof(_signature)); return _signatureLength > 0; } int Capability::marshal(uint8_t data[ZT_CAPABILITY_MARSHAL_SIZE_MAX],const bool forSign) const noexcept { int p = 0; if (forSign) { for(int k=0;k<8;++k) data[p++] = 0x7f; } Utils::storeBigEndian(data + p,_nwid); p += 8; Utils::storeBigEndian(data + p,(uint64_t)_ts); p += 8; Utils::storeBigEndian(data + p,_id); p += 4; Utils::storeBigEndian(data + p,(uint16_t)_ruleCount); p += 2; p += Capability::marshalVirtualNetworkRules(data + p,_rules,_ruleCount); // LEGACY: older versions supported multiple records with this being a maximum custody // chain length. This is deprecated so set the max chain length to one. data[p++] = (uint8_t)1; if (!forSign) { _issuedTo.copyTo(data + p); p += ZT_ADDRESS_LENGTH; _signedBy.copyTo(data + 0); p += ZT_ADDRESS_LENGTH; data[p++] = 1; // LEGACY: old versions require a reserved byte here Utils::storeBigEndian(data + p,(uint16_t)_signatureLength); p += 2; Utils::copy(data + p,_signature,_signatureLength); p += (int)_signatureLength; // LEGACY: older versions supported more than one record terminated by a zero address. for(int k=0;k(data); _ts = (int64_t)Utils::loadBigEndian(data + 8); _id = Utils::loadBigEndian(data + 16); const unsigned int rc = Utils::loadBigEndian(data + 20); if (rc > ZT_MAX_CAPABILITY_RULES) return -1; const int rulesLen = unmarshalVirtualNetworkRules(data + 22,len - 22,_rules,_ruleCount,rc); if (rulesLen < 0) return rulesLen; int p = 22 + rulesLen; if (p >= len) return -1; ++p; // LEGACY: skip old max record count // LEGACY: since it was once supported to have multiple records, scan them all. Since // this feature was never used, just set the signature and issued to and other related // fields each time and we should only ever see one. If there's more than one and the // last is not the controller, this credential will just fail validity check. for(unsigned int i=0;;++i) { if ((p + ZT_ADDRESS_LENGTH) > len) return -1; const Address to(data + p); p += ZT_ADDRESS_LENGTH; if (!to) break; _issuedTo = to; if ((p + ZT_ADDRESS_LENGTH) > len) return -1; _signedBy.setTo(data + p); p += ZT_ADDRESS_LENGTH + 1; // LEGACY: +1 to skip reserved field if ((p + 2) > len) return -1; _signatureLength = Utils::loadBigEndian(data + p); p += 2; if ((_signatureLength > sizeof(_signature))||((p + (int)_signatureLength) > len)) return -1; Utils::copy(_signature,data + p,_signatureLength); p += (int)_signatureLength; } if ((p + 2) > len) return -1; p += 2 + Utils::loadBigEndian(data + p); if (p > len) return -1; return p; } int Capability::marshalVirtualNetworkRules(uint8_t *data,const ZT_VirtualNetworkRule *const rules,const unsigned int ruleCount) noexcept { int p = 0; for(unsigned int i=0;i(data + p,rules[i].v.fwd.address); p += 8; Utils::storeBigEndian(data + p,rules[i].v.fwd.flags); p += 4; Utils::storeBigEndian(data + p,rules[i].v.fwd.length); p += 2; break; case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: data[p++] = 5; Address(rules[i].v.zt).copyTo(data + p); p += ZT_ADDRESS_LENGTH; break; case ZT_NETWORK_RULE_MATCH_VLAN_ID: data[p++] = 2; Utils::storeBigEndian(data + p,rules[i].v.vlanId); p += 2; break; case ZT_NETWORK_RULE_MATCH_VLAN_PCP: data[p++] = 1; data[p++] = rules[i].v.vlanPcp; break; case ZT_NETWORK_RULE_MATCH_VLAN_DEI: data[p++] = 1; data[p++] = rules[i].v.vlanDei; break; case ZT_NETWORK_RULE_MATCH_MAC_SOURCE: case ZT_NETWORK_RULE_MATCH_MAC_DEST: data[p++] = 6; MAC(rules[i].v.mac).copyTo(data + p); p += 6; break; case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE: case ZT_NETWORK_RULE_MATCH_IPV4_DEST: data[p++] = 5; data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[0]; data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[1]; data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[2]; data[p++] = reinterpret_cast(&(rules[i].v.ipv4.ip))[3]; data[p++] = rules[i].v.ipv4.mask; break; case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: case ZT_NETWORK_RULE_MATCH_IPV6_DEST: data[p++] = 17; Utils::copy<16>(data + p,rules[i].v.ipv6.ip); p += 16; data[p++] = rules[i].v.ipv6.mask; break; case ZT_NETWORK_RULE_MATCH_IP_TOS: data[p++] = 3; data[p++] = rules[i].v.ipTos.mask; data[p++] = rules[i].v.ipTos.value[0]; data[p++] = rules[i].v.ipTos.value[1]; break; case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL: data[p++] = 1; data[p++] = rules[i].v.ipProtocol; break; case ZT_NETWORK_RULE_MATCH_ETHERTYPE: data[p++] = 2; Utils::storeBigEndian(data + p,rules[i].v.etherType); p += 2; break; case ZT_NETWORK_RULE_MATCH_ICMP: data[p++] = 3; data[p++] = rules[i].v.icmp.type; data[p++] = rules[i].v.icmp.code; data[p++] = rules[i].v.icmp.flags; break; case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE: case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE: data[p++] = 4; Utils::storeBigEndian(data + p,rules[i].v.port[0]); p += 2; Utils::storeBigEndian(data + p,rules[i].v.port[1]); p += 2; break; case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: data[p++] = 8; Utils::storeBigEndian(data + p,rules[i].v.characteristics); p += 8; break; case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: data[p++] = 4; Utils::storeBigEndian(data + p,rules[i].v.frameSize[0]); p += 2; Utils::storeBigEndian(data + p,rules[i].v.frameSize[1]); p += 2; break; case ZT_NETWORK_RULE_MATCH_RANDOM: data[p++] = 4; Utils::storeBigEndian(data + p,rules[i].v.randomProbability); p += 4; break; case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE: case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND: case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR: case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL: case ZT_NETWORK_RULE_MATCH_TAG_SENDER: case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: data[p++] = 8; Utils::storeBigEndian(data + p,rules[i].v.tag.id); p += 4; Utils::storeBigEndian(data + p,rules[i].v.tag.value); p += 4; break; case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: data[p++] = 19; Utils::storeBigEndian(data + p,rules[i].v.intRange.start); p += 8; Utils::storeBigEndian(data + p,rules[i].v.intRange.start + (uint64_t)rules[i].v.intRange.end); p += 8; Utils::storeBigEndian(data + p,rules[i].v.intRange.idx); p += 2; data[p++] = rules[i].v.intRange.format; break; } } return p; } int Capability::unmarshalVirtualNetworkRules(const uint8_t *const data,const int len,ZT_VirtualNetworkRule *const rules,unsigned int &ruleCount,const unsigned int maxRuleCount) noexcept { int p = 0; unsigned int rc = 0; while (rc < maxRuleCount) { if (p >= len) return -1; rules[ruleCount].t = data[p++]; const int fieldLen = (int)data[p++]; if ((p + fieldLen) > len) return -1; switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3fU)) { default: break; case ZT_NETWORK_RULE_ACTION_TEE: case ZT_NETWORK_RULE_ACTION_WATCH: case ZT_NETWORK_RULE_ACTION_REDIRECT: if ((p + 14) > len) return -1; rules[ruleCount].v.fwd.address = Utils::loadBigEndian(data + p); p += 8; rules[ruleCount].v.fwd.flags = Utils::loadBigEndian(data + p); p += 4; rules[ruleCount].v.fwd.length = Utils::loadBigEndian(data + p); p += 2; break; case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: if ((p + ZT_ADDRESS_LENGTH) > len) return -1; rules[ruleCount].v.zt = Address(data + p).toInt(); p += ZT_ADDRESS_LENGTH; break; case ZT_NETWORK_RULE_MATCH_VLAN_ID: if ((p + 2) > len) return -1; rules[ruleCount].v.vlanId = Utils::loadBigEndian(data + p); p += 2; break; case ZT_NETWORK_RULE_MATCH_VLAN_PCP: if ((p + 1) > len) return -1; rules[ruleCount].v.vlanPcp = data[p++]; break; case ZT_NETWORK_RULE_MATCH_VLAN_DEI: if ((p + 1) > len) return -1; rules[ruleCount].v.vlanDei = data[p++]; break; case ZT_NETWORK_RULE_MATCH_MAC_SOURCE: case ZT_NETWORK_RULE_MATCH_MAC_DEST: if ((p + 6) > len) return -1; Utils::copy<6>(rules[ruleCount].v.mac,data + p); p += 6; break; case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE: case ZT_NETWORK_RULE_MATCH_IPV4_DEST: if ((p + 5) > len) return -1; Utils::copy<4>(&(rules[ruleCount].v.ipv4.ip),data + p); p += 4; rules[ruleCount].v.ipv4.mask = data[p++]; break; case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: case ZT_NETWORK_RULE_MATCH_IPV6_DEST: if ((p + 17) > len) return -1; Utils::copy<16>(rules[ruleCount].v.ipv6.ip,data + p); p += 16; rules[ruleCount].v.ipv6.mask = data[p++]; break; case ZT_NETWORK_RULE_MATCH_IP_TOS: if ((p + 3) > len) return -1; rules[ruleCount].v.ipTos.mask = data[p++]; rules[ruleCount].v.ipTos.value[0] = data[p++]; rules[ruleCount].v.ipTos.value[1] = data[p++]; break; case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL: if ((p + 1) > len) return -1; rules[ruleCount].v.ipProtocol = data[p++]; break; case ZT_NETWORK_RULE_MATCH_ETHERTYPE: if ((p + 2) > len) return -1; rules[ruleCount].v.etherType = Utils::loadBigEndian(data + p); p += 2; break; case ZT_NETWORK_RULE_MATCH_ICMP: if ((p + 3) > len) return -1; rules[ruleCount].v.icmp.type = data[p++]; rules[ruleCount].v.icmp.code = data[p++]; rules[ruleCount].v.icmp.flags = data[p++]; break; case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE: case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE: if ((p + 4) > len) return -1; rules[ruleCount].v.port[0] = Utils::loadBigEndian(data + p); p += 2; rules[ruleCount].v.port[1] = Utils::loadBigEndian(data + p); p += 2; break; case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: if ((p + 8) > len) return -1; rules[ruleCount].v.characteristics = Utils::loadBigEndian(data + p); p += 8; break; case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: if ((p + 4) > len) return -1; rules[ruleCount].v.frameSize[0] = Utils::loadBigEndian(data + p); p += 2; rules[ruleCount].v.frameSize[1] = Utils::loadBigEndian(data + p); p += 2; break; case ZT_NETWORK_RULE_MATCH_RANDOM: if ((p + 4) > len) return -1; rules[ruleCount].v.randomProbability = Utils::loadBigEndian(data + p); p += 4; break; case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE: case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND: case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR: case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL: case ZT_NETWORK_RULE_MATCH_TAG_SENDER: case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: if ((p + 4) > len) return -1; rules[ruleCount].v.tag.id = Utils::loadBigEndian(data + p); p += 4; rules[ruleCount].v.tag.value = Utils::loadBigEndian(data + p); p += 4; break; case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: if ((p + 19) > len) return -1; rules[ruleCount].v.intRange.start = Utils::loadBigEndian(data + p); p += 8; rules[ruleCount].v.intRange.end = (uint32_t)(Utils::loadBigEndian(data + p) - rules[ruleCount].v.intRange.start); p += 8; rules[ruleCount].v.intRange.idx = Utils::loadBigEndian(data + p); p += 2; rules[ruleCount].v.intRange.format = data[p++]; break; } p += fieldLen; ++rc; } ruleCount = rc; return p; } } // namespace ZeroTier