Fix memory leak.
[openssl.git] / ssl / t1_lib.c
index f62a004cf2eb6c200ac0c65a4166de89a5e54e1f..5b285995ab34b073b626b37adecabfad4ac49328 100644 (file)
@@ -642,6 +642,19 @@ int tls12_get_req_sig_algs(SSL *s, unsigned char *p)
        return (int)slen;
        }
 
+/* byte_compare is a compare function for qsort(3) that compares bytes. */
+static int byte_compare(const void *in_a, const void *in_b)
+       {
+       unsigned char a = *((const unsigned char*) in_a);
+       unsigned char b = *((const unsigned char*) in_b);
+
+       if (a > b)
+               return 1;
+       else if (a < b)
+               return -1;
+       return 0;
+}
+
 unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned char *limit)
        {
        int extdatalen=0;
@@ -983,7 +996,27 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                 ret += el;
                 }
 
-       if ((extdatalen = ret-p-2)== 0) 
+       /* Add TLS extension Server_Authz_DataFormats to the ClientHello */
+       /* 2 bytes for extension type */
+       /* 2 bytes for extension length */
+       /* 1 byte for the list length */
+       /* 1 byte for the list (we only support audit proofs) */
+       if (s->ctx->tlsext_authz_server_audit_proof_cb != NULL)
+               {
+               size_t lenmax;
+                const unsigned short ext_len = 2;
+                const unsigned char list_len = 1;
+
+               if ((lenmax = limit - ret - 6) < 0) return NULL;
+
+               s2n(TLSEXT_TYPE_server_authz, ret);
+                /* Extension length: 2 bytes */
+               s2n(ext_len, ret);
+               *(ret++) = list_len;
+               *(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof;
+               }
+
+       if ((extdatalen = ret-p-2) == 0)
                return p;
 
        s2n(extdatalen,p);
@@ -1170,6 +1203,79 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
                }
 #endif
 
+       /* If the client supports authz then see whether we have any to offer
+        * to it. */
+       if (s->s3->tlsext_authz_client_types_len)
+               {
+               size_t authz_length;
+               /* By now we already know the new cipher, so we can look ahead
+                * to see whether the cert we are going to send
+                * has any authz data attached to it. */
+               const unsigned char* authz = ssl_get_authz_data(s, &authz_length);
+               const unsigned char* const orig_authz = authz;
+               size_t i;
+               unsigned authz_count = 0;
+
+               /* The authz data contains a number of the following structures:
+                *      uint8_t authz_type
+                *      uint16_t length
+                *      uint8_t data[length]
+                *
+                * First we walk over it to find the number of authz elements. */
+               for (i = 0; i < authz_length; i++)
+                       {
+                       unsigned short length;
+                       unsigned char type;
+
+                       type = *(authz++);
+                       if (memchr(s->s3->tlsext_authz_client_types,
+                                  type,
+                                  s->s3->tlsext_authz_client_types_len) != NULL)
+                               authz_count++;
+
+                       n2s(authz, length);
+                       /* n2s increments authz by 2 */
+                       i += 2;
+                       authz += length;
+                       i += length;
+                       }
+
+               if (authz_count)
+                       {
+                       /* Add TLS extension server_authz to the ServerHello message
+                        * 2 bytes for extension type
+                        * 2 bytes for extension length
+                        * 1 byte for the list length
+                        * n bytes for the list */
+                       const unsigned short ext_len = 1 + authz_count;
+
+                       if ((long)(limit - ret - 4 - ext_len) < 0) return NULL;
+                       s2n(TLSEXT_TYPE_server_authz, ret);
+                       s2n(ext_len, ret);
+                       *(ret++) = authz_count;
+                       s->s3->tlsext_authz_promised_to_client = 1;
+                       }
+
+               authz = orig_authz;
+               for (i = 0; i < authz_length; i++)
+                       {
+                       unsigned short length;
+                       unsigned char type;
+
+                       authz_count++;
+                       type = *(authz++);
+                       if (memchr(s->s3->tlsext_authz_client_types,
+                                  type,
+                                  s->s3->tlsext_authz_client_types_len) != NULL)
+                               *(ret++) = type;
+                       n2s(authz, length);
+                       /* n2s increments authz by 2 */
+                       i += 2;
+                       authz += length;
+                       i += length;
+                       }
+               }
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -1621,7 +1727,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
                else if (type == TLSEXT_TYPE_next_proto_neg &&
-                         s->s3->tmp.finish_md_len == 0)
+                        s->s3->tmp.finish_md_len == 0)
                        {
                        /* We shouldn't accept this extension on a
                         * renegotiation.
@@ -1650,9 +1756,69 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                                return 0;
                         }
 
+               else if (type == TLSEXT_TYPE_server_authz)
+                       {
+                       unsigned char *sdata = data;
+                       unsigned char server_authz_dataformatlist_length;
+
+                       if (size == 0)
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+
+                       server_authz_dataformatlist_length = *(sdata++);
+
+                       if (server_authz_dataformatlist_length != size - 1)
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+
+                       /* Successful session resumption uses the same authz
+                        * information as the original session so we ignore this
+                        * in the case of a session resumption. */
+                       if (!s->hit)
+                               {
+                               size_t i;
+                               if (s->s3->tlsext_authz_client_types != NULL)
+                                       OPENSSL_free(s->s3->tlsext_authz_client_types);
+                               s->s3->tlsext_authz_client_types =
+                                       OPENSSL_malloc(server_authz_dataformatlist_length);
+                               if (!s->s3->tlsext_authz_client_types)
+                                       {
+                                       *al = TLS1_AD_INTERNAL_ERROR;
+                                       return 0;
+                                       }
+
+                               s->s3->tlsext_authz_client_types_len =
+                                       server_authz_dataformatlist_length;
+                               memcpy(s->s3->tlsext_authz_client_types,
+                                      sdata,
+                                      server_authz_dataformatlist_length);
+
+                               /* Sort the types in order to check for duplicates. */
+                               qsort(s->s3->tlsext_authz_client_types,
+                                     server_authz_dataformatlist_length,
+                                     1 /* element size */,
+                                     byte_compare);
+
+                               for (i = 0; i < server_authz_dataformatlist_length; i++)
+                                       {
+                                       if (i > 0 &&
+                                           s->s3->tlsext_authz_client_types[i] ==
+                                             s->s3->tlsext_authz_client_types[i-1])
+                                               {
+                                               *al = TLS1_AD_DECODE_ERROR;
+                                               return 0;
+                                               }
+                                       }
+                               }
+                       }
+
                data+=size;
                }
-                               
+
        *p = data;
 
        ri_check:
@@ -1692,7 +1858,7 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
 /* ssl_next_proto_validate validates a Next Protocol Negotiation block. No
  * elements of zero length are allowed and the set of elements must exactly fill
  * the length of the block. */
-static int ssl_next_proto_validate(unsigned char *d, unsigned len)
+static char ssl_next_proto_validate(unsigned char *d, unsigned len)
        {
        unsigned int off = 0;
 
@@ -1916,7 +2082,46 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
                                 return 0;
                         }
 
-               data+=size;             
+               else if (type == TLSEXT_TYPE_server_authz)
+                       {
+                       /* We only support audit proofs. It's an error to send
+                        * an authz hello extension if the client
+                        * didn't request a proof. */
+                       unsigned char *sdata = data;
+                       unsigned char server_authz_dataformatlist_length;
+
+                       if (!s->ctx->tlsext_authz_server_audit_proof_cb)
+                               {
+                               *al = TLS1_AD_UNSUPPORTED_EXTENSION;
+                               return 0;
+                               }
+
+                       if (!size)
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+
+                       server_authz_dataformatlist_length = *(sdata++);
+                       if (server_authz_dataformatlist_length != size - 1)
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+
+                       /* We only support audit proofs, so a legal ServerHello
+                        * authz list contains exactly one entry. */
+                       if (server_authz_dataformatlist_length != 1 ||
+                               sdata[0] != TLSEXT_AUTHZDATAFORMAT_audit_proof)
+                               {
+                               *al = TLS1_AD_UNSUPPORTED_EXTENSION;
+                               return 0;
+                               }
+
+                       s->s3->tlsext_authz_server_promised = 1;
+                       }
+               data += size;
                }
 
        if (data != d+n)