// Decode peerinfo packet static int peermgtDecodePacketPeerinfo(struct s_peermgt *mgt, const struct s_packet_data *data) { const int peerinfo_size = (packet_PEERID_SIZE + nodeid_SIZE + peeraddr_SIZE); struct s_nodeid nodeid; struct s_peeraddr addr; int peerinfo_count; int localid; int pos; int relayid; int relayct; int relaypeerid; int i; int64_t r; if(data->pl_length > 4) { peerinfo_count = utilReadInt32(data->pl_buf); /** * JEWED: overflow on peerinfo_count >= (INT_MAX - 4) / peerinfo_size */ if(peerinfo_count > 0 && (((peerinfo_count * peerinfo_size) + 4) <= data->pl_length)) { if((peermgtGetRemoteFlag(mgt, data->peerid, peermgt_FLAG_RELAY)) && (!peeraddrIsInternal(&mgt->data[data->peerid].remoteaddr))) { // add indirect NodeDB entry relayid = data->peerid; relayct = mgt->data[relayid].conntime; } else { // don't change indirect NodeDB entry relayid = 0; relayct = 0; } r = (abs(cryptoRandInt()) % peerinfo_count); // randomly select a peer for(i=0; i<peerinfo_count; i++) { pos = (4 + (r * peerinfo_size)); /** * JEWED: OOB read pos + 4 > data->pl_length */ relaypeerid = utilReadInt32(&data->pl_buf[pos]); memcpy(nodeid.id, &data->pl_buf[(pos + (packet_PEERID_SIZE))], nodeid_SIZE); memcpy(addr.addr, &data->pl_buf[(pos + (packet_PEERID_SIZE + nodeid_SIZE))], peeraddr_SIZE); if(!peeraddrIsInternal(&addr)) { // only accept external PeerAddr localid = peermgtGetID(mgt, &nodeid); if(localid < 0) { // check if we are already connected to this NodeID // not connected yet nodedbUpdate(&mgt->nodedb, &nodeid, &addr, 1, 0, 0, relayid, relayct, relaypeerid); } else { if(localid > 0) { // already connected if(peeraddrIsInternal(&mgt->data[localid].remoteaddr)) { nodedbUpdate(&mgt->nodedb, &nodeid, &addr, 1, 1, 0, relayid, relayct, relaypeerid); } else { nodedbUpdate(&mgt->nodedb, &nodeid, NULL, 1, 1, 0, relayid, relayct, relaypeerid); } } } } r = ((r + 1) % peerinfo_count); } return 1; } } return 0; }
// Decode auth message S3 static int authDecodeS3(struct s_auth_state *authstate, const unsigned char *msg, const int msg_len) { int msgnum; int decmsg_len; unsigned char decmsg[auth_MAXMSGSIZE_S3]; if(msg_len > 6) { memcpy(decmsg, msg, 6); msgnum = utilReadInt16(&decmsg[4]); if(msgnum == (authstate->state + 1)) { decmsg_len = (4 + 2 + cryptoDec(&authstate->crypto_ctx[auth_CRYPTOCTX_CNEG], &decmsg[(4 + 2)], (auth_MAXMSGSIZE_S3 - 2 - 4), &msg[(4 + 2)], (msg_len - 2 - 4), auth_CNEGHMACSIZE, auth_CNEGIVSIZE)); if(decmsg_len >= (4 + 2 + auth_NONCESIZE + seq_SIZE + 4 + 8)) { memcpy(authstate->remote_keygen_nonce, &decmsg[(4 + 2)], auth_NONCESIZE); if((msgnum % 2) == 0) { memcpy(&authstate->keygen_nonce[0], authstate->local_keygen_nonce, auth_NONCESIZE); memcpy(&authstate->keygen_nonce[auth_NONCESIZE], authstate->remote_keygen_nonce, auth_NONCESIZE); } else { memcpy(&authstate->keygen_nonce[0], authstate->remote_keygen_nonce, auth_NONCESIZE); memcpy(&authstate->keygen_nonce[auth_NONCESIZE], authstate->local_keygen_nonce, auth_NONCESIZE); } memcpy(authstate->remote_seq, &decmsg[(4 + 2 + auth_NONCESIZE)], seq_SIZE); authstate->remote_peerid = utilReadInt32(&decmsg[(4 + 2 + auth_NONCESIZE + seq_SIZE)]); memcpy(authstate->remote_flags, &decmsg[(4 + 2 + auth_NONCESIZE + seq_SIZE + 4)], 8); return 1; } } } return 0; }
// Get indirect PeerAddr attributes. Returns 1 on success or 0 if the PeerAddr is not indirect. static int peeraddrGetIndirect(const struct s_peeraddr *peeraddr, int *relayid, int *relayct, int *peerid) { if(peeraddrGetInternalType(peeraddr) == peeraddr_INTERNAL_INDIRECT) { if(relayid != NULL) { *relayid = utilReadInt32(&peeraddr->addr[8]); } if(relayct != NULL) { *relayct = utilReadInt32(&peeraddr->addr[12]); } if(peerid != NULL) { *peerid = utilReadInt32(&peeraddr->addr[16]); } return 1; } else { return 0; } }
// Returns type of internal PeerAddr or -1 if it is not internal. static int peeraddrGetInternalType(const struct s_peeraddr *peeraddr) { if(peeraddrIsInternal(peeraddr)) { return utilReadInt32(&peeraddr->addr[4]); } else { return -1; } }
// Returns true if PeerAddr is internal. static int peeraddrIsInternal(const struct s_peeraddr *peeraddr) { int i; i = utilReadInt32(&peeraddr->addr[0]); if(i == 0) { return 1; } else { return 0; } }
// Decode relay-in packet static int peermgtDecodePacketRelayIn(struct s_peermgt *mgt, const struct s_packet_data *data) { int targetpeerid; int len = data->pl_length; if((len > 4) && (len < (peermgt_MSGSIZE_MAX - 4))) { targetpeerid = utilReadInt32(data->pl_buf); if(peermgtIsActiveRemoteID(mgt, targetpeerid)) { utilWriteInt32(&mgt->rrmsg.msg[0], data->peerid); memcpy(&mgt->rrmsg.msg[4], &data->pl_buf[4], (len - 4)); mgt->rrmsgpeerid = targetpeerid; mgt->rrmsgtype = packet_PLTYPE_RELAY_OUT; mgt->rrmsg.len = len; mgt->rrmsgusetargetaddr = 0; return 1; } } return 0; }
// Decode auth message. Returns 1 if message is accepted. int authmgtDecodeMsg(struct s_authmgt *mgt, const unsigned char *msg, const int msg_len, const struct s_peeraddr *peeraddr) { int authid; int authstateid; int tnow = utilGetClock(); int newsession; int dupid; CREATE_HUMAN_IP(peeraddr); debugf("[%s] AUTH message received", humanIp); if(msg_len <= 4) { debugf("[%s] Wrong AUTH message size: %d", humanIp, msg_len); return 0; } authid = utilReadInt32(msg); if(authid > 0) { // message belongs to existing auth session authstateid = (authid - 1); debugf("Found active auth session: %d", authstateid); if(authstateid >= idspSize(&mgt->idsp)) { debugf("[%s] wrong auth state ID", humanIp); return 0; } if(!authDecodeMsg(&mgt->authstate[authstateid], msg, msg_len)) { debugf("[%s] failed to decode AUTH message", humanIp); return 0; } mgt->lastrecv[authstateid] = tnow; mgt->peeraddr[authstateid] = *peeraddr; if(mgt->fastauth) { mgt->lastsend[authstateid] = (tnow - authmgt_RESEND_TIMEOUT - 3); } if((authIsAuthed(&mgt->authstate[authstateid])) && (!authIsCompleted(&mgt->authstate[authstateid]))) mgt->current_authed_id = authstateid; if((authIsCompleted(&mgt->authstate[authstateid])) && (!authIsPeerCompleted(&mgt->authstate[authstateid]))) { msgf("Host %s authorized", humanIp); mgt->current_completed_id = authstateid; } return 1; } else if(authid == 0) { debugf("starting new session for %s, authid: %d", humanIp, authid); // message requests new auth session dupid = authmgtFindAddr(mgt, peeraddr); // we already have this session if(dupid >= 0) { // auth session with same PeerAddr found. if(authIsPreauth(&mgt->authstate[dupid])) { return 0; } authmgtDelete(mgt, dupid); } authstateid = authmgtNew(mgt, peeraddr); if(authstateid < 0) { // all auth slots are full, search for unused sessions that can be replaced dupid = authmgtFindUnused(mgt); if(!(dupid < 0)) { authmgtDelete(mgt, dupid); authstateid = authmgtNew(mgt, peeraddr); debugf("new auth session started for %s, authstateid %d", humanIp, authstateid); } } if(!(authstateid < 0)) { if(authDecodeMsg(&mgt->authstate[authstateid], msg, msg_len)) { mgt->lastrecv[authstateid] = tnow; mgt->peeraddr[authstateid] = *peeraddr; if(mgt->fastauth) { mgt->lastsend[authstateid] = (tnow - authmgt_RESEND_TIMEOUT - 3); } return 1; } else { authmgtDelete(mgt, authstateid); } } } return 0; }
// return the peer ID static int packetGetPeerID(const unsigned char *pbuf) { int32_t *scr_peerid = ((int32_t *)pbuf); int32_t ne_peerid = (scr_peerid[0] ^ (scr_peerid[1] ^ scr_peerid[2])); return utilReadInt32((unsigned char *)&ne_peerid); }
// Decode input packet recursively. Decapsulates relayed packets if necessary. static int peermgtDecodePacketRecursive(struct s_peermgt *mgt, const unsigned char *packet, const int packet_len, const struct s_peeraddr *source_addr, const int tnow, const int depth) { int ret; int peerid; struct s_packet_data data = { .pl_buf_size = peermgt_MSGSIZE_MAX, .pl_buf = mgt->msgbuf }; struct s_peeraddr indirect_addr; ret = 0; if(packet_len > (packet_PEERID_SIZE + packet_HMAC_SIZE) && (depth < peermgt_DECODE_RECURSION_MAX_DEPTH)) { peerid = packetGetPeerID(packet); if(peermgtIsActiveID(mgt, peerid)) { if(peerid > 0) { // packet has an active PeerID mgt->msgsize = 0; if(packetDecode(&data, packet, packet_len, &mgt->ctx[peerid], &mgt->data[peerid].seq) > 0) { if((data.pl_length > 0) && (data.pl_length < peermgt_MSGSIZE_MAX)) { switch(data.pl_type) { case packet_PLTYPE_USERDATA: if(peermgtGetFlag(mgt, peermgt_FLAG_USERDATA)) { ret = 1; mgt->msgsize = data.pl_length; mgt->msgpeerid = data.peerid; } else { ret = 0; } break; case packet_PLTYPE_USERDATA_FRAGMENT: if(peermgtGetFlag(mgt, peermgt_FLAG_USERDATA)) { ret = peermgtDecodeUserdataFragment(mgt, &data); if(ret > 0) { mgt->msgsize = data.pl_length; mgt->msgpeerid = data.peerid; } } else { ret = 0; } break; case packet_PLTYPE_PEERINFO: ret = peermgtDecodePacketPeerinfo(mgt, &data); break; case packet_PLTYPE_PING: ret = peermgtDecodePacketPing(mgt, &data); break; case packet_PLTYPE_PONG: ret = peermgtDecodePacketPong(mgt, &data); break; case packet_PLTYPE_RELAY_IN: if(peermgtGetFlag(mgt, peermgt_FLAG_RELAY)) { ret = peermgtDecodePacketRelayIn(mgt, &data); } else { ret = 0; } break; case packet_PLTYPE_RELAY_OUT: if(data.pl_length > packet_PEERID_SIZE) { memcpy(mgt->relaymsgbuf, &data.pl_buf[4], (data.pl_length - packet_PEERID_SIZE)); peeraddrSetIndirect(&indirect_addr, peerid, mgt->data[peerid].conntime, utilReadInt32(&data.pl_buf[0])); // generate indirect PeerAddr ret = peermgtDecodePacketRecursive(mgt, mgt->relaymsgbuf, (data.pl_length - packet_PEERID_SIZE), &indirect_addr, tnow, (depth + 1)); // decode decapsulated packet } break; default: ret = 0; break; } if(ret > 0) { mgt->data[peerid].lastrecv = tnow; mgt->data[peerid].remoteaddr = *source_addr; return 1; } } } } else if(peerid == 0) { // packet has an anonymous PeerID if(packetDecode(&data, packet, packet_len, &mgt->ctx[0], NULL)) { switch(data.pl_type) { case packet_PLTYPE_AUTH: return peermgtDecodePacketAuth(mgt, &data, source_addr); default: return 0; } } } } } return 0; } // Decode input packet. Returns 1 on success. static int peermgtDecodePacket(struct s_peermgt *mgt, const unsigned char *packet, const int packet_len, const struct s_peeraddr *source_addr) { int tnow; tnow = utilGetTime(); return peermgtDecodePacketRecursive(mgt, packet, packet_len, source_addr, tnow, 0); }