Keep old method in case of an unsupported protocol
[openssl.git] / ssl / d1_lib.c
index b1bc3f58502b9f6ba3818bd15c32f45d921743d5..82ca653920630e0a7e49072748089ff242512371 100644 (file)
@@ -82,6 +82,7 @@ SSL3_ENC_METHOD DTLSv1_enc_data={
        TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
        TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
        tls1_alert_code,
+       tls1_export_keying_material,
        };
 
 long dtls1_default_timeout(void)
@@ -129,26 +130,33 @@ int dtls1_new(SSL *s)
        return(1);
        }
 
-void dtls1_free(SSL *s)
+static void dtls1_clear_queues(SSL *s)
        {
     pitem *item = NULL;
     hm_fragment *frag = NULL;
-
-       ssl3_free(s);
+       DTLS1_RECORD_DATA *rdata;
 
     while( (item = pqueue_pop(s->d1->unprocessed_rcds.q)) != NULL)
         {
+               rdata = (DTLS1_RECORD_DATA *) item->data;
+               if (rdata->rbuf.buf)
+                       {
+                       OPENSSL_free(rdata->rbuf.buf);
+                       }
         OPENSSL_free(item->data);
         pitem_free(item);
         }
-    pqueue_free(s->d1->unprocessed_rcds.q);
 
     while( (item = pqueue_pop(s->d1->processed_rcds.q)) != NULL)
         {
+               rdata = (DTLS1_RECORD_DATA *) item->data;
+               if (rdata->rbuf.buf)
+                       {
+                       OPENSSL_free(rdata->rbuf.buf);
+                       }
         OPENSSL_free(item->data);
         pitem_free(item);
         }
-    pqueue_free(s->d1->processed_rcds.q);
 
     while( (item = pqueue_pop(s->d1->buffered_messages)) != NULL)
         {
@@ -157,7 +165,6 @@ void dtls1_free(SSL *s)
         OPENSSL_free(frag);
         pitem_free(item);
         }
-    pqueue_free(s->d1->buffered_messages);
 
     while ( (item = pqueue_pop(s->d1->sent_messages)) != NULL)
         {
@@ -166,22 +173,74 @@ void dtls1_free(SSL *s)
         OPENSSL_free(frag);
         pitem_free(item);
         }
-       pqueue_free(s->d1->sent_messages);
 
        while ( (item = pqueue_pop(s->d1->buffered_app_data.q)) != NULL)
                {
-               frag = (hm_fragment *)item->data;
-               OPENSSL_free(frag->fragment);
-               OPENSSL_free(frag);
+               rdata = (DTLS1_RECORD_DATA *) item->data;
+               if (rdata->rbuf.buf)
+                       {
+                       OPENSSL_free(rdata->rbuf.buf);
+                       }
+               OPENSSL_free(item->data);
                pitem_free(item);
                }
+       }
+
+void dtls1_free(SSL *s)
+       {
+       ssl3_free(s);
+
+       dtls1_clear_queues(s);
+
+    pqueue_free(s->d1->unprocessed_rcds.q);
+    pqueue_free(s->d1->processed_rcds.q);
+    pqueue_free(s->d1->buffered_messages);
+       pqueue_free(s->d1->sent_messages);
        pqueue_free(s->d1->buffered_app_data.q);
 
        OPENSSL_free(s->d1);
+       s->d1 = NULL;
        }
 
 void dtls1_clear(SSL *s)
        {
+    pqueue unprocessed_rcds;
+    pqueue processed_rcds;
+    pqueue buffered_messages;
+       pqueue sent_messages;
+       pqueue buffered_app_data;
+       unsigned int mtu;
+
+       if (s->d1)
+               {
+               unprocessed_rcds = s->d1->unprocessed_rcds.q;
+               processed_rcds = s->d1->processed_rcds.q;
+               buffered_messages = s->d1->buffered_messages;
+               sent_messages = s->d1->sent_messages;
+               buffered_app_data = s->d1->buffered_app_data.q;
+               mtu = s->d1->mtu;
+
+               dtls1_clear_queues(s);
+
+               memset(s->d1, 0, sizeof(*(s->d1)));
+
+               if (s->server)
+                       {
+                       s->d1->cookie_len = sizeof(s->d1->cookie);
+                       }
+
+               if (SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)
+                       {
+                       s->d1->mtu = mtu;
+                       }
+
+               s->d1->unprocessed_rcds.q = unprocessed_rcds;
+               s->d1->processed_rcds.q = processed_rcds;
+               s->d1->buffered_messages = buffered_messages;
+               s->d1->sent_messages = sent_messages;
+               s->d1->buffered_app_data.q = buffered_app_data;
+               }
+
        ssl3_clear(s);
        if (s->options & SSL_OP_CISCO_ANYCONNECT)
                s->version=DTLS1_BAD_VER;
@@ -207,6 +266,16 @@ long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg)
        case DTLS_CTRL_LISTEN:
                ret = dtls1_listen(s, parg);
                break;
+       case SSL_CTRL_CHECK_PROTO_VERSION:
+               /* For library-internal use; checks that the current protocol
+                * is the highest enabled version (according to s->ctx->method,
+                * as version negotiation may have changed s->method). */
+#if DTLS_MAX_VERSION != DTLS1_VERSION
+#  error Code needs update for DTLS_method() support beyond DTLS1_VERSION.
+#endif
+               /* Just one protocol version is supported so far;
+                * fail closed if the version is not as expected. */
+               return s->version == DTLS_MAX_VERSION;
 
        default:
                ret = ssl3_ctrl(s, cmd, larg, parg);
@@ -237,6 +306,15 @@ const SSL_CIPHER *dtls1_get_cipher(unsigned int u)
 
 void dtls1_start_timer(SSL *s)
        {
+#ifndef OPENSSL_NO_SCTP
+       /* Disable timer for SCTP */
+       if (BIO_dgram_is_sctp(SSL_get_wbio(s)))
+               {
+               memset(&(s->d1->next_timeout), 0, sizeof(struct timeval));
+               return;
+               }
+#endif
+
        /* If timer is not set, initialize duration with 1 second */
        if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0)
                {
@@ -283,6 +361,16 @@ struct timeval* dtls1_get_timeout(SSL *s, struct timeval* timeleft)
                timeleft->tv_usec += 1000000;
                }
 
+       /* If remaining time is less than 15 ms, set it to 0
+        * to prevent issues because of small devergences with
+        * socket timeouts.
+        */
+       if (timeleft->tv_sec == 0 && timeleft->tv_usec < 15000)
+               {
+               memset(timeleft, 0, sizeof(struct timeval));
+               }
+       
+
        return timeleft;
        }
 
@@ -317,15 +405,36 @@ void dtls1_double_timeout(SSL *s)
 void dtls1_stop_timer(SSL *s)
        {
        /* Reset everything */
+       memset(&(s->d1->timeout), 0, sizeof(struct dtls1_timeout_st));
        memset(&(s->d1->next_timeout), 0, sizeof(struct timeval));
        s->d1->timeout_duration = 1;
        BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout));
+       /* Clear retransmission buffer */
+       dtls1_clear_record_buffer(s);
        }
 
-int dtls1_handle_timeout(SSL *s)
+int dtls1_check_timeout_num(SSL *s)
        {
-       DTLS1_STATE *state;
+       s->d1->timeout.num_alerts++;
+
+       /* Reduce MTU after 2 unsuccessful retransmissions */
+       if (s->d1->timeout.num_alerts > 2)
+               {
+               s->d1->mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL);               
+               }
+
+       if (s->d1->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT)
+               {
+               /* fail the connection, enough alerts have been sent */
+               SSLerr(SSL_F_DTLS1_CHECK_TIMEOUT_NUM,SSL_R_READ_TIMEOUT_EXPIRED);
+               return -1;
+               }
 
+       return 0;
+       }
+
+int dtls1_handle_timeout(SSL *s)
+       {
        /* if no timer is expired, don't do anything */
        if (!dtls1_is_timer_expired(s))
                {
@@ -333,20 +442,23 @@ int dtls1_handle_timeout(SSL *s)
                }
 
        dtls1_double_timeout(s);
-       state = s->d1;
-       state->timeout.num_alerts++;
-       if ( state->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT)
+
+       if (dtls1_check_timeout_num(s) < 0)
+               return -1;
+
+       s->d1->timeout.read_timeouts++;
+       if (s->d1->timeout.read_timeouts > DTLS1_TMO_READ_COUNT)
                {
-               /* fail the connection, enough alerts have been sent */
-               SSLerr(SSL_F_DTLS1_HANDLE_TIMEOUT,SSL_R_READ_TIMEOUT_EXPIRED);
-               return 0;
+               s->d1->timeout.read_timeouts = 1;
                }
 
-       state->timeout.read_timeouts++;
-       if ( state->timeout.read_timeouts > DTLS1_TMO_READ_COUNT)
+#ifndef OPENSSL_NO_HEARTBEATS
+       if (s->tlsext_hb_pending)
                {
-               state->timeout.read_timeouts = 1;
+               s->tlsext_hb_pending = 0;
+               return dtls1_heartbeat(s);
                }
+#endif
 
        dtls1_start_timer(s);
        return dtls1_retransmit_buffered_messages(s);
@@ -382,194 +494,3 @@ int dtls1_listen(SSL *s, struct sockaddr *client)
        (void) BIO_dgram_get_peer(SSL_get_rbio(s), client);
        return 1;
        }
-
-#ifndef OPENSSL_NO_TLSEXT
-unsigned char *ssl_add_clienthello_dtlsext(SSL *s, unsigned char *p, unsigned char *limit)
-       {
-       int extdatalen = 0;
-       unsigned char *ret = p;
-       int el;
-
-       ret+=2;
-       
-       if (ret>=limit) return NULL; /* this really never occurs, but ... */
-
-       /* Renegotiate extension */
-       if(!ssl_add_clienthello_renegotiate_ext(s, 0, &el, 0))
-               {
-               SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
-               return NULL;
-               }
-
-       if((limit - p - 4 - el) < 0) return NULL;
-         
-       s2n(TLSEXT_TYPE_renegotiate,ret);
-       s2n(el,ret);
-
-       if(!ssl_add_clienthello_renegotiate_ext(s, ret, &el, el))
-               {
-               SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
-               return NULL;
-               }
-
-       ret += el;
-
-       if ((extdatalen = ret-p-2)== 0) 
-               return p;
-
-       s2n(extdatalen,p);
-
-       return ret;
-       }
-
-int ssl_parse_clienthello_dtlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
-       {
-       unsigned short type;
-       unsigned short size;
-       unsigned short len;
-       unsigned char *data = *p;
-       int renegotiate_seen = 0;
-
-       if (data >= (d+n-2))
-               {
-               if (s->new_session
-                       && !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
-                       {
-                       /* We should always see one extension: the renegotiate extension */
-                       SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
-                       *al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
-                       return 0;
-                       }
-               return 1;
-               }
-       n2s(data,len);
-
-       if (data > (d+n-len)) 
-               return 1;
-
-       while (data <= (d+n-4))
-               {
-               n2s(data,type);
-               n2s(data,size);
-               
-               if (data+size > (d+n))
-                       return 1;
-               
-               if (type == TLSEXT_TYPE_renegotiate)
-                       {
-                       if(!ssl_parse_clienthello_renegotiate_ext(s, data, size, al))
-                               return 0;
-                       renegotiate_seen = 1;
-                       }
-               
-               data+=size;
-               }
-
-       if (s->new_session && !renegotiate_seen
-               && !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
-               {
-               *al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
-               SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
-               return 0;
-               }
-
-       *p = data;
-       return 1;
-       }
-
-unsigned char *ssl_add_serverhello_dtlsext(SSL *s, unsigned char *p, unsigned char *limit)
-       {
-       int extdatalen = 0;
-       unsigned char *ret = p;
-       
-       ret+=2;
-       
-       if (ret>=limit) return NULL; /* this really never occurs, but ... */
-       
-       if(s->s3->send_connection_binding)
-               {
-               int el;
-
-               if(!ssl_add_serverhello_renegotiate_ext(s, 0, &el, 0))
-                       {
-                       SSLerr(SSL_F_SSL_ADD_SERVERHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
-                       return NULL;
-                       }
-
-               if((limit - p - 4 - el) < 0) return NULL;
-          
-               s2n(TLSEXT_TYPE_renegotiate,ret);
-               s2n(el,ret);
-
-               if(!ssl_add_serverhello_renegotiate_ext(s, ret, &el, el))
-                       {
-                       SSLerr(SSL_F_SSL_ADD_SERVERHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
-                       return NULL;
-                       }
-
-               ret += el;
-               }
-
-       if ((extdatalen = ret-p-2)== 0) 
-               return p;
-
-       s2n(extdatalen,p);
-
-       return ret;
-       }
-
-int ssl_parse_serverhello_dtlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
-       {
-       unsigned short type;
-       unsigned short size;
-       unsigned short len;
-       unsigned char *data = *p;
-       int renegotiate_seen = 0;
-       
-       if (data >= (d+n-2))
-               {
-               if (s->new_session
-                       && !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
-                       {
-                       /* We should always see one extension: the renegotiate extension */
-                       SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
-                       *al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
-                       return 0;
-                       }
-               return 1;
-               }
-       n2s(data,len);
-       
-       if (data > (d+n-len)) 
-               return 1;
-       
-       while (data <= (d+n-4))
-               {
-               n2s(data,type);
-               n2s(data,size);
-               
-               if (data+size > (d+n))
-                       return 1;
-               
-               if (type == TLSEXT_TYPE_renegotiate)
-                       {
-                       if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al))
-                               return 0;
-                       renegotiate_seen = 1;
-                       }
-               
-               data+=size;
-               }
-
-       if (s->new_session && !renegotiate_seen
-               && !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
-               {
-               *al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
-               SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
-               return 0;
-               }
-
-       *p = data;
-       return 1;
-       }
-#endif