|
|
|
@ -52,19 +52,12 @@ namespace ZeroTier { |
|
|
|
NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken) |
|
|
|
NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken) |
|
|
|
throw(std::runtime_error) : |
|
|
|
throw(std::runtime_error) : |
|
|
|
_r(renv), |
|
|
|
_r(renv), |
|
|
|
_authToken(authToken), |
|
|
|
|
|
|
|
_controlSocket(true,ZT_CONTROL_UDP_PORT,false,&_CBcontrolPacketHandler,this) |
|
|
|
_controlSocket(true,ZT_CONTROL_UDP_PORT,false,&_CBcontrolPacketHandler,this) |
|
|
|
{ |
|
|
|
{ |
|
|
|
SHA256_CTX sha; |
|
|
|
SHA256_CTX sha; |
|
|
|
|
|
|
|
|
|
|
|
SHA256_Init(&sha); |
|
|
|
|
|
|
|
SHA256_Update(&sha,_authToken.data(),_authToken.length()); |
|
|
|
|
|
|
|
SHA256_Final(_keys,&sha); // first 32 bytes of keys[]: Salsa20 key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SHA256_Init(&sha); |
|
|
|
SHA256_Init(&sha); |
|
|
|
SHA256_Update(&sha,_keys,32); |
|
|
|
SHA256_Update(&sha,authToken,strlen(authToken)); |
|
|
|
SHA256_Update(&sha,_authToken.data(),_authToken.length()); |
|
|
|
SHA256_Final(_controlSocketKey,&sha); |
|
|
|
SHA256_Final(_keys + 32,&sha); // second 32 bytes of keys[]: HMAC key
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
NodeConfig::~NodeConfig() |
|
|
|
NodeConfig::~NodeConfig() |
|
|
|
@ -146,64 +139,86 @@ std::vector<std::string> NodeConfig::execute(const char *command) |
|
|
|
return r; |
|
|
|
return r; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len) |
|
|
|
std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMessage(const void *key,unsigned long conversationId,const std::vector<std::string> &payload) |
|
|
|
|
|
|
|
throw(std::out_of_range) |
|
|
|
{ |
|
|
|
{ |
|
|
|
char hmacKey[32]; |
|
|
|
|
|
|
|
char hmac[32]; |
|
|
|
char hmac[32]; |
|
|
|
char buf[131072]; |
|
|
|
char keytmp[32]; |
|
|
|
NodeConfig *nc = (NodeConfig *)arg; |
|
|
|
std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > packets; |
|
|
|
const RuntimeEnvironment *_r = nc->_r; |
|
|
|
Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
packet.setSize(16); // HMAC and IV
|
|
|
|
// Minimum length
|
|
|
|
packet.append((uint32_t)(conversationId & 0xffffffff)); |
|
|
|
if (len < 28) |
|
|
|
for(unsigned int i=0;i<payload.size();++i) { |
|
|
|
return; |
|
|
|
packet.append(payload[i]); // will throw if too big
|
|
|
|
if (len >= sizeof(buf)) // only up to len - 28 bytes are used on receive/decrypt
|
|
|
|
packet.append((unsigned char)0); |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
if (((i + 1) >= payload.size())||((packet.size() + payload[i + 1].length() + 1) >= packet.capacity())) { |
|
|
|
// Compare first 16 bytes of HMAC, which is after IV in packet
|
|
|
|
Utils::getSecureRandom(packet.field(8,8),8); |
|
|
|
memcpy(hmacKey,nc->_keys + 32,32); |
|
|
|
|
|
|
|
*((uint64_t *)hmacKey) ^= *((const uint64_t *)data); // include IV in HMAC
|
|
|
|
memcpy(keytmp,key,32); |
|
|
|
HMAC::sha256(hmacKey,32,((const unsigned char *)data) + 28,len - 28,hmac); |
|
|
|
for(unsigned int i=0;i<32;++i) |
|
|
|
if (memcmp(hmac,((const unsigned char *)data) + 8,16)) |
|
|
|
keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20
|
|
|
|
return; |
|
|
|
HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac); |
|
|
|
|
|
|
|
memcpy(packet.field(0,8),hmac,8); |
|
|
|
// Decrypt payload if we passed HMAC
|
|
|
|
|
|
|
|
Salsa20 s20(nc->_keys,256,data); // first 64 bits of data are IV
|
|
|
|
Salsa20 s20(key,256,packet.field(8,8)); |
|
|
|
s20.decrypt(((const unsigned char *)data) + 28,buf,len - 28); |
|
|
|
s20.encrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16); |
|
|
|
|
|
|
|
|
|
|
|
// Null-terminate string for execute()
|
|
|
|
packets.push_back(packet); |
|
|
|
buf[len - 28] = (char)0; |
|
|
|
|
|
|
|
|
|
|
|
packet.setSize(16); // HMAC and IV
|
|
|
|
// Execute command
|
|
|
|
packet.append((uint32_t)(conversationId & 0xffffffff)); |
|
|
|
std::vector<std::string> r(nc->execute(buf)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Result packet contains a series of null-terminated results
|
|
|
|
|
|
|
|
unsigned int resultLen = 28; |
|
|
|
|
|
|
|
for(std::vector<std::string>::iterator i(r.begin());i!=r.end();++i) { |
|
|
|
|
|
|
|
if ((resultLen + i->length() + 1) >= sizeof(buf)) |
|
|
|
|
|
|
|
return; // result too long
|
|
|
|
|
|
|
|
memcpy(buf + resultLen,i->c_str(),i->length() + 1); |
|
|
|
|
|
|
|
resultLen += i->length() + 1; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Generate result packet IV
|
|
|
|
return packets; |
|
|
|
Utils::getSecureRandom(buf,8); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Generate result packet HMAC
|
|
|
|
bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,unsigned int len,unsigned long &conversationId,std::vector<std::string> &payload) |
|
|
|
memcpy(hmacKey,nc->_keys + 32,32); |
|
|
|
{ |
|
|
|
*((uint64_t *)hmacKey) ^= *((const uint64_t *)buf); // include IV in HMAC
|
|
|
|
char hmac[32]; |
|
|
|
HMAC::sha256(hmacKey,32,((const unsigned char *)buf) + 28,resultLen - 28,hmac); |
|
|
|
char keytmp[32]; |
|
|
|
memcpy(buf + 8,hmac,16); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Copy arbitrary tag from original packet
|
|
|
|
try { |
|
|
|
memcpy(buf + 24,((const unsigned char *)data) + 24,4); |
|
|
|
if (len < 20) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet(data,len); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(keytmp,key,32); |
|
|
|
|
|
|
|
for(unsigned int i=0;i<32;++i) |
|
|
|
|
|
|
|
keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20
|
|
|
|
|
|
|
|
HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac); |
|
|
|
|
|
|
|
if (memcmp(packet.field(0,8),hmac,8)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Salsa20 s20(key,256,packet.field(8,8)); |
|
|
|
|
|
|
|
s20.decrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
conversationId = packet.at<uint32_t>(16); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *pl = ((const char *)packet.data()) + 20; |
|
|
|
|
|
|
|
unsigned int pll = packet.size() - 20; |
|
|
|
|
|
|
|
payload.clear(); |
|
|
|
|
|
|
|
for(unsigned int i=0;i<pll;) { |
|
|
|
|
|
|
|
unsigned int eos = i; |
|
|
|
|
|
|
|
while ((eos < pll)&&(pl[eos])) |
|
|
|
|
|
|
|
++eos; |
|
|
|
|
|
|
|
if (eos > i) { |
|
|
|
|
|
|
|
payload.push_back(std::string(pl + i,eos - i)); |
|
|
|
|
|
|
|
i = eos + 1; |
|
|
|
|
|
|
|
} else break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Send encrypted result back to requester
|
|
|
|
return true; |
|
|
|
sock->send(remoteAddr,buf,resultLen,-1); |
|
|
|
|
|
|
|
} catch ( ... ) { |
|
|
|
} catch ( ... ) { |
|
|
|
TRACE("unexpected exception parsing control packet or generating response"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} // namespace ZeroTier
|
|
|
|
} // namespace ZeroTier
|
|
|
|
|