diff options
author | marha <marha@users.sourceforge.net> | 2010-03-30 12:36:28 +0000 |
---|---|---|
committer | marha <marha@users.sourceforge.net> | 2010-03-30 12:36:28 +0000 |
commit | ff48c0d9098080b51ea12710029135916d117806 (patch) | |
tree | 96e6af9caf170ba21a1027b24e306a07e27d7b75 /openssl/ssl/d1_both.c | |
parent | bb731f5ac92655c4860a41fa818a7a63005f8369 (diff) | |
download | vcxsrv-ff48c0d9098080b51ea12710029135916d117806.tar.gz vcxsrv-ff48c0d9098080b51ea12710029135916d117806.tar.bz2 vcxsrv-ff48c0d9098080b51ea12710029135916d117806.zip |
svn merge -r514:HEAD ^/branches/released .
Diffstat (limited to 'openssl/ssl/d1_both.c')
-rw-r--r-- | openssl/ssl/d1_both.c | 326 |
1 files changed, 201 insertions, 125 deletions
diff --git a/openssl/ssl/d1_both.c b/openssl/ssl/d1_both.c index 15a201a25..0242f1e4d 100644 --- a/openssl/ssl/d1_both.c +++ b/openssl/ssl/d1_both.c @@ -136,7 +136,6 @@ static unsigned char *dtls1_write_message_header(SSL *s, static void dtls1_set_message_header_int(SSL *s, unsigned char mt, unsigned long len, unsigned short seq_num, unsigned long frag_off, unsigned long frag_len); -static int dtls1_retransmit_buffered_messages(SSL *s); static long dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok); @@ -178,7 +177,7 @@ int dtls1_do_write(SSL *s, int type) { int ret; int curr_mtu; - unsigned int len, frag_off; + unsigned int len, frag_off, mac_size, blocksize; /* AHA! Figure out the MTU, and stick to the right size */ if ( ! (SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) @@ -226,11 +225,22 @@ int dtls1_do_write(SSL *s, int type) OPENSSL_assert(s->init_num == (int)s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH); + if (s->write_hash) + mac_size = EVP_MD_CTX_size(s->write_hash); + else + mac_size = 0; + + if (s->enc_write_ctx && + (EVP_CIPHER_mode( s->enc_write_ctx->cipher) & EVP_CIPH_CBC_MODE)) + blocksize = 2 * EVP_CIPHER_block_size(s->enc_write_ctx->cipher); + else + blocksize = 0; + frag_off = 0; while( s->init_num) { curr_mtu = s->d1->mtu - BIO_wpending(SSL_get_wbio(s)) - - DTLS1_RT_HEADER_LENGTH; + DTLS1_RT_HEADER_LENGTH - mac_size - blocksize; if ( curr_mtu <= DTLS1_HM_HEADER_LENGTH) { @@ -238,7 +248,8 @@ int dtls1_do_write(SSL *s, int type) ret = BIO_flush(SSL_get_wbio(s)); if ( ret <= 0) return ret; - curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH; + curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH - + mac_size - blocksize; } if ( s->init_num > curr_mtu) @@ -280,7 +291,7 @@ int dtls1_do_write(SSL *s, int type) * retransmit */ if ( BIO_ctrl(SSL_get_wbio(s), - BIO_CTRL_DGRAM_MTU_EXCEEDED, 0, NULL)) + BIO_CTRL_DGRAM_MTU_EXCEEDED, 0, NULL) > 0 ) s->d1->mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); else @@ -301,7 +312,7 @@ int dtls1_do_write(SSL *s, int type) const struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr; int xlen; - if (frag_off == 0 && s->client_version != DTLS1_BAD_VER) + if (frag_off == 0 && s->version != DTLS1_BAD_VER) { /* reconstruct message header is if it * is being sent in single fragment */ @@ -408,9 +419,10 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok) s2n (msg_hdr->seq,p); l2n3(0,p); l2n3(msg_len,p); - if (s->client_version != DTLS1_BAD_VER) - p -= DTLS1_HM_HEADER_LENGTH, + if (s->version != DTLS1_BAD_VER) { + p -= DTLS1_HM_HEADER_LENGTH; msg_len += DTLS1_HM_HEADER_LENGTH; + } ssl3_finish_mac(s, p, msg_len); if (s->msg_callback) @@ -474,7 +486,7 @@ static int dtls1_preprocess_fragment(SSL *s,struct hm_header_st *msg_hdr,int max { /* msg_len is limited to 2^24, but is effectively checked * against max above */ - if (!BUF_MEM_grow_clean(s->init_buf,(int)msg_len+DTLS1_HM_HEADER_LENGTH)) + if (!BUF_MEM_grow_clean(s->init_buf,msg_len+DTLS1_HM_HEADER_LENGTH)) { SSLerr(SSL_F_DTLS1_PREPROCESS_FRAGMENT,ERR_R_BUF_LIB); return SSL_AD_INTERNAL_ERROR; @@ -519,6 +531,7 @@ dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok) if ( s->d1->handshake_read_seq == frag->msg_header.seq) { + unsigned long frag_len = frag->msg_header.frag_len; pqueue_pop(s->d1->buffered_messages); al=dtls1_preprocess_fragment(s,&frag->msg_header,max); @@ -536,7 +549,7 @@ dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok) if (al==0) { *ok = 1; - return frag->msg_header.frag_len; + return frag_len; } ssl3_send_alert(s,SSL3_AL_FATAL,al); @@ -555,13 +568,26 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok) int i=-1; hm_fragment *frag = NULL; pitem *item = NULL; - PQ_64BIT seq64; + unsigned char seq64be[8]; unsigned long frag_len = msg_hdr->frag_len; if ((msg_hdr->frag_off+frag_len) > msg_hdr->msg_len) goto err; - if (msg_hdr->seq <= s->d1->handshake_read_seq) + /* Try to find item in queue, to prevent duplicate entries */ + memset(seq64be,0,sizeof(seq64be)); + seq64be[6] = (unsigned char) (msg_hdr->seq>>8); + seq64be[7] = (unsigned char) msg_hdr->seq; + item = pqueue_find(s->d1->buffered_messages, seq64be); + + /* Discard the message if sequence number was already there, is + * too far in the future, already in the queue or if we received + * a FINISHED before the SERVER_HELLO, which then must be a stale + * retransmit. + */ + if (msg_hdr->seq <= s->d1->handshake_read_seq || + msg_hdr->seq > s->d1->handshake_read_seq + 10 || item != NULL || + (s->d1->handshake_read_seq == 0 && msg_hdr->type == SSL3_MT_FINISHED)) { unsigned char devnull [256]; @@ -575,30 +601,31 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok) } } - frag = dtls1_hm_fragment_new(frag_len); - if ( frag == NULL) - goto err; - - memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr)); - if (frag_len) { + frag = dtls1_hm_fragment_new(frag_len); + if ( frag == NULL) + goto err; + + memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr)); + /* read the body of the fragment (header has already been read */ i = s->method->ssl_read_bytes(s,SSL3_RT_HANDSHAKE, frag->fragment,frag_len,0); if (i<=0 || (unsigned long)i!=frag_len) goto err; - } - pq_64bit_init(&seq64); - pq_64bit_assign_word(&seq64, msg_hdr->seq); + memset(seq64be,0,sizeof(seq64be)); + seq64be[6] = (unsigned char)(msg_hdr->seq>>8); + seq64be[7] = (unsigned char)(msg_hdr->seq); - item = pitem_new(seq64, frag); - pq_64bit_free(&seq64); - if ( item == NULL) - goto err; + item = pitem_new(seq64be, frag); + if ( item == NULL) + goto err; + + pqueue_insert(s->d1->buffered_messages, item); + } - pqueue_insert(s->d1->buffered_messages, item); return DTLS1_HM_FRAGMENT_RETRY; err: @@ -731,14 +758,30 @@ int dtls1_send_finished(SSL *s, int a, int b, const char *sender, int slen) p= &(d[DTLS1_HM_HEADER_LENGTH]); i=s->method->ssl3_enc->final_finish_mac(s, - &(s->s3->finish_dgst1), - &(s->s3->finish_dgst2), sender,slen,s->s3->tmp.finish_md); s->s3->tmp.finish_md_len = i; memcpy(p, s->s3->tmp.finish_md, i); p+=i; l=i; + /* Copy the finished so we can use it for + * renegotiation checks + */ + if(s->type == SSL_ST_CONNECT) + { + OPENSSL_assert(i <= EVP_MAX_MD_SIZE); + memcpy(s->s3->previous_client_finished, + s->s3->tmp.finish_md, i); + s->s3->previous_client_finished_len=i; + } + else + { + OPENSSL_assert(i <= EVP_MAX_MD_SIZE); + memcpy(s->s3->previous_server_finished, + s->s3->tmp.finish_md, i); + s->s3->previous_server_finished_len=i; + } + #ifdef OPENSSL_SYS_WIN16 /* MSVC 1.5 does not clear the top bytes of the word unless * I do this. @@ -779,12 +822,11 @@ int dtls1_send_change_cipher_spec(SSL *s, int a, int b) s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; s->init_num=DTLS1_CCS_HEADER_LENGTH; - if (s->client_version == DTLS1_BAD_VER) - { + if (s->version == DTLS1_BAD_VER) { s->d1->next_handshake_write_seq++; s2n(s->d1->handshake_write_seq,p); s->init_num+=2; - } + } s->init_off=0; @@ -801,14 +843,30 @@ int dtls1_send_change_cipher_spec(SSL *s, int a, int b) return(dtls1_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC)); } +static int dtls1_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x) + { + int n; + unsigned char *p; + + n=i2d_X509(x,NULL); + if (!BUF_MEM_grow_clean(buf,(int)(n+(*l)+3))) + { + SSLerr(SSL_F_DTLS1_ADD_CERT_TO_BUF,ERR_R_BUF_LIB); + return 0; + } + p=(unsigned char *)&(buf->data[*l]); + l2n3(n,p); + i2d_X509(x,&p); + *l+=n+3; + + return 1; + } unsigned long dtls1_output_cert_chain(SSL *s, X509 *x) { unsigned char *p; - int n,i; + int i; unsigned long l= 3 + DTLS1_HM_HEADER_LENGTH; BUF_MEM *buf; - X509_STORE_CTX xs_ctx; - X509_OBJECT obj; /* TLSv1 sends a chain with nothing in it, instead of an alert */ buf=s->init_buf; @@ -819,54 +877,35 @@ unsigned long dtls1_output_cert_chain(SSL *s, X509 *x) } if (x != NULL) { - if(!X509_STORE_CTX_init(&xs_ctx,s->ctx->cert_store,NULL,NULL)) - { - SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_X509_LIB); - return(0); - } - - for (;;) - { - n=i2d_X509(x,NULL); - if (!BUF_MEM_grow_clean(buf,(int)(n+l+3))) - { - SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB); - return(0); - } - p=(unsigned char *)&(buf->data[l]); - l2n3(n,p); - i2d_X509(x,&p); - l+=n+3; - if (X509_NAME_cmp(X509_get_subject_name(x), - X509_get_issuer_name(x)) == 0) break; - - i=X509_STORE_get_by_subject(&xs_ctx,X509_LU_X509, - X509_get_issuer_name(x),&obj); - if (i <= 0) break; - x=obj.data.x509; - /* Count is one too high since the X509_STORE_get uped the - * ref count */ - X509_free(x); - } - - X509_STORE_CTX_cleanup(&xs_ctx); - } - - /* Thawte special :-) */ - if (s->ctx->extra_certs != NULL) + X509_STORE_CTX xs_ctx; + + if (!X509_STORE_CTX_init(&xs_ctx,s->ctx->cert_store,x,NULL)) + { + SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_X509_LIB); + return(0); + } + + X509_verify_cert(&xs_ctx); + /* Don't leave errors in the queue */ + ERR_clear_error(); + for (i=0; i < sk_X509_num(xs_ctx.chain); i++) + { + x = sk_X509_value(xs_ctx.chain, i); + + if (!dtls1_add_cert_to_buf(buf, &l, x)) + { + X509_STORE_CTX_cleanup(&xs_ctx); + return 0; + } + } + X509_STORE_CTX_cleanup(&xs_ctx); + } + /* Thawte special :-) */ for (i=0; i<sk_X509_num(s->ctx->extra_certs); i++) { x=sk_X509_value(s->ctx->extra_certs,i); - n=i2d_X509(x,NULL); - if (!BUF_MEM_grow_clean(buf,(int)(n+l+3))) - { - SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB); - return(0); - } - p=(unsigned char *)&(buf->data[l]); - l2n3(n,p); - i2d_X509(x,&p); - l+=n+3; + if (!dtls1_add_cert_to_buf(buf, &l, x)) + return 0; } l-= (3 + DTLS1_HM_HEADER_LENGTH); @@ -883,18 +922,13 @@ unsigned long dtls1_output_cert_chain(SSL *s, X509 *x) int dtls1_read_failed(SSL *s, int code) { - DTLS1_STATE *state; - BIO *bio; - int send_alert = 0; - if ( code > 0) { fprintf( stderr, "invalid state reached %s:%d", __FILE__, __LINE__); return 1; } - bio = SSL_get_rbio(s); - if ( ! BIO_dgram_recv_timedout(bio)) + if (!dtls1_is_timer_expired(s)) { /* not a timeout, none of our business, let higher layers handle this. in fact it's probably an error */ @@ -907,23 +941,6 @@ int dtls1_read_failed(SSL *s, int code) return code; } - state = s->d1; - state->timeout.num_alerts++; - if ( state->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT) - { - /* fail the connection, enough alerts have been sent */ - SSLerr(SSL_F_DTLS1_READ_FAILED,SSL_R_READ_TIMEOUT_EXPIRED); - return 0; - } - - state->timeout.read_timeouts++; - if ( state->timeout.read_timeouts > DTLS1_TMO_READ_COUNT) - { - send_alert = 1; - state->timeout.read_timeouts = 1; - } - - #if 0 /* for now, each alert contains only one record number */ item = pqueue_peek(state->rcvd_records); if ( item ) @@ -934,16 +951,29 @@ int dtls1_read_failed(SSL *s, int code) #endif #if 0 /* no more alert sending, just retransmit the last set of messages */ - if ( send_alert) - ssl3_send_alert(s,SSL3_AL_WARNING, - DTLS1_AD_MISSING_HANDSHAKE_MESSAGE); + if ( state->timeout.read_timeouts >= DTLS1_TMO_READ_COUNT) + ssl3_send_alert(s,SSL3_AL_WARNING, + DTLS1_AD_MISSING_HANDSHAKE_MESSAGE); #endif - return dtls1_retransmit_buffered_messages(s) ; + return dtls1_handle_timeout(s); } +int +dtls1_get_queue_priority(unsigned short seq, int is_ccs) + { + /* The index of the retransmission queue actually is the message sequence number, + * since the queue only contains messages of a single handshake. However, the + * ChangeCipherSpec has no message sequence number and so using only the sequence + * will result in the CCS and Finished having the same index. To prevent this, + * the sequence number is multiplied by 2. In case of a CCS 1 is subtracted. + * This does not only differ CSS and Finished, it also maintains the order of the + * index (important for priority queues) and fits in the unsigned short variable. + */ + return seq * 2 - is_ccs; + } -static int +int dtls1_retransmit_buffered_messages(SSL *s) { pqueue sent = s->d1->sent_messages; @@ -957,8 +987,9 @@ dtls1_retransmit_buffered_messages(SSL *s) for ( item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) { frag = (hm_fragment *)item->data; - if ( dtls1_retransmit_message(s, frag->msg_header.seq, 0, &found) <= 0 && - found) + if ( dtls1_retransmit_message(s, + (unsigned short)dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs), + 0, &found) <= 0 && found) { fprintf(stderr, "dtls1_retransmit_message() failed\n"); return -1; @@ -973,8 +1004,7 @@ dtls1_buffer_message(SSL *s, int is_ccs) { pitem *item; hm_fragment *frag; - PQ_64BIT seq64; - unsigned int epoch = s->d1->w_epoch; + unsigned char seq64be[8]; /* this function is called immediately after a message has * been serialized */ @@ -987,8 +1017,7 @@ dtls1_buffer_message(SSL *s, int is_ccs) if ( is_ccs) { OPENSSL_assert(s->d1->w_msg_hdr.msg_len + - DTLS1_CCS_HEADER_LENGTH <= (unsigned int)s->init_num); - epoch++; + ((s->version==DTLS1_VERSION)?DTLS1_CCS_HEADER_LENGTH:3) == (unsigned int)s->init_num); } else { @@ -1003,11 +1032,20 @@ dtls1_buffer_message(SSL *s, int is_ccs) frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len; frag->msg_header.is_ccs = is_ccs; - pq_64bit_init(&seq64); - pq_64bit_assign_word(&seq64, epoch<<16 | frag->msg_header.seq); - - item = pitem_new(seq64, frag); - pq_64bit_free(&seq64); + /* save current state*/ + frag->msg_header.saved_retransmit_state.enc_write_ctx = s->enc_write_ctx; + frag->msg_header.saved_retransmit_state.write_hash = s->write_hash; + frag->msg_header.saved_retransmit_state.compress = s->compress; + frag->msg_header.saved_retransmit_state.session = s->session; + frag->msg_header.saved_retransmit_state.epoch = s->d1->w_epoch; + + memset(seq64be,0,sizeof(seq64be)); + seq64be[6] = (unsigned char)(dtls1_get_queue_priority(frag->msg_header.seq, + frag->msg_header.is_ccs)>>8); + seq64be[7] = (unsigned char)(dtls1_get_queue_priority(frag->msg_header.seq, + frag->msg_header.is_ccs)); + + item = pitem_new(seq64be, frag); if ( item == NULL) { dtls1_hm_fragment_free(frag); @@ -1033,7 +1071,9 @@ dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, pitem *item; hm_fragment *frag ; unsigned long header_length; - PQ_64BIT seq64; + unsigned char seq64be[8]; + struct dtls1_retransmit_state saved_state; + unsigned char save_write_sequence[8]; /* OPENSSL_assert(s->init_num == 0); @@ -1041,11 +1081,11 @@ dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, */ /* XDTLS: the requested message ought to be found, otherwise error */ - pq_64bit_init(&seq64); - pq_64bit_assign_word(&seq64, seq); + memset(seq64be,0,sizeof(seq64be)); + seq64be[6] = (unsigned char)(seq>>8); + seq64be[7] = (unsigned char)seq; - item = pqueue_find(s->d1->sent_messages, seq64); - pq_64bit_free(&seq64); + item = pqueue_find(s->d1->sent_messages, seq64be); if ( item == NULL) { fprintf(stderr, "retransmit: message %d non-existant\n", seq); @@ -1069,9 +1109,45 @@ dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, frag->msg_header.msg_len, frag->msg_header.seq, 0, frag->msg_header.frag_len); + /* save current state */ + saved_state.enc_write_ctx = s->enc_write_ctx; + saved_state.write_hash = s->write_hash; + saved_state.compress = s->compress; + saved_state.session = s->session; + saved_state.epoch = s->d1->w_epoch; + saved_state.epoch = s->d1->w_epoch; + s->d1->retransmitting = 1; + + /* restore state in which the message was originally sent */ + s->enc_write_ctx = frag->msg_header.saved_retransmit_state.enc_write_ctx; + s->write_hash = frag->msg_header.saved_retransmit_state.write_hash; + s->compress = frag->msg_header.saved_retransmit_state.compress; + s->session = frag->msg_header.saved_retransmit_state.session; + s->d1->w_epoch = frag->msg_header.saved_retransmit_state.epoch; + + if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) + { + memcpy(save_write_sequence, s->s3->write_sequence, sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, s->d1->last_write_sequence, sizeof(s->s3->write_sequence)); + } + ret = dtls1_do_write(s, frag->msg_header.is_ccs ? - SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE); + SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE); + + /* restore current state */ + s->enc_write_ctx = saved_state.enc_write_ctx; + s->write_hash = saved_state.write_hash; + s->compress = saved_state.compress; + s->session = saved_state.session; + s->d1->w_epoch = saved_state.epoch; + + if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) + { + memcpy(s->d1->last_write_sequence, s->s3->write_sequence, sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, save_write_sequence, sizeof(s->s3->write_sequence)); + } + s->d1->retransmitting = 0; (void)BIO_flush(SSL_get_wbio(s)); @@ -1160,7 +1236,7 @@ dtls1_min_mtu(void) static unsigned int dtls1_guess_mtu(unsigned int curr_mtu) { - size_t i; + unsigned int i; if ( curr_mtu == 0 ) return g_probable_mtu[0] ; |