diff options
Diffstat (limited to 'openssl/ssl/d1_pkt.c')
-rw-r--r-- | openssl/ssl/d1_pkt.c | 1792 |
1 files changed, 1792 insertions, 0 deletions
diff --git a/openssl/ssl/d1_pkt.c b/openssl/ssl/d1_pkt.c new file mode 100644 index 000000000..eb56cf987 --- /dev/null +++ b/openssl/ssl/d1_pkt.c @@ -0,0 +1,1792 @@ +/* ssl/d1_pkt.c */ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include <stdio.h> +#include <errno.h> +#define USE_SOCKETS +#include "ssl_locl.h" +#include <openssl/evp.h> +#include <openssl/buffer.h> +#include <openssl/pqueue.h> +#include <openssl/rand.h> + +static int have_handshake_fragment(SSL *s, int type, unsigned char *buf, + int len, int peek); +static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap, + PQ_64BIT *seq_num); +static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap); +static DTLS1_BITMAP *dtls1_get_bitmap(SSL *s, SSL3_RECORD *rr, + unsigned int *is_next_epoch); +#if 0 +static int dtls1_record_needs_buffering(SSL *s, SSL3_RECORD *rr, + unsigned short *priority, unsigned long *offset); +#endif +static int dtls1_buffer_record(SSL *s, record_pqueue *q, + PQ_64BIT priority); +static int dtls1_process_record(SSL *s); +#if PQ_64BIT_IS_INTEGER +static PQ_64BIT bytes_to_long_long(unsigned char *bytes, PQ_64BIT *num); +#endif +static void dtls1_clear_timeouts(SSL *s); + +/* copy buffered record into SSL structure */ +static int +dtls1_copy_record(SSL *s, pitem *item) + { + DTLS1_RECORD_DATA *rdata; + + rdata = (DTLS1_RECORD_DATA *)item->data; + + if (s->s3->rbuf.buf != NULL) + OPENSSL_free(s->s3->rbuf.buf); + + s->packet = rdata->packet; + s->packet_length = rdata->packet_length; + memcpy(&(s->s3->rbuf), &(rdata->rbuf), sizeof(SSL3_BUFFER)); + memcpy(&(s->s3->rrec), &(rdata->rrec), sizeof(SSL3_RECORD)); + + return(1); + } + + +static int +dtls1_buffer_record(SSL *s, record_pqueue *queue, PQ_64BIT priority) +{ + DTLS1_RECORD_DATA *rdata; + pitem *item; + + rdata = OPENSSL_malloc(sizeof(DTLS1_RECORD_DATA)); + item = pitem_new(priority, rdata); + if (rdata == NULL || item == NULL) + { + if (rdata != NULL) OPENSSL_free(rdata); + if (item != NULL) pitem_free(item); + + SSLerr(SSL_F_DTLS1_BUFFER_RECORD, ERR_R_INTERNAL_ERROR); + return(0); + } + + rdata->packet = s->packet; + rdata->packet_length = s->packet_length; + memcpy(&(rdata->rbuf), &(s->s3->rbuf), sizeof(SSL3_BUFFER)); + memcpy(&(rdata->rrec), &(s->s3->rrec), sizeof(SSL3_RECORD)); + + item->data = rdata; + + /* insert should not fail, since duplicates are dropped */ + if (pqueue_insert(queue->q, item) == NULL) + { + OPENSSL_free(rdata); + pitem_free(item); + return(0); + } + + s->packet = NULL; + s->packet_length = 0; + memset(&(s->s3->rbuf), 0, sizeof(SSL3_BUFFER)); + memset(&(s->s3->rrec), 0, sizeof(SSL3_RECORD)); + + if (!ssl3_setup_buffers(s)) + { + SSLerr(SSL_F_DTLS1_BUFFER_RECORD, ERR_R_INTERNAL_ERROR); + OPENSSL_free(rdata); + pitem_free(item); + return(0); + } + + return(1); + } + + +static int +dtls1_retrieve_buffered_record(SSL *s, record_pqueue *queue) + { + pitem *item; + + item = pqueue_pop(queue->q); + if (item) + { + dtls1_copy_record(s, item); + + OPENSSL_free(item->data); + pitem_free(item); + + return(1); + } + + return(0); + } + + +/* retrieve a buffered record that belongs to the new epoch, i.e., not processed + * yet */ +#define dtls1_get_unprocessed_record(s) \ + dtls1_retrieve_buffered_record((s), \ + &((s)->d1->unprocessed_rcds)) + +/* retrieve a buffered record that belongs to the current epoch, ie, processed */ +#define dtls1_get_processed_record(s) \ + dtls1_retrieve_buffered_record((s), \ + &((s)->d1->processed_rcds)) + +static int +dtls1_process_buffered_records(SSL *s) + { + pitem *item; + + item = pqueue_peek(s->d1->unprocessed_rcds.q); + if (item) + { + DTLS1_RECORD_DATA *rdata; + rdata = (DTLS1_RECORD_DATA *)item->data; + + /* Check if epoch is current. */ + if (s->d1->unprocessed_rcds.epoch != s->d1->r_epoch) + return(1); /* Nothing to do. */ + + /* Process all the records. */ + while (pqueue_peek(s->d1->unprocessed_rcds.q)) + { + dtls1_get_unprocessed_record(s); + if ( ! dtls1_process_record(s)) + return(0); + dtls1_buffer_record(s, &(s->d1->processed_rcds), + s->s3->rrec.seq_num); + } + } + + /* sync epoch numbers once all the unprocessed records + * have been processed */ + s->d1->processed_rcds.epoch = s->d1->r_epoch; + s->d1->unprocessed_rcds.epoch = s->d1->r_epoch + 1; + + return(1); + } + + +#if 0 + +static int +dtls1_get_buffered_record(SSL *s) + { + pitem *item; + PQ_64BIT priority = + (((PQ_64BIT)s->d1->handshake_read_seq) << 32) | + ((PQ_64BIT)s->d1->r_msg_hdr.frag_off); + + if ( ! SSL_in_init(s)) /* if we're not (re)negotiating, + nothing buffered */ + return 0; + + + item = pqueue_peek(s->d1->rcvd_records); + if (item && item->priority == priority) + { + /* Check if we've received the record of interest. It must be + * a handshake record, since data records as passed up without + * buffering */ + DTLS1_RECORD_DATA *rdata; + item = pqueue_pop(s->d1->rcvd_records); + rdata = (DTLS1_RECORD_DATA *)item->data; + + if (s->s3->rbuf.buf != NULL) + OPENSSL_free(s->s3->rbuf.buf); + + s->packet = rdata->packet; + s->packet_length = rdata->packet_length; + memcpy(&(s->s3->rbuf), &(rdata->rbuf), sizeof(SSL3_BUFFER)); + memcpy(&(s->s3->rrec), &(rdata->rrec), sizeof(SSL3_RECORD)); + + OPENSSL_free(item->data); + pitem_free(item); + + /* s->d1->next_expected_seq_num++; */ + return(1); + } + + return 0; + } + +#endif + +static int +dtls1_process_record(SSL *s) +{ + int i,al; + int clear=0; + int enc_err; + SSL_SESSION *sess; + SSL3_RECORD *rr; + unsigned int mac_size; + unsigned char md[EVP_MAX_MD_SIZE]; + + + rr= &(s->s3->rrec); + sess = s->session; + + /* At this point, s->packet_length == SSL3_RT_HEADER_LNGTH + rr->length, + * and we have that many bytes in s->packet + */ + rr->input= &(s->packet[DTLS1_RT_HEADER_LENGTH]); + + /* ok, we can now read from 's->packet' data into 'rr' + * rr->input points at rr->length bytes, which + * need to be copied into rr->data by either + * the decryption or by the decompression + * When the data is 'copied' into the rr->data buffer, + * rr->input will be pointed at the new buffer */ + + /* We now have - encrypted [ MAC [ compressed [ plain ] ] ] + * rr->length bytes of encrypted compressed stuff. */ + + /* check is not needed I believe */ + if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) + { + al=SSL_AD_RECORD_OVERFLOW; + SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_ENCRYPTED_LENGTH_TOO_LONG); + goto f_err; + } + + /* decrypt in place in 'rr->input' */ + rr->data=rr->input; + + enc_err = s->method->ssl3_enc->enc(s,0); + if (enc_err <= 0) + { + if (enc_err == 0) + /* SSLerr() and ssl3_send_alert() have been called */ + goto err; + + /* otherwise enc_err == -1 */ + goto decryption_failed_or_bad_record_mac; + } + +#ifdef TLS_DEBUG +printf("dec %d\n",rr->length); +{ unsigned int z; for (z=0; z<rr->length; z++) printf("%02X%c",rr->data[z],((z+1)%16)?' ':'\n'); } +printf("\n"); +#endif + + /* r->length is now the compressed data plus mac */ +if ( (sess == NULL) || + (s->enc_read_ctx == NULL) || + (s->read_hash == NULL)) + clear=1; + + if (!clear) + { + mac_size=EVP_MD_size(s->read_hash); + + if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+mac_size) + { +#if 0 /* OK only for stream ciphers (then rr->length is visible from ciphertext anyway) */ + al=SSL_AD_RECORD_OVERFLOW; + SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_PRE_MAC_LENGTH_TOO_LONG); + goto f_err; +#else + goto decryption_failed_or_bad_record_mac; +#endif + } + /* check the MAC for rr->input (it's in mac_size bytes at the tail) */ + if (rr->length < mac_size) + { +#if 0 /* OK only for stream ciphers */ + al=SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_LENGTH_TOO_SHORT); + goto f_err; +#else + goto decryption_failed_or_bad_record_mac; +#endif + } + rr->length-=mac_size; + i=s->method->ssl3_enc->mac(s,md,0); + if (memcmp(md,&(rr->data[rr->length]),mac_size) != 0) + { + goto decryption_failed_or_bad_record_mac; + } + } + + /* r->length is now just compressed */ + if (s->expand != NULL) + { + if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH) + { + al=SSL_AD_RECORD_OVERFLOW; + SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_COMPRESSED_LENGTH_TOO_LONG); + goto f_err; + } + if (!ssl3_do_uncompress(s)) + { + al=SSL_AD_DECOMPRESSION_FAILURE; + SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_BAD_DECOMPRESSION); + goto f_err; + } + } + + if (rr->length > SSL3_RT_MAX_PLAIN_LENGTH) + { + al=SSL_AD_RECORD_OVERFLOW; + SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_DATA_LENGTH_TOO_LONG); + goto f_err; + } + + rr->off=0; + /* So at this point the following is true + * ssl->s3->rrec.type is the type of record + * ssl->s3->rrec.length == number of bytes in record + * ssl->s3->rrec.off == offset to first valid byte + * ssl->s3->rrec.data == where to take bytes from, increment + * after use :-). + */ + + /* we have pulled in a full packet so zero things */ + s->packet_length=0; + dtls1_record_bitmap_update(s, &(s->d1->bitmap));/* Mark receipt of record. */ + return(1); + +decryption_failed_or_bad_record_mac: + /* Separate 'decryption_failed' alert was introduced with TLS 1.0, + * SSL 3.0 only has 'bad_record_mac'. But unless a decryption + * failure is directly visible from the ciphertext anyway, + * we should not reveal which kind of error occured -- this + * might become visible to an attacker (e.g. via logfile) */ + al=SSL_AD_BAD_RECORD_MAC; + SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); +f_err: + ssl3_send_alert(s,SSL3_AL_FATAL,al); +err: + return(0); +} + + +/* Call this to get a new input record. + * It will return <= 0 if more data is needed, normally due to an error + * or non-blocking IO. + * When it finishes, one packet has been decoded and can be found in + * ssl->s3->rrec.type - is the type of record + * ssl->s3->rrec.data, - data + * ssl->s3->rrec.length, - number of bytes + */ +/* used only by dtls1_read_bytes */ +int dtls1_get_record(SSL *s) + { + int ssl_major,ssl_minor,al; + int i,n; + SSL3_RECORD *rr; + SSL_SESSION *sess; + unsigned char *p; + unsigned short version; + DTLS1_BITMAP *bitmap; + unsigned int is_next_epoch; + + rr= &(s->s3->rrec); + sess=s->session; + + /* The epoch may have changed. If so, process all the + * pending records. This is a non-blocking operation. */ + if ( ! dtls1_process_buffered_records(s)) + return 0; + + /* if we're renegotiating, then there may be buffered records */ + if (dtls1_get_processed_record(s)) + return 1; + + /* get something from the wire */ +again: + /* check if we have the header */ + if ( (s->rstate != SSL_ST_READ_BODY) || + (s->packet_length < DTLS1_RT_HEADER_LENGTH)) + { + n=ssl3_read_n(s, DTLS1_RT_HEADER_LENGTH, s->s3->rbuf.len, 0); + /* read timeout is handled by dtls1_read_bytes */ + if (n <= 0) return(n); /* error or non-blocking */ + + OPENSSL_assert(s->packet_length == DTLS1_RT_HEADER_LENGTH); + + s->rstate=SSL_ST_READ_BODY; + + p=s->packet; + + /* Pull apart the header into the DTLS1_RECORD */ + rr->type= *(p++); + ssl_major= *(p++); + ssl_minor= *(p++); + version=(ssl_major<<8)|ssl_minor; + + /* sequence number is 64 bits, with top 2 bytes = epoch */ + n2s(p,rr->epoch); + + memcpy(&(s->s3->read_sequence[2]), p, 6); + p+=6; + + n2s(p,rr->length); + + /* Lets check version */ + if (!s->first_packet) + { + if (version != s->version && version != DTLS1_BAD_VER) + { + SSLerr(SSL_F_DTLS1_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER); + /* Send back error using their + * version number :-) */ + s->version=version; + al=SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + } + + if ((version & 0xff00) != (DTLS1_VERSION & 0xff00) && + (version & 0xff00) != (DTLS1_BAD_VER & 0xff00)) + { + SSLerr(SSL_F_DTLS1_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER); + goto err; + } + + if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) + { + al=SSL_AD_RECORD_OVERFLOW; + SSLerr(SSL_F_DTLS1_GET_RECORD,SSL_R_PACKET_LENGTH_TOO_LONG); + goto f_err; + } + + s->client_version = version; + /* now s->rstate == SSL_ST_READ_BODY */ + } + + /* s->rstate == SSL_ST_READ_BODY, get and decode the data */ + + if (rr->length > s->packet_length-DTLS1_RT_HEADER_LENGTH) + { + /* now s->packet_length == DTLS1_RT_HEADER_LENGTH */ + i=rr->length; + n=ssl3_read_n(s,i,i,1); + if (n <= 0) return(n); /* error or non-blocking io */ + + /* this packet contained a partial record, dump it */ + if ( n != i) + { + s->packet_length = 0; + goto again; + } + + /* now n == rr->length, + * and s->packet_length == DTLS1_RT_HEADER_LENGTH + rr->length */ + } + s->rstate=SSL_ST_READ_HEADER; /* set state for later operations */ + + /* match epochs. NULL means the packet is dropped on the floor */ + bitmap = dtls1_get_bitmap(s, rr, &is_next_epoch); + if ( bitmap == NULL) + { + s->packet_length = 0; /* dump this record */ + goto again; /* get another record */ + } + + /* check whether this is a repeat, or aged record */ + if ( ! dtls1_record_replay_check(s, bitmap, &(rr->seq_num))) + { + rr->length = 0; + s->packet_length=0; /* dump this record */ + goto again; /* get another record */ + } + + /* just read a 0 length packet */ + if (rr->length == 0) goto again; + + /* If this record is from the next epoch (either HM or ALERT), buffer it + * since it cannot be processed at this time. + * Records from the next epoch are marked as received even though they are + * not processed, so as to prevent any potential resource DoS attack */ + if (is_next_epoch) + { + dtls1_record_bitmap_update(s, bitmap); + dtls1_buffer_record(s, &(s->d1->unprocessed_rcds), rr->seq_num); + s->packet_length = 0; + goto again; + } + + if ( ! dtls1_process_record(s)) + return(0); + + dtls1_clear_timeouts(s); /* done waiting */ + return(1); + +f_err: + ssl3_send_alert(s,SSL3_AL_FATAL,al); +err: + return(0); + } + +/* Return up to 'len' payload bytes received in 'type' records. + * 'type' is one of the following: + * + * - SSL3_RT_HANDSHAKE (when ssl3_get_message calls us) + * - SSL3_RT_APPLICATION_DATA (when ssl3_read calls us) + * - 0 (during a shutdown, no data has to be returned) + * + * If we don't have stored data to work from, read a SSL/TLS record first + * (possibly multiple records if we still don't have anything to return). + * + * This function must handle any surprises the peer may have for us, such as + * Alert records (e.g. close_notify), ChangeCipherSpec records (not really + * a surprise, but handled as if it were), or renegotiation requests. + * Also if record payloads contain fragments too small to process, we store + * them until there is enough for the respective protocol (the record protocol + * may use arbitrary fragmentation and even interleaving): + * Change cipher spec protocol + * just 1 byte needed, no need for keeping anything stored + * Alert protocol + * 2 bytes needed (AlertLevel, AlertDescription) + * Handshake protocol + * 4 bytes needed (HandshakeType, uint24 length) -- we just have + * to detect unexpected Client Hello and Hello Request messages + * here, anything else is handled by higher layers + * Application data protocol + * none of our business + */ +int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) + { + int al,i,j,ret; + unsigned int n; + SSL3_RECORD *rr; + void (*cb)(const SSL *ssl,int type2,int val)=NULL; + + if (s->s3->rbuf.buf == NULL) /* Not initialized yet */ + if (!ssl3_setup_buffers(s)) + return(-1); + + /* XXX: check what the second '&& type' is about */ + if ((type && (type != SSL3_RT_APPLICATION_DATA) && + (type != SSL3_RT_HANDSHAKE) && type) || + (peek && (type != SSL3_RT_APPLICATION_DATA))) + { + SSLerr(SSL_F_DTLS1_READ_BYTES, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* check whether there's a handshake message (client hello?) waiting */ + if ( (ret = have_handshake_fragment(s, type, buf, len, peek))) + return ret; + + /* Now s->d1->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */ + + if (!s->in_handshake && SSL_in_init(s)) + { + /* type == SSL3_RT_APPLICATION_DATA */ + i=s->handshake_func(s); + if (i < 0) return(i); + if (i == 0) + { + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE); + return(-1); + } + } + +start: + s->rwstate=SSL_NOTHING; + + /* s->s3->rrec.type - is the type of record + * s->s3->rrec.data, - data + * s->s3->rrec.off, - offset into 'data' for next read + * s->s3->rrec.length, - number of bytes. */ + rr = &(s->s3->rrec); + + /* get new packet if necessary */ + if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY)) + { + ret=dtls1_get_record(s); + if (ret <= 0) + { + ret = dtls1_read_failed(s, ret); + /* anything other than a timeout is an error */ + if (ret <= 0) + return(ret); + else + goto start; + } + } + + /* we now have a packet which can be read and processed */ + + if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec, + * reset by ssl3_get_finished */ + && (rr->type != SSL3_RT_HANDSHAKE)) + { + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_DATA_BETWEEN_CCS_AND_FINISHED); + goto err; + } + + /* If the other end has shut down, throw anything we read away + * (even in 'peek' mode) */ + if (s->shutdown & SSL_RECEIVED_SHUTDOWN) + { + rr->length=0; + s->rwstate=SSL_NOTHING; + return(0); + } + + + if (type == rr->type) /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */ + { + /* make sure that we are not getting application data when we + * are doing a handshake for the first time */ + if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) && + (s->enc_read_ctx == NULL)) + { + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_APP_DATA_IN_HANDSHAKE); + goto f_err; + } + + if (len <= 0) return(len); + + if ((unsigned int)len > rr->length) + n = rr->length; + else + n = (unsigned int)len; + + memcpy(buf,&(rr->data[rr->off]),n); + if (!peek) + { + rr->length-=n; + rr->off+=n; + if (rr->length == 0) + { + s->rstate=SSL_ST_READ_HEADER; + rr->off=0; + } + } + return(n); + } + + + /* If we get here, then type != rr->type; if we have a handshake + * message, then it was unexpected (Hello Request or Client Hello). */ + + /* In case of record types for which we have 'fragment' storage, + * fill that so that we can process the data at a fixed place. + */ + { + unsigned int k, dest_maxlen = 0; + unsigned char *dest = NULL; + unsigned int *dest_len = NULL; + + if (rr->type == SSL3_RT_HANDSHAKE) + { + dest_maxlen = sizeof s->d1->handshake_fragment; + dest = s->d1->handshake_fragment; + dest_len = &s->d1->handshake_fragment_len; + } + else if (rr->type == SSL3_RT_ALERT) + { + dest_maxlen = sizeof(s->d1->alert_fragment); + dest = s->d1->alert_fragment; + dest_len = &s->d1->alert_fragment_len; + } + /* else it's a CCS message, or it's wrong */ + else if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) + { + /* Not certain if this is the right error handling */ + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_UNEXPECTED_RECORD); + goto f_err; + } + + + if (dest_maxlen > 0) + { + /* XDTLS: In a pathalogical case, the Client Hello + * may be fragmented--don't always expect dest_maxlen bytes */ + if ( rr->length < dest_maxlen) + { +#ifdef DTLS1_AD_MISSING_HANDSHAKE_MESSAGE + /* + * for normal alerts rr->length is 2, while + * dest_maxlen is 7 if we were to handle this + * non-existing alert... + */ + FIX ME +#endif + s->rstate=SSL_ST_READ_HEADER; + rr->length = 0; + goto start; + } + + /* now move 'n' bytes: */ + for ( k = 0; k < dest_maxlen; k++) + { + dest[k] = rr->data[rr->off++]; + rr->length--; + } + *dest_len = dest_maxlen; + } + } + + /* s->d1->handshake_fragment_len == 12 iff rr->type == SSL3_RT_HANDSHAKE; + * s->d1->alert_fragment_len == 7 iff rr->type == SSL3_RT_ALERT. + * (Possibly rr is 'empty' now, i.e. rr->length may be 0.) */ + + /* If we are a client, check for an incoming 'Hello Request': */ + if ((!s->server) && + (s->d1->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH) && + (s->d1->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) && + (s->session != NULL) && (s->session->cipher != NULL)) + { + s->d1->handshake_fragment_len = 0; + + if ((s->d1->handshake_fragment[1] != 0) || + (s->d1->handshake_fragment[2] != 0) || + (s->d1->handshake_fragment[3] != 0)) + { + al=SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_BAD_HELLO_REQUEST); + goto err; + } + + /* no need to check sequence number on HELLO REQUEST messages */ + + if (s->msg_callback) + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, + s->d1->handshake_fragment, 4, s, s->msg_callback_arg); + + if (SSL_is_init_finished(s) && + !(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) && + !s->s3->renegotiate) + { + ssl3_renegotiate(s); + if (ssl3_renegotiate_check(s)) + { + i=s->handshake_func(s); + if (i < 0) return(i); + if (i == 0) + { + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE); + return(-1); + } + + if (!(s->mode & SSL_MODE_AUTO_RETRY)) + { + if (s->s3->rbuf.left == 0) /* no read-ahead left? */ + { + BIO *bio; + /* In the case where we try to read application data, + * but we trigger an SSL handshake, we return -1 with + * the retry option set. Otherwise renegotiation may + * cause nasty problems in the blocking world */ + s->rwstate=SSL_READING; + bio=SSL_get_rbio(s); + BIO_clear_retry_flags(bio); + BIO_set_retry_read(bio); + return(-1); + } + } + } + } + /* we either finished a handshake or ignored the request, + * now try again to obtain the (application) data we were asked for */ + goto start; + } + + if (s->d1->alert_fragment_len >= DTLS1_AL_HEADER_LENGTH) + { + int alert_level = s->d1->alert_fragment[0]; + int alert_descr = s->d1->alert_fragment[1]; + + s->d1->alert_fragment_len = 0; + + if (s->msg_callback) + s->msg_callback(0, s->version, SSL3_RT_ALERT, + s->d1->alert_fragment, 2, s, s->msg_callback_arg); + + if (s->info_callback != NULL) + cb=s->info_callback; + else if (s->ctx->info_callback != NULL) + cb=s->ctx->info_callback; + + if (cb != NULL) + { + j = (alert_level << 8) | alert_descr; + cb(s, SSL_CB_READ_ALERT, j); + } + + if (alert_level == 1) /* warning */ + { + s->s3->warn_alert = alert_descr; + if (alert_descr == SSL_AD_CLOSE_NOTIFY) + { + s->shutdown |= SSL_RECEIVED_SHUTDOWN; + return(0); + } +#if 0 + /* XXX: this is a possible improvement in the future */ + /* now check if it's a missing record */ + if (alert_descr == DTLS1_AD_MISSING_HANDSHAKE_MESSAGE) + { + unsigned short seq; + unsigned int frag_off; + unsigned char *p = &(s->d1->alert_fragment[2]); + + n2s(p, seq); + n2l3(p, frag_off); + + dtls1_retransmit_message(s, seq, frag_off, &found); + if ( ! found && SSL_in_init(s)) + { + /* fprintf( stderr,"in init = %d\n", SSL_in_init(s)); */ + /* requested a message not yet sent, + send an alert ourselves */ + ssl3_send_alert(s,SSL3_AL_WARNING, + DTLS1_AD_MISSING_HANDSHAKE_MESSAGE); + } + } +#endif + } + else if (alert_level == 2) /* fatal */ + { + char tmp[16]; + + s->rwstate=SSL_NOTHING; + s->s3->fatal_alert = alert_descr; + SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_AD_REASON_OFFSET + alert_descr); + BIO_snprintf(tmp,sizeof tmp,"%d",alert_descr); + ERR_add_error_data(2,"SSL alert number ",tmp); + s->shutdown|=SSL_RECEIVED_SHUTDOWN; + SSL_CTX_remove_session(s->ctx,s->session); + return(0); + } + else + { + al=SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_UNKNOWN_ALERT_TYPE); + goto f_err; + } + + goto start; + } + + if (s->shutdown & SSL_SENT_SHUTDOWN) /* but we have not received a shutdown */ + { + s->rwstate=SSL_NOTHING; + rr->length=0; + return(0); + } + + if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) + { + struct ccs_header_st ccs_hdr; + + dtls1_get_ccs_header(rr->data, &ccs_hdr); + + /* 'Change Cipher Spec' is just a single byte, so we know + * exactly what the record payload has to look like */ + /* XDTLS: check that epoch is consistent */ + if ( (s->client_version == DTLS1_BAD_VER && rr->length != 3) || + (s->client_version != DTLS1_BAD_VER && rr->length != DTLS1_CCS_HEADER_LENGTH) || + (rr->off != 0) || (rr->data[0] != SSL3_MT_CCS)) + { + i=SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_BAD_CHANGE_CIPHER_SPEC); + goto err; + } + + rr->length=0; + + if (s->msg_callback) + s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, + rr->data, 1, s, s->msg_callback_arg); + + s->s3->change_cipher_spec=1; + if (!ssl3_do_change_cipher_spec(s)) + goto err; + + /* do this whenever CCS is processed */ + dtls1_reset_seq_numbers(s, SSL3_CC_READ); + + if (s->client_version == DTLS1_BAD_VER) + s->d1->handshake_read_seq++; + + goto start; + } + + /* Unexpected handshake message (Client Hello, or protocol violation) */ + if ((s->d1->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH) && + !s->in_handshake) + { + struct hm_header_st msg_hdr; + + /* this may just be a stale retransmit */ + dtls1_get_message_header(rr->data, &msg_hdr); + if( rr->epoch != s->d1->r_epoch) + { + rr->length = 0; + goto start; + } + + if (((s->state&SSL_ST_MASK) == SSL_ST_OK) && + !(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)) + { +#if 0 /* worked only because C operator preferences are not as expected (and + * because this is not really needed for clients except for detecting + * protocol violations): */ + s->state=SSL_ST_BEFORE|(s->server) + ?SSL_ST_ACCEPT + :SSL_ST_CONNECT; +#else + s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT; +#endif + s->new_session=1; + } + i=s->handshake_func(s); + if (i < 0) return(i); + if (i == 0) + { + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE); + return(-1); + } + + if (!(s->mode & SSL_MODE_AUTO_RETRY)) + { + if (s->s3->rbuf.left == 0) /* no read-ahead left? */ + { + BIO *bio; + /* In the case where we try to read application data, + * but we trigger an SSL handshake, we return -1 with + * the retry option set. Otherwise renegotiation may + * cause nasty problems in the blocking world */ + s->rwstate=SSL_READING; + bio=SSL_get_rbio(s); + BIO_clear_retry_flags(bio); + BIO_set_retry_read(bio); + return(-1); + } + } + goto start; + } + + switch (rr->type) + { + default: +#ifndef OPENSSL_NO_TLS + /* TLS just ignores unknown message types */ + if (s->version == TLS1_VERSION) + { + rr->length = 0; + goto start; + } +#endif + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_UNEXPECTED_RECORD); + goto f_err; + case SSL3_RT_CHANGE_CIPHER_SPEC: + case SSL3_RT_ALERT: + case SSL3_RT_HANDSHAKE: + /* we already handled all of these, with the possible exception + * of SSL3_RT_HANDSHAKE when s->in_handshake is set, but that + * should not happen when type != rr->type */ + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_DTLS1_READ_BYTES,ERR_R_INTERNAL_ERROR); + goto f_err; + case SSL3_RT_APPLICATION_DATA: + /* At this point, we were expecting handshake data, + * but have application data. If the library was + * running inside ssl3_read() (i.e. in_read_app_data + * is set) and it makes sense to read application data + * at this point (session renegotiation not yet started), + * we will indulge it. + */ + if (s->s3->in_read_app_data && + (s->s3->total_renegotiations != 0) && + (( + (s->state & SSL_ST_CONNECT) && + (s->state >= SSL3_ST_CW_CLNT_HELLO_A) && + (s->state <= SSL3_ST_CR_SRVR_HELLO_A) + ) || ( + (s->state & SSL_ST_ACCEPT) && + (s->state <= SSL3_ST_SW_HELLO_REQ_A) && + (s->state >= SSL3_ST_SR_CLNT_HELLO_A) + ) + )) + { + s->s3->in_read_app_data=2; + return(-1); + } + else + { + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_UNEXPECTED_RECORD); + goto f_err; + } + } + /* not reached */ + +f_err: + ssl3_send_alert(s,SSL3_AL_FATAL,al); +err: + return(-1); + } + +int +dtls1_write_app_data_bytes(SSL *s, int type, const void *buf_, int len) + { + unsigned int n,tot; + int i; + + if (SSL_in_init(s) && !s->in_handshake) + { + i=s->handshake_func(s); + if (i < 0) return(i); + if (i == 0) + { + SSLerr(SSL_F_DTLS1_WRITE_APP_DATA_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + + tot = s->s3->wnum; + n = len - tot; + + while( n) + { + /* dtls1_write_bytes sends one record at a time, sized according to + * the currently known MTU */ + i = dtls1_write_bytes(s, type, buf_, len); + if (i <= 0) return i; + + if ((i == (int)n) || + (type == SSL3_RT_APPLICATION_DATA && + (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) + { + /* next chunk of data should get another prepended empty fragment + * in ciphersuites with known-IV weakness: */ + s->s3->empty_fragment_done = 0; + return tot+i; + } + + tot += i; + n-=i; + } + + return tot; + } + + + /* this only happens when a client hello is received and a handshake + * is started. */ +static int +have_handshake_fragment(SSL *s, int type, unsigned char *buf, + int len, int peek) + { + + if ((type == SSL3_RT_HANDSHAKE) && (s->d1->handshake_fragment_len > 0)) + /* (partially) satisfy request from storage */ + { + unsigned char *src = s->d1->handshake_fragment; + unsigned char *dst = buf; + unsigned int k,n; + + /* peek == 0 */ + n = 0; + while ((len > 0) && (s->d1->handshake_fragment_len > 0)) + { + *dst++ = *src++; + len--; s->d1->handshake_fragment_len--; + n++; + } + /* move any remaining fragment bytes: */ + for (k = 0; k < s->d1->handshake_fragment_len; k++) + s->d1->handshake_fragment[k] = *src++; + return n; + } + + return 0; + } + + + + +/* Call this to write data in records of type 'type' + * It will return <= 0 if not all data has been sent or non-blocking IO. + */ +int dtls1_write_bytes(SSL *s, int type, const void *buf_, int len) + { + const unsigned char *buf=buf_; + unsigned int tot,n,nw; + int i; + unsigned int mtu; + + s->rwstate=SSL_NOTHING; + tot=s->s3->wnum; + + n=(len-tot); + + /* handshake layer figures out MTU for itself, but data records + * are also sent through this interface, so need to figure out MTU */ +#if 0 + mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_MTU, 0, NULL); + mtu += DTLS1_HM_HEADER_LENGTH; /* HM already inserted */ +#endif + mtu = s->d1->mtu; + + if (mtu > SSL3_RT_MAX_PLAIN_LENGTH) + mtu = SSL3_RT_MAX_PLAIN_LENGTH; + + if (n > mtu) + nw=mtu; + else + nw=n; + + i=do_dtls1_write(s, type, &(buf[tot]), nw, 0); + if (i <= 0) + { + s->s3->wnum=tot; + return i; + } + + if ( (int)s->s3->wnum + i == len) + s->s3->wnum = 0; + else + s->s3->wnum += i; + + return i; + } + +int do_dtls1_write(SSL *s, int type, const unsigned char *buf, unsigned int len, int create_empty_fragment) + { + unsigned char *p,*pseq; + int i,mac_size,clear=0; + int prefix_len = 0; + SSL3_RECORD *wr; + SSL3_BUFFER *wb; + SSL_SESSION *sess; + int bs; + + /* first check if there is a SSL3_BUFFER still being written + * out. This will happen with non blocking IO */ + if (s->s3->wbuf.left != 0) + { + OPENSSL_assert(0); /* XDTLS: want to see if we ever get here */ + return(ssl3_write_pending(s,type,buf,len)); + } + + /* If we have an alert to send, lets send it */ + if (s->s3->alert_dispatch) + { + i=s->method->ssl_dispatch_alert(s); + if (i <= 0) + return(i); + /* if it went, fall through and send more stuff */ + } + + if (len == 0 && !create_empty_fragment) + return 0; + + wr= &(s->s3->wrec); + wb= &(s->s3->wbuf); + sess=s->session; + + if ( (sess == NULL) || + (s->enc_write_ctx == NULL) || + (s->write_hash == NULL)) + clear=1; + + if (clear) + mac_size=0; + else + mac_size=EVP_MD_size(s->write_hash); + + /* DTLS implements explicit IV, so no need for empty fragments */ +#if 0 + /* 'create_empty_fragment' is true only when this function calls itself */ + if (!clear && !create_empty_fragment && !s->s3->empty_fragment_done + && SSL_version(s) != DTLS1_VERSION) + { + /* countermeasure against known-IV weakness in CBC ciphersuites + * (see http://www.openssl.org/~bodo/tls-cbc.txt) + */ + + if (s->s3->need_empty_fragments && type == SSL3_RT_APPLICATION_DATA) + { + /* recursive function call with 'create_empty_fragment' set; + * this prepares and buffers the data for an empty fragment + * (these 'prefix_len' bytes are sent out later + * together with the actual payload) */ + prefix_len = s->method->do_ssl_write(s, type, buf, 0, 1); + if (prefix_len <= 0) + goto err; + + if (s->s3->wbuf.len < (size_t)prefix_len + SSL3_RT_MAX_PACKET_SIZE) + { + /* insufficient space */ + SSLerr(SSL_F_DO_DTLS1_WRITE, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + s->s3->empty_fragment_done = 1; + } +#endif + + p = wb->buf + prefix_len; + + /* write the header */ + + *(p++)=type&0xff; + wr->type=type; + + if (s->client_version == DTLS1_BAD_VER) + *(p++) = DTLS1_BAD_VER>>8, + *(p++) = DTLS1_BAD_VER&0xff; + else + *(p++)=(s->version>>8), + *(p++)=s->version&0xff; + + /* field where we are to write out packet epoch, seq num and len */ + pseq=p; + p+=10; + + /* lets setup the record stuff. */ + + /* Make space for the explicit IV in case of CBC. + * (this is a bit of a boundary violation, but what the heck). + */ + if ( s->enc_write_ctx && + (EVP_CIPHER_mode( s->enc_write_ctx->cipher ) & EVP_CIPH_CBC_MODE)) + bs = EVP_CIPHER_block_size(s->enc_write_ctx->cipher); + else + bs = 0; + + wr->data=p + bs; /* make room for IV in case of CBC */ + wr->length=(int)len; + wr->input=(unsigned char *)buf; + + /* we now 'read' from wr->input, wr->length bytes into + * wr->data */ + + /* first we compress */ + if (s->compress != NULL) + { + if (!ssl3_do_compress(s)) + { + SSLerr(SSL_F_DO_DTLS1_WRITE,SSL_R_COMPRESSION_FAILURE); + goto err; + } + } + else + { + memcpy(wr->data,wr->input,wr->length); + wr->input=wr->data; + } + + /* we should still have the output to wr->data and the input + * from wr->input. Length should be wr->length. + * wr->data still points in the wb->buf */ + + if (mac_size != 0) + { + s->method->ssl3_enc->mac(s,&(p[wr->length + bs]),1); + wr->length+=mac_size; + } + + /* this is true regardless of mac size */ + wr->input=p; + wr->data=p; + + + /* ssl3_enc can only have an error on read */ + if (bs) /* bs != 0 in case of CBC */ + { + RAND_pseudo_bytes(p,bs); + /* master IV and last CBC residue stand for + * the rest of randomness */ + wr->length += bs; + } + + s->method->ssl3_enc->enc(s,1); + + /* record length after mac and block padding */ +/* if (type == SSL3_RT_APPLICATION_DATA || + (type == SSL3_RT_ALERT && ! SSL_in_init(s))) */ + + /* there's only one epoch between handshake and app data */ + + s2n(s->d1->w_epoch, pseq); + + /* XDTLS: ?? */ +/* else + s2n(s->d1->handshake_epoch, pseq); */ + + memcpy(pseq, &(s->s3->write_sequence[2]), 6); + pseq+=6; + s2n(wr->length,pseq); + + /* we should now have + * wr->data pointing to the encrypted data, which is + * wr->length long */ + wr->type=type; /* not needed but helps for debugging */ + wr->length+=DTLS1_RT_HEADER_LENGTH; + +#if 0 /* this is now done at the message layer */ + /* buffer the record, making it easy to handle retransmits */ + if ( type == SSL3_RT_HANDSHAKE || type == SSL3_RT_CHANGE_CIPHER_SPEC) + dtls1_buffer_record(s, wr->data, wr->length, + *((PQ_64BIT *)&(s->s3->write_sequence[0]))); +#endif + + ssl3_record_sequence_update(&(s->s3->write_sequence[0])); + + if (create_empty_fragment) + { + /* we are in a recursive call; + * just return the length, don't write out anything here + */ + return wr->length; + } + + /* now let's set up wb */ + wb->left = prefix_len + wr->length; + wb->offset = 0; + + /* memorize arguments so that ssl3_write_pending can detect bad write retries later */ + s->s3->wpend_tot=len; + s->s3->wpend_buf=buf; + s->s3->wpend_type=type; + s->s3->wpend_ret=len; + + /* we now just need to write the buffer */ + return ssl3_write_pending(s,type,buf,len); +err: + return -1; + } + + + +static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap, + PQ_64BIT *seq_num) + { +#if PQ_64BIT_IS_INTEGER + PQ_64BIT mask = 0x0000000000000001L; +#endif + PQ_64BIT rcd_num, tmp; + + pq_64bit_init(&rcd_num); + pq_64bit_init(&tmp); + + /* this is the sequence number for the record just read */ + pq_64bit_bin2num(&rcd_num, s->s3->read_sequence, 8); + + + if (pq_64bit_gt(&rcd_num, &(bitmap->max_seq_num)) || + pq_64bit_eq(&rcd_num, &(bitmap->max_seq_num))) + { + pq_64bit_assign(seq_num, &rcd_num); + pq_64bit_free(&rcd_num); + pq_64bit_free(&tmp); + return 1; /* this record is new */ + } + + pq_64bit_sub(&tmp, &(bitmap->max_seq_num), &rcd_num); + + if ( pq_64bit_get_word(&tmp) > bitmap->length) + { + pq_64bit_free(&rcd_num); + pq_64bit_free(&tmp); + return 0; /* stale, outside the window */ + } + +#if PQ_64BIT_IS_BIGNUM + { + int offset; + pq_64bit_sub(&tmp, &(bitmap->max_seq_num), &rcd_num); + pq_64bit_sub_word(&tmp, 1); + offset = pq_64bit_get_word(&tmp); + if ( pq_64bit_is_bit_set(&(bitmap->map), offset)) + { + pq_64bit_free(&rcd_num); + pq_64bit_free(&tmp); + return 0; + } + } +#else + mask <<= (bitmap->max_seq_num - rcd_num - 1); + if (bitmap->map & mask) + return 0; /* record previously received */ +#endif + + pq_64bit_assign(seq_num, &rcd_num); + pq_64bit_free(&rcd_num); + pq_64bit_free(&tmp); + return 1; + } + + +static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap) + { + unsigned int shift; + PQ_64BIT rcd_num; + PQ_64BIT tmp; + PQ_64BIT_CTX *ctx; + + pq_64bit_init(&rcd_num); + pq_64bit_init(&tmp); + + pq_64bit_bin2num(&rcd_num, s->s3->read_sequence, 8); + + /* unfortunate code complexity due to 64-bit manipulation support + * on 32-bit machines */ + if ( pq_64bit_gt(&rcd_num, &(bitmap->max_seq_num)) || + pq_64bit_eq(&rcd_num, &(bitmap->max_seq_num))) + { + pq_64bit_sub(&tmp, &rcd_num, &(bitmap->max_seq_num)); + pq_64bit_add_word(&tmp, 1); + + shift = (unsigned int)pq_64bit_get_word(&tmp); + + pq_64bit_lshift(&(tmp), &(bitmap->map), shift); + pq_64bit_assign(&(bitmap->map), &tmp); + + pq_64bit_set_bit(&(bitmap->map), 0); + pq_64bit_add_word(&rcd_num, 1); + pq_64bit_assign(&(bitmap->max_seq_num), &rcd_num); + + pq_64bit_assign_word(&tmp, 1); + pq_64bit_lshift(&tmp, &tmp, bitmap->length); + ctx = pq_64bit_ctx_new(&ctx); + pq_64bit_mod(&(bitmap->map), &(bitmap->map), &tmp, ctx); + pq_64bit_ctx_free(ctx); + } + else + { + pq_64bit_sub(&tmp, &(bitmap->max_seq_num), &rcd_num); + pq_64bit_sub_word(&tmp, 1); + shift = (unsigned int)pq_64bit_get_word(&tmp); + + pq_64bit_set_bit(&(bitmap->map), shift); + } + + pq_64bit_free(&rcd_num); + pq_64bit_free(&tmp); + } + + +int dtls1_dispatch_alert(SSL *s) + { + int i,j; + void (*cb)(const SSL *ssl,int type,int val)=NULL; + unsigned char buf[DTLS1_AL_HEADER_LENGTH]; + unsigned char *ptr = &buf[0]; + + s->s3->alert_dispatch=0; + + memset(buf, 0x00, sizeof(buf)); + *ptr++ = s->s3->send_alert[0]; + *ptr++ = s->s3->send_alert[1]; + +#ifdef DTLS1_AD_MISSING_HANDSHAKE_MESSAGE + if (s->s3->send_alert[1] == DTLS1_AD_MISSING_HANDSHAKE_MESSAGE) + { + s2n(s->d1->handshake_read_seq, ptr); +#if 0 + if ( s->d1->r_msg_hdr.frag_off == 0) /* waiting for a new msg */ + + else + s2n(s->d1->r_msg_hdr.seq, ptr); /* partial msg read */ +#endif + +#if 0 + fprintf(stderr, "s->d1->handshake_read_seq = %d, s->d1->r_msg_hdr.seq = %d\n",s->d1->handshake_read_seq,s->d1->r_msg_hdr.seq); +#endif + l2n3(s->d1->r_msg_hdr.frag_off, ptr); + } +#endif + + i = do_dtls1_write(s, SSL3_RT_ALERT, &buf[0], sizeof(buf), 0); + if (i <= 0) + { + s->s3->alert_dispatch=1; + /* fprintf( stderr, "not done with alert\n" ); */ + } + else + { + if (s->s3->send_alert[0] == SSL3_AL_FATAL +#ifdef DTLS1_AD_MISSING_HANDSHAKE_MESSAGE + || s->s3->send_alert[1] == DTLS1_AD_MISSING_HANDSHAKE_MESSAGE +#endif + ) + (void)BIO_flush(s->wbio); + + if (s->msg_callback) + s->msg_callback(1, s->version, SSL3_RT_ALERT, s->s3->send_alert, + 2, s, s->msg_callback_arg); + + if (s->info_callback != NULL) + cb=s->info_callback; + else if (s->ctx->info_callback != NULL) + cb=s->ctx->info_callback; + + if (cb != NULL) + { + j=(s->s3->send_alert[0]<<8)|s->s3->send_alert[1]; + cb(s,SSL_CB_WRITE_ALERT,j); + } + } + return(i); + } + + +static DTLS1_BITMAP * +dtls1_get_bitmap(SSL *s, SSL3_RECORD *rr, unsigned int *is_next_epoch) + { + + *is_next_epoch = 0; + + /* In current epoch, accept HM, CCS, DATA, & ALERT */ + if (rr->epoch == s->d1->r_epoch) + return &s->d1->bitmap; + + /* Only HM and ALERT messages can be from the next epoch */ + else if (rr->epoch == (unsigned long)(s->d1->r_epoch + 1) && + (rr->type == SSL3_RT_HANDSHAKE || + rr->type == SSL3_RT_ALERT)) + { + *is_next_epoch = 1; + return &s->d1->next_bitmap; + } + + return NULL; + } + +#if 0 +static int +dtls1_record_needs_buffering(SSL *s, SSL3_RECORD *rr, unsigned short *priority, + unsigned long *offset) + { + + /* alerts are passed up immediately */ + if ( rr->type == SSL3_RT_APPLICATION_DATA || + rr->type == SSL3_RT_ALERT) + return 0; + + /* Only need to buffer if a handshake is underway. + * (this implies that Hello Request and Client Hello are passed up + * immediately) */ + if ( SSL_in_init(s)) + { + unsigned char *data = rr->data; + /* need to extract the HM/CCS sequence number here */ + if ( rr->type == SSL3_RT_HANDSHAKE || + rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) + { + unsigned short seq_num; + struct hm_header_st msg_hdr; + struct ccs_header_st ccs_hdr; + + if ( rr->type == SSL3_RT_HANDSHAKE) + { + dtls1_get_message_header(data, &msg_hdr); + seq_num = msg_hdr.seq; + *offset = msg_hdr.frag_off; + } + else + { + dtls1_get_ccs_header(data, &ccs_hdr); + seq_num = ccs_hdr.seq; + *offset = 0; + } + + /* this is either a record we're waiting for, or a + * retransmit of something we happened to previously + * receive (higher layers will drop the repeat silently */ + if ( seq_num < s->d1->handshake_read_seq) + return 0; + if (rr->type == SSL3_RT_HANDSHAKE && + seq_num == s->d1->handshake_read_seq && + msg_hdr.frag_off < s->d1->r_msg_hdr.frag_off) + return 0; + else if ( seq_num == s->d1->handshake_read_seq && + (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC || + msg_hdr.frag_off == s->d1->r_msg_hdr.frag_off)) + return 0; + else + { + *priority = seq_num; + return 1; + } + } + else /* unknown record type */ + return 0; + } + + return 0; + } +#endif + +void +dtls1_reset_seq_numbers(SSL *s, int rw) + { + unsigned char *seq; + unsigned int seq_bytes = sizeof(s->s3->read_sequence); + + if ( rw & SSL3_CC_READ) + { + seq = s->s3->read_sequence; + s->d1->r_epoch++; + + pq_64bit_assign(&(s->d1->bitmap.map), &(s->d1->next_bitmap.map)); + s->d1->bitmap.length = s->d1->next_bitmap.length; + pq_64bit_assign(&(s->d1->bitmap.max_seq_num), + &(s->d1->next_bitmap.max_seq_num)); + + pq_64bit_free(&(s->d1->next_bitmap.map)); + pq_64bit_free(&(s->d1->next_bitmap.max_seq_num)); + memset(&(s->d1->next_bitmap), 0x00, sizeof(DTLS1_BITMAP)); + pq_64bit_init(&(s->d1->next_bitmap.map)); + pq_64bit_init(&(s->d1->next_bitmap.max_seq_num)); + } + else + { + seq = s->s3->write_sequence; + s->d1->w_epoch++; + } + + memset(seq, 0x00, seq_bytes); + } + +#if PQ_64BIT_IS_INTEGER +static PQ_64BIT +bytes_to_long_long(unsigned char *bytes, PQ_64BIT *num) + { + PQ_64BIT _num; + + _num = (((PQ_64BIT)bytes[0]) << 56) | + (((PQ_64BIT)bytes[1]) << 48) | + (((PQ_64BIT)bytes[2]) << 40) | + (((PQ_64BIT)bytes[3]) << 32) | + (((PQ_64BIT)bytes[4]) << 24) | + (((PQ_64BIT)bytes[5]) << 16) | + (((PQ_64BIT)bytes[6]) << 8) | + (((PQ_64BIT)bytes[7]) ); + + *num = _num ; + return _num; + } +#endif + + +static void +dtls1_clear_timeouts(SSL *s) + { + memset(&(s->d1->timeout), 0x00, sizeof(struct dtls1_timeout_st)); + } |