X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=blobdiff_plain;f=src%2Fsptps.c;h=2a298ffa70ace8b9431ae38d729d7f536df33e3b;hp=422940c9df26d0bc864d6f880ca426c119059e80;hb=d917c8cb6b69475d568ccbe82389b9f2b3eb5e80;hpb=153abaa4d940bf2bc9bd7275d5efe5c01c354190 diff --git a/src/sptps.c b/src/sptps.c index 422940c9..2a298ffa 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -1,6 +1,7 @@ /* sptps.c -- Simple Peer-to-Peer Security - Copyright (C) 2011 Guus Sliepen , + Copyright (C) 2011-2012 Guus Sliepen , + 2010 Brandon L. Black This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,6 +28,8 @@ #include "prf.h" #include "sptps.h" +unsigned int sptps_replaywin = 16; + /* Nonce MUST be exchanged first (done) Signatures MUST be done over both nonces, to guarantee the signature is fresh @@ -206,7 +209,7 @@ static bool generate_key_material(sptps_t *s, const char *shared, size_t len) { memcpy(seed + 13, s->hiskex + 1, 32); memcpy(seed + 45, s->mykex + 1, 32); } - memcpy(seed + 78, s->label, s->labellen); + memcpy(seed + 77, s->label, s->labellen); // Use PRF to generate the key material if(!prf(shared, len, seed, s->labellen + 64 + 13, s->key, keylen)) @@ -336,7 +339,6 @@ bool sptps_force_kex(sptps_t *s) { // Receive a handshake record. static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) { // Only a few states to deal with handshaking. - fprintf(stderr, "Received handshake message, current state %d\n", s->state); switch(s->state) { case SPTPS_SECONDARY_KEX: // We receive a secondary KEX request, first respond by sending our own. @@ -376,6 +378,20 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) { } } +// Check datagram for valid HMAC +bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) { + if(!s->instate || len < 21) + return false; + + char buffer[len + 23]; + uint16_t netlen = htons(len - 21); + + memcpy(buffer, &netlen, 2); + memcpy(buffer + 2, data, len); + + return digest_verify(&s->indigest, buffer, len - 14, buffer + len - 14); +} + // Receive incoming data, datagram version. static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len) { if(len < (s->instate ? 21 : 5)) @@ -401,15 +417,42 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len return receive_handshake(s, data + 5, len - 5); } - if(seqno < s->inseqno) { - fprintf(stderr, "Received late or replayed packet: %d < %d\n", seqno, s->inseqno); - return true; + // 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) { + fprintf(stderr, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture); + return false; + } + // Unless we have seen lots of them, in which case we consider the others lost. + fprintf(stderr, "Lost %d packets\n", seqno - s->inseqno); + memset(s->late, 0, 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))) { + fprintf(stderr, "Received late or replayed packet, seqno %d, last received %d", seqno, s->inseqno); + return false; + } + } 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) - fprintf(stderr, "Missed %d packets\n", seqno - s->inseqno); - - s->inseqno = seqno + 1; + s->inseqno = seqno + 1; uint16_t netlen = htons(len - 21); @@ -447,6 +490,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len return true; } + // 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) { if(s->datagram) @@ -464,7 +508,7 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { s->buflen += toread; len -= toread; data += toread; - + // Exit early if we don't have the full length. if(s->buflen < 6) return true; @@ -550,6 +594,12 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_ s->datagram = datagram; s->mykey = mykey; s->hiskey = hiskey; + s->replaywin = sptps_replaywin; + if(s->replaywin) { + s->late = malloc(s->replaywin); + if(!s->late) + return error(s, errno, strerror(errno)); + } s->label = malloc(labellen); if(!s->label) @@ -577,16 +627,17 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_ // Stop a SPTPS session. bool sptps_stop(sptps_t *s) { // Clean up any resources. + cipher_close(&s->incipher); + cipher_close(&s->outcipher); + digest_close(&s->indigest); + digest_close(&s->outdigest); ecdh_free(&s->ecdh); free(s->inbuf); - s->inbuf = NULL; free(s->mykex); - s->mykex = NULL; free(s->hiskex); - s->hiskex = NULL; free(s->key); - s->key = NULL; free(s->label); - s->label = NULL; + free(s->late); + memset(s, 0, sizeof *s); return true; }