X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Fsptps.c;h=4a9683f29aed3076de37f9051ab34eb50fbe35d9;hb=89715454c083aaeb4dc73340f2d0ab9a3d9503e0;hp=7d6293c081bb913c66df453c114a0f15086d7c08;hpb=f0e7e6b03e34e69cac5b01a2d943ad3b9b59d36c;p=tinc diff --git a/src/sptps.c b/src/sptps.c index 7d6293c0..4a9683f2 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-2014 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) @@ -370,14 +370,73 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) { } } +static bool sptps_check_seqno(sptps_t *s, uint32_t seqno, bool update_state) { + // Replay protection using a sliding window of configurable size. + // s->inseqno is expected sequence number + // seqno is received sequence number + // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet + // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno. + if(s->replaywin) { + if(seqno != s->inseqno) { + if(seqno >= s->inseqno + s->replaywin * 8) { + // Prevent packets that jump far ahead of the queue from causing many others to be dropped. + bool farfuture = s->farfuture < s->replaywin >> 2; + 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); + + // 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) { + // Mark all packets in the replay window as being late. + memset(s->late, 255, s->replaywin); + } + } 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); + } else if (update_state) { + // We missed some packets. Mark them in the bitmap as being late. + for(int i = s->inseqno; i < seqno; i++) + s->late[(i / 8) % s->replaywin] |= 1 << i % 8; + } + } + + if (update_state) { + // Mark the current packet as not being late. + s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8); + s->farfuture = 0; + } + } + + if (update_state) { + if(seqno >= s->inseqno) + s->inseqno = seqno + 1; + + if(!s->inseqno) + s->received = 0; + else + s->received++; + } + + return true; +} + // 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"); - // TODO: just decrypt without updating the replay window + uint32_t seqno; + memcpy(&seqno, data, 4); + seqno = ntohl(seqno); + if (!sptps_check_seqno(s, seqno, false)) + return false; - return true; + char buffer[len]; + size_t outlen; + return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen); } // Receive incoming data, datagram version. @@ -412,45 +471,8 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen)) return error(s, EIO, "Failed to decrypt and verify packet"); - // Replay protection using a sliding window of configurable size. - // s->inseqno is expected sequence number - // seqno is received sequence number - // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet - // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno. - if(s->replaywin) { - if(seqno != s->inseqno) { - if(seqno >= s->inseqno + s->replaywin * 8) { - // Prevent packets that jump far ahead of the queue from causing many others to be dropped. - if(s->farfuture++ < s->replaywin >> 2) - return error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture); - - // Unless we have seen lots of them, in which case we consider the others lost. - warning(s, "Lost %d packets\n", seqno - s->inseqno); - // Mark all packets in the replay window as being late. - memset(s->late, 255, s->replaywin); - } 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); - } else { - // We missed some packets. Mark them in the bitmap as being late. - for(int i = s->inseqno; i < seqno; i++) - s->late[(i / 8) % s->replaywin] |= 1 << i % 8; - } - } - - // Mark the current packet as not being late. - s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8); - s->farfuture = 0; - } - - if(seqno >= s->inseqno) - s->inseqno = seqno + 1; - - if(!s->inseqno) - s->received = 0; - else - s->received++; + if(!sptps_check_seqno(s, seqno, true)) + return false; // Append a NULL byte for safety. buffer[len - 20] = 0; @@ -461,10 +483,10 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len 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(); + return false; } else if(type == SPTPS_HANDSHAKE) { if(!receive_handshake(s, buffer + 1, len - 21)) - abort(); + return false; } else { return error(s, EIO, "Invalid record type %d", type); } @@ -473,7 +495,7 @@ 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) { +bool sptps_receive_data(sptps_t *s, const void *data, size_t len) { if(!s->state) return error(s, EIO, "Invalid session state zero"); @@ -560,7 +582,7 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { } // 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);