]> git.ipfire.org Git - thirdparty/openssl.git/blobdiff - ssl/t1_lib.c
Implement the Opaque PRF Input TLS extension
[thirdparty/openssl.git] / ssl / t1_lib.c
index b5eab2cb68b449e6beece24f9195c5a67bb707a3..0c7841402d3f3e55c792ba234b07948aca7c62ac 100644 (file)
@@ -284,8 +284,8 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                   + hostname length 
                */
                   
-               if ((lenmax = limit - p - 9) < 0 
-               || (size_str = strlen(s->tlsext_hostname)) > (unsigned long)lenmax) 
+               if ((lenmax = limit - ret - 9) < 0 
+                   || (size_str = strlen(s->tlsext_hostname)) > (unsigned long)lenmax) 
                        return NULL;
                        
                /* extension type and length */
@@ -300,15 +300,15 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                s2n(size_str,ret);
                memcpy(ret, s->tlsext_hostname, size_str);
                ret+=size_str;
-
                }
+
 #ifndef OPENSSL_NO_EC
        if (s->tlsext_ecpointformatlist != NULL)
                {
                /* Add TLS extension ECPointFormats to the ClientHello message */
                long lenmax; 
 
-               if ((lenmax = limit - p - 5) < 0) return NULL; 
+               if ((lenmax = limit - ret - 5) < 0) return NULL; 
                if (s->tlsext_ecpointformatlist_length > (unsigned long)lenmax) return NULL;
                if (s->tlsext_ecpointformatlist_length > 255)
                        {
@@ -327,7 +327,7 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                /* Add TLS extension EllipticCurves to the ClientHello message */
                long lenmax; 
 
-               if ((lenmax = limit - p - 6) < 0) return NULL; 
+               if ((lenmax = limit - ret - 6) < 0) return NULL; 
                if (s->tlsext_ellipticcurvelist_length > (unsigned long)lenmax) return NULL;
                if (s->tlsext_ellipticcurvelist_length > 65532)
                        {
@@ -359,8 +359,7 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                /* Check for enough room 2 for extension type, 2 for len
                 * rest for ticket
                 */
-               if (limit - p - 4 - ticklen < 0)
-                       return NULL;
+               if ((long)(limit - ret - 4 - ticklen) < 0) return NULL;
                s2n(TLSEXT_TYPE_session_ticket,ret); 
                s2n(ticklen,ret);
                if (ticklen)
@@ -370,6 +369,24 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                        }
                }
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+       if (s->s3->client_opaque_prf_input != NULL)
+               {
+               size_t col = s->s3->client_opaque_prf_input_len;
+               
+               if ((long)(limit - ret - 6 - col < 0))
+                       return NULL;
+               if (col > 0xFFFD) /* can't happen */
+                       return NULL;
+
+               s2n(TLSEXT_TYPE_opaque_prf_input, ret); 
+               s2n(col + 2, ret);
+               s2n(col, ret);
+               memcpy(ret, s->s3->client_opaque_prf_input, col);
+               ret += col;
+               }
+#endif
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -387,7 +404,7 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
 
        if (!s->hit && s->servername_done == 1 && s->session->tlsext_hostname != NULL)
                { 
-               if (limit - p - 4 < 0) return NULL; 
+               if ((long)(limit - ret - 4) < 0) return NULL; 
 
                s2n(TLSEXT_TYPE_server_name,ret);
                s2n(0,ret);
@@ -398,7 +415,7 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
                /* Add TLS extension ECPointFormats to the ServerHello message */
                long lenmax; 
 
-               if ((lenmax = limit - p - 5) < 0) return NULL; 
+               if ((lenmax = limit - ret - 5) < 0) return NULL; 
                if (s->tlsext_ecpointformatlist_length > (unsigned long)lenmax) return NULL;
                if (s->tlsext_ecpointformatlist_length > 255)
                        {
@@ -419,11 +436,29 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
        if (s->tlsext_ticket_expected
                && !(SSL_get_options(s) & SSL_OP_NO_TICKET)) 
                { 
-               if (limit - p - 4 < 0) return NULL; 
+               if ((long)(limit - ret - 4) < 0) return NULL; 
                s2n(TLSEXT_TYPE_session_ticket,ret);
                s2n(0,ret);
                }
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+       if (s->s3->server_opaque_prf_input != NULL)
+               {
+               size_t sol = s->s3->server_opaque_prf_input_len;
                
+               if ((long)(limit - ret - 6 - sol) < 0)
+                       return NULL;
+               if (sol > 0xFFFD) /* can't happen */
+                       return NULL;
+
+               s2n(TLSEXT_TYPE_opaque_prf_input, ret); 
+               s2n(sol + 2, ret);
+               s2n(sol, ret);
+               memcpy(ret, s->s3->server_opaque_prf_input, sol);
+               ret += sol;
+               }
+#endif
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -610,6 +645,35 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 #endif
                        }
 #endif /* OPENSSL_NO_EC */
+#ifdef TLSEXT_TYPE_opaque_prf_input
+               else if (type == TLSEXT_TYPE_opaque_prf_input)
+                       {
+                       unsigned char *sdata = data;
+
+                       if (size < 2)
+                               {
+                               *al = SSL_AD_DECODE_ERROR;
+                               return 0;
+                               }
+                       n2s(sdata, s->s3->client_opaque_prf_input_len);
+                       if (s->s3->client_opaque_prf_input_len != size - 2)
+                               {
+                               *al = SSL_AD_DECODE_ERROR;
+                               return 0;
+                               }
+
+                       if (s->s3->client_opaque_prf_input != NULL) /* shouldn't really happen */
+                               OPENSSL_free(s->s3->client_opaque_prf_input);
+
+                       s->s3->client_opaque_prf_input = BUF_memdup(sdata, s->s3->client_opaque_prf_input_len);
+                       if (s->s3->client_opaque_prf_input == NULL)
+                               {
+                               *al = TLS1_AD_INTERNAL_ERROR;
+                               return 0;
+                               }
+                       }
+#endif
+
                /* session ticket processed earlier */
                data+=size;
                }
@@ -694,6 +758,35 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
                                }
                        s->tlsext_ticket_expected = 1;
                        }
+#ifdef TLSEXT_TYPE_opaque_prf_input
+               else if (type == TLSEXT_TYPE_opaque_prf_input)
+                       {
+                       unsigned char *sdata = data;
+
+                       if (size < 2)
+                               {
+                               *al = SSL_AD_DECODE_ERROR;
+                               return 0;
+                               }
+                       n2s(sdata, s->s3->server_opaque_prf_input_len);
+                       if (s->s3->server_opaque_prf_input_len != size - 2)
+                               {
+                               *al = SSL_AD_DECODE_ERROR;
+                               return 0;
+                               }
+                       
+                       if (s->s3->server_opaque_prf_input != NULL) /* shouldn't really happen */
+                               OPENSSL_free(s->s3->server_opaque_prf_input);
+                       s->s3->server_opaque_prf_input = BUF_memdup(sdata, s->s3->server_opaque_prf_input_len);
+
+                       if (s->s3->server_opaque_prf_input == NULL)
+                               {
+                               *al = TLS1_AD_INTERNAL_ERROR;
+                               return 0;
+                               }
+                       }
+#endif
+
                data+=size;             
                }
 
@@ -780,6 +873,38 @@ int ssl_prepare_clienthello_tlsext(SSL *s)
                        s2n(i,j);
                }
 #endif /* OPENSSL_NO_EC */
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+       {
+               int r = 1;
+       
+               if (s->ctx->tlsext_opaque_prf_input_callback != 0)
+                       {
+                       r = s->ctx->tlsext_opaque_prf_input_callback(s, NULL, 0, s->ctx->tlsext_opaque_prf_input_callback_arg);
+                       if (!r)
+                               return -1;
+                       }
+
+               if (s->tlsext_opaque_prf_input != NULL)
+                       {
+                       if (s->s3->client_opaque_prf_input != NULL) /* shouldn't really happen */
+                               OPENSSL_free(s->s3->client_opaque_prf_input);
+
+                       s->s3->client_opaque_prf_input = BUF_memdup(s->tlsext_opaque_prf_input, s->tlsext_opaque_prf_input_len);
+                       if (s->s3->client_opaque_prf_input == NULL)
+                               {
+                               SSLerr(SSL_F_SSL_PREPARE_CLIENTHELLO_TLSEXT,ERR_R_MALLOC_FAILURE);
+                               return -1;
+                               }
+                       s->s3->client_opaque_prf_input_len = s->tlsext_opaque_prf_input_len;
+                       }
+
+               if (r == 2)
+                       /* at callback's request, insist on receiving an appropriate server opaque PRF input */
+                       s->s3->server_opaque_prf_input_len = s->tlsext_opaque_prf_input_len;
+       }
+#endif
+
        return 1;
        }
 
@@ -810,6 +935,7 @@ int ssl_prepare_serverhello_tlsext(SSL *s)
                s->tlsext_ecpointformatlist[2] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2;
                }
 #endif /* OPENSSL_NO_EC */
+
        return 1;
        }
 
@@ -832,6 +958,62 @@ int ssl_check_clienthello_tlsext(SSL *s)
        else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0)             
                ret = s->initial_ctx->tlsext_servername_callback(s, &al, s->initial_ctx->tlsext_servername_arg);
 
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+       {
+               /* This sort of belongs into ssl_prepare_serverhello_tlsext(),
+                * but we might be sending an alert in response to the client hello,
+                * so this has to happen here in ssl_check_clienthello_tlsext(). */
+
+               int r = 1;
+       
+               if (s->ctx->tlsext_opaque_prf_input_callback != 0)
+                       {
+                       r = s->ctx->tlsext_opaque_prf_input_callback(s, NULL, 0, s->ctx->tlsext_opaque_prf_input_callback_arg);
+                       if (!r)
+                               {
+                               ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+                               al = SSL_AD_INTERNAL_ERROR;
+                               goto err;
+                               }
+                       }
+
+               if (s->s3->server_opaque_prf_input != NULL) /* shouldn't really happen */
+                       OPENSSL_free(s->s3->server_opaque_prf_input);
+               s->s3->server_opaque_prf_input = NULL;
+
+               if (s->tlsext_opaque_prf_input != NULL)
+                       {
+                       if (s->s3->client_opaque_prf_input != NULL &&
+                               s->s3->client_opaque_prf_input_len == s->tlsext_opaque_prf_input_len)
+                               {
+                               /* can only use this extension if we have a server opaque PRF input
+                                * of the same length as the client opaque PRF input! */
+
+                               s->s3->server_opaque_prf_input = BUF_memdup(s->tlsext_opaque_prf_input, s->tlsext_opaque_prf_input_len);
+                               if (s->s3->server_opaque_prf_input == NULL)
+                                       {
+                                       ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+                                       al = SSL_AD_INTERNAL_ERROR;
+                                       goto err;
+                                       }
+                               s->s3->server_opaque_prf_input_len = s->tlsext_opaque_prf_input_len;
+                               }
+                       }
+
+               if (r == 2 && s->s3->server_opaque_prf_input == NULL)
+                       {
+                       /* The callback wants to enforce use of the extension,
+                        * but we can't do that with the client opaque PRF input;
+                        * abort the handshake.
+                        */
+                       ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+                       al = SSL_AD_HANDSHAKE_FAILURE;
+                       }
+       }
+#endif
+
+ err:
        switch (ret)
                {
                case SSL_TLSEXT_ERR_ALERT_FATAL:
@@ -895,6 +1077,29 @@ int ssl_check_serverhello_tlsext(SSL *s)
        else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0)             
                ret = s->initial_ctx->tlsext_servername_callback(s, &al, s->initial_ctx->tlsext_servername_arg);
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+       if (s->s3->server_opaque_prf_input_len > 0)
+               {
+               /* This case may indicate that we, as a client, want to insist on using opaque PRF inputs.
+                * So first verify that we really have a value from the server too. */
+
+               if (s->s3->server_opaque_prf_input == NULL)
+                       {
+                       ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+                       al = SSL_AD_HANDSHAKE_FAILURE;
+                       }
+               
+               /* Anytime the server *has* sent an opaque PRF input, we need to check
+                * that we have a client opaque PRF input of the same size. */
+               if (s->s3->client_opaque_prf_input == NULL ||
+                   s->s3->client_opaque_prf_input_len != s->s3->server_opaque_prf_input_len)
+                       {
+                       ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+                       al = SSL_AD_ILLEGAL_PARAMETER;
+                       }
+               }
+#endif
+
        switch (ret)
                {
                case SSL_TLSEXT_ERR_ALERT_FATAL: