X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=blobdiff_plain;f=src%2Fsptps.c;h=712d50ea8d2d68d8787fda3793d2c2fcec36abd8;hp=e9ce94aef85ef361a706de4322a1117d5475160e;hb=cd5f222cc4e769395a7c6c8646abefe1d657f844;hpb=54fd228e696acc9d78a17845402640cc04e2c54c diff --git a/src/sptps.c b/src/sptps.c index e9ce94ae..712d50ea 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -1,6 +1,6 @@ /* sptps.c -- Simple Peer-to-Peer Security - Copyright (C) 2011-2013 Guus Sliepen , + Copyright (C) 2011-2015 Guus Sliepen , 2010 Brandon L. Black This program is free software; you can redistribute it and/or modify @@ -81,7 +81,7 @@ static void warning(sptps_t *s, const char *format, ...) { } // Send a record (datagram version, accepts all record types, handles encryption and authentication). -static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data, uint16_t len) { +static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const void *data, uint16_t len) { char buffer[len + 21UL]; // Create header with sequence number, length and record type @@ -102,7 +102,7 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data } } // Send a record (private version, accepts all record types, handles encryption and authentication). -static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_t len) { +static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_t len) { if(s->datagram) return send_record_priv_datagram(s, type, data, len); @@ -127,7 +127,7 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_ } // Send an application record. -bool sptps_send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len) { +bool sptps_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len) { // Sanity checks: application cannot send data before handshake is finished, // and only record types 0..127 are allowed. if(!s->outstate) @@ -204,7 +204,7 @@ static bool generate_key_material(sptps_t *s, const char *shared, size_t len) { // Create the HMAC seed, which is "key expansion" + session label + server nonce + client nonce char seed[s->labellen + 64 + 13]; - strcpy(seed, "key expansion"); + memcpy(seed, "key expansion", 13); if(s->initiator) { memcpy(seed + 13, s->mykex + 1, 32); memcpy(seed + 45, s->hiskex + 1, 32); @@ -384,10 +384,11 @@ static bool sptps_check_seqno(sptps_t *s, uint32_t seqno, bool update_state) { if (update_state) s->farfuture++; if(farfuture) - return error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture); + return update_state ? error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture) : false; // Unless we have seen lots of them, in which case we consider the others lost. - warning(s, "Lost %d packets\n", seqno - s->inseqno); + if(update_state) + warning(s, "Lost %d packets\n", seqno - s->inseqno); if (update_state) { // Mark all packets in the replay window as being late. memset(s->late, 255, s->replaywin); @@ -395,7 +396,7 @@ static bool sptps_check_seqno(sptps_t *s, uint32_t seqno, bool update_state) { } else if (seqno < s->inseqno) { // If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it. if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8))) - return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno); + return update_state ? error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno) : false; } else if (update_state) { // We missed some packets. Mark them in the bitmap as being late. for(int i = s->inseqno; i < seqno; i++) @@ -424,7 +425,7 @@ static bool sptps_check_seqno(sptps_t *s, uint32_t seqno, bool update_state) { } // Check datagram for valid HMAC -bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) { +bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len) { if(!s->instate || len < 21) return error(s, EIO, "Received short packet"); @@ -447,6 +448,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len uint32_t seqno; memcpy(&seqno, data, 4); seqno = ntohl(seqno); + data += 4; len -= 4; if(!s->instate) { if(seqno != s->inseqno) @@ -454,39 +456,40 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len s->inseqno = seqno + 1; - uint8_t type = data[4]; + uint8_t type = *(data++); len--; if(type != SPTPS_HANDSHAKE) return error(s, EIO, "Application record received before handshake finished"); - return receive_handshake(s, data + 5, len - 5); + return receive_handshake(s, data, len); } // Decrypt char buffer[len]; - size_t outlen; - - if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen)) + if(!chacha_poly1305_decrypt(s->incipher, seqno, data, len, buffer, &outlen)) return error(s, EIO, "Failed to decrypt and verify packet"); if(!sptps_check_seqno(s, seqno, true)) return false; // Append a NULL byte for safety. - buffer[len - 20] = 0; + buffer[outlen] = 0; + + data = buffer; + len = outlen; - uint8_t type = buffer[0]; + uint8_t type = *(data++); len--; if(type < SPTPS_HANDSHAKE) { if(!s->instate) return error(s, EIO, "Application record received before handshake finished"); - if(!s->receive_record(s->handle, type, buffer + 1, len - 21)) - abort(); + if(!s->receive_record(s->handle, type, data, len)) + return false; } else if(type == SPTPS_HANDSHAKE) { - if(!receive_handshake(s, buffer + 1, len - 21)) - abort(); + if(!receive_handshake(s, data, len)) + return false; } else { return error(s, EIO, "Invalid record type %d", type); } @@ -495,94 +498,96 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len } // Receive incoming data. Check if it contains a complete record, if so, handle it. -bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { +size_t sptps_receive_data(sptps_t *s, const void *data, size_t len) { + size_t total_read = 0; + if(!s->state) return error(s, EIO, "Invalid session state zero"); if(s->datagram) - return sptps_receive_data_datagram(s, data, len); + return sptps_receive_data_datagram(s, data, len) ? len : false; - while(len) { - // First read the 2 length bytes. - if(s->buflen < 2) { - size_t toread = 2 - s->buflen; - if(toread > len) - toread = len; + // First read the 2 length bytes. + if(s->buflen < 2) { + size_t toread = 2 - s->buflen; + if(toread > len) + toread = len; - memcpy(s->inbuf + s->buflen, data, toread); + memcpy(s->inbuf + s->buflen, data, toread); - s->buflen += toread; - len -= toread; - data += toread; + total_read += toread; + s->buflen += toread; + len -= toread; + data += toread; - // Exit early if we don't have the full length. - if(s->buflen < 2) - return true; + // Exit early if we don't have the full length. + if(s->buflen < 2) + return total_read; - // Get the length bytes + // Get the length bytes - memcpy(&s->reclen, s->inbuf, 2); - s->reclen = ntohs(s->reclen); + memcpy(&s->reclen, s->inbuf, 2); + s->reclen = ntohs(s->reclen); - // If we have the length bytes, ensure our buffer can hold the whole request. - s->inbuf = realloc(s->inbuf, s->reclen + 19UL); - if(!s->inbuf) - return error(s, errno, strerror(errno)); + // If we have the length bytes, ensure our buffer can hold the whole request. + s->inbuf = realloc(s->inbuf, s->reclen + 19UL); + if(!s->inbuf) + return error(s, errno, strerror(errno)); - // Exit early if we have no more data to process. - if(!len) - return true; - } + // Exit early if we have no more data to process. + if(!len) + return total_read; + } - // Read up to the end of the record. - size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen; - if(toread > len) - toread = len; + // Read up to the end of the record. + size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen; + if(toread > len) + toread = len; - memcpy(s->inbuf + s->buflen, data, toread); - s->buflen += toread; - len -= toread; - data += toread; + memcpy(s->inbuf + s->buflen, data, toread); + total_read += toread; + s->buflen += toread; + len -= toread; + data += toread; - // If we don't have a whole record, exit. - if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL)) - return true; + // If we don't have a whole record, exit. + if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL)) + return total_read; - // Update sequence number. + // Update sequence number. - uint32_t seqno = s->inseqno++; + uint32_t seqno = s->inseqno++; - // Check HMAC and decrypt. - if(s->instate) { - if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL)) - return error(s, EINVAL, "Failed to decrypt and verify record"); - } - - // Append a NULL byte for safety. - s->inbuf[s->reclen + 3UL] = 0; + // Check HMAC and decrypt. + if(s->instate) { + if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL)) + return error(s, EINVAL, "Failed to decrypt and verify record"); + } - uint8_t type = s->inbuf[2]; + // Append a NULL byte for safety. + s->inbuf[s->reclen + 3UL] = 0; - if(type < SPTPS_HANDSHAKE) { - if(!s->instate) - return error(s, EIO, "Application record received before handshake finished"); - if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen)) - return false; - } else if(type == SPTPS_HANDSHAKE) { - if(!receive_handshake(s, s->inbuf + 3, s->reclen)) - return false; - } else { - return error(s, EIO, "Invalid record type %d", type); - } + uint8_t type = s->inbuf[2]; - s->buflen = 0; + if(type < SPTPS_HANDSHAKE) { + if(!s->instate) + return error(s, EIO, "Application record received before handshake finished"); + if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen)) + return false; + } else if(type == SPTPS_HANDSHAKE) { + if(!receive_handshake(s, s->inbuf + 3, s->reclen)) + return false; + } else { + return error(s, EIO, "Invalid record type %d", type); } - return true; + s->buflen = 0; + + return total_read; } // Start a SPTPS session. -bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) { +bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const void *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) { // Initialise struct sptps memset(s, 0, sizeof *s);