Add support for arbitrary TLS extensions.
authorTrevor <trevp@trevp.net>
Mon, 13 May 2013 01:55:27 +0000 (18:55 -0700)
committerBen Laurie <ben@links.org>
Wed, 12 Jun 2013 16:01:13 +0000 (17:01 +0100)
Contributed by Trevor Perrin.

17 files changed:
CHANGES
apps/s_client.c
apps/s_server.c
ssl/s3_lib.c
ssl/ssl.h
ssl/ssl3.h
ssl/ssl_cert.c
ssl/ssl_err.c
ssl/ssl_lib.c
ssl/ssl_locl.h
ssl/ssl_rsa.c
ssl/ssltest.c
ssl/t1_lib.c
test/Makefile
test/serverinfo.pem [new file with mode: 0644]
test/testssl
util/pl/unix.pl

diff --git a/CHANGES b/CHANGES
index 63a85fbc1d05f0c42712636ab7f36cc4d7c8c378..73f4a57c867a4e88b8b4c4748532a04bc85bd20d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,9 @@
 
  Changes between 1.0.x and 1.1.0  [xx XXX xxxx]
 
 
  Changes between 1.0.x and 1.1.0  [xx XXX xxxx]
 
+  *) Add callbacks for arbitrary TLS extensions.
+     [Trevor Perrin <trevp@trevp.net> and Ben Laurie]
+
   *) Support for DTLS 1.2. This adds two sets of DTLS methods: DTLS_*_method()
      supports both DTLS 1.2 and 1.0 and should use whatever version the peer
      supports and DTLSv1_2_*_method() which supports DTLS 1.2 only.
   *) Support for DTLS 1.2. This adds two sets of DTLS methods: DTLS_*_method()
      supports both DTLS 1.2 and 1.0 and should use whatever version the peer
      supports and DTLSv1_2_*_method() which supports DTLS 1.2 only.
index 45f6ced044b6d638411e14b007c3b6f77efd3c2f..5fb5dffda7cb3b528409805cfa6f8c9e36f64035 100644 (file)
@@ -365,6 +365,9 @@ static void sc_usage(void)
 # ifndef OPENSSL_NO_NEXTPROTONEG
        BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n");
 # endif
 # ifndef OPENSSL_NO_NEXTPROTONEG
        BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n");
 # endif
+#ifndef OPENSSL_NO_TLSEXT
+       BIO_printf(bio_err," -serverinfo types - send empty ClientHello extensions (comma-separated numbers)\n");
+#endif
 #endif
        BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
        BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n");
 #endif
        BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
        BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n");
@@ -542,6 +545,26 @@ static int next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, con
        return SSL_TLSEXT_ERR_OK;
        }
 # endif  /* ndef OPENSSL_NO_NEXTPROTONEG */
        return SSL_TLSEXT_ERR_OK;
        }
 # endif  /* ndef OPENSSL_NO_NEXTPROTONEG */
+
+static int serverinfo_cli_cb(SSL* s, unsigned short ext_type,
+                            const unsigned char* in, unsigned short inlen, 
+                            int* al, void* arg)
+       {
+       char pem_name[100];
+       unsigned char ext_buf[4 + 65536];
+
+       /* Reconstruct the type/len fields prior to extension data */
+       ext_buf[0] = ext_type >> 8;
+       ext_buf[1] = ext_type & 0xFF;
+       ext_buf[2] = inlen >> 8;
+       ext_buf[3] = inlen & 0xFF;
+       memcpy(ext_buf+4, in, inlen);
+
+       BIO_snprintf(pem_name, sizeof(pem_name), "SERVER_INFO %d", ext_type);
+       PEM_write_bio(bio_c_out, pem_name, "", ext_buf, 4 + inlen);
+       return 1;
+       }
+
 #endif
 
 enum
 #endif
 
 enum
@@ -614,6 +637,9 @@ int MAIN(int argc, char **argv)
 # ifndef OPENSSL_NO_NEXTPROTONEG
        const char *next_proto_neg_in = NULL;
 # endif
 # ifndef OPENSSL_NO_NEXTPROTONEG
        const char *next_proto_neg_in = NULL;
 # endif
+# define MAX_SI_TYPES 100
+       unsigned short serverinfo_types[MAX_SI_TYPES];
+       int serverinfo_types_count = 0;
 #endif
        char *sess_in = NULL;
        char *sess_out = NULL;
 #endif
        char *sess_in = NULL;
        char *sess_out = NULL;
@@ -968,6 +994,29 @@ static char *jpake_secret = NULL;
                        next_proto_neg_in = *(++argv);
                        }
 # endif
                        next_proto_neg_in = *(++argv);
                        }
 # endif
+               else if (strcmp(*argv,"-serverinfo") == 0)
+                       {
+                       char *c;
+                       int start = 0;
+                       int len;
+
+                       if (--argc < 1) goto bad;
+                       c = *(++argv);
+                       serverinfo_types_count = 0;
+                       len = strlen(c);
+                       for (i = 0; i <= len; ++i)
+                               {
+                               if (i == len || c[i] == ',')
+                                       {
+                                       serverinfo_types[serverinfo_types_count]
+                                           = atoi(c+start);
+                                       serverinfo_types_count++;
+                                       start = i+1;
+                                       }
+                               if (serverinfo_types_count == MAX_SI_TYPES)
+                                       break;
+                               }
+                       }
 #endif
 #ifdef FIONBIO
                else if (strcmp(*argv,"-nbio") == 0)
 #endif
 #ifdef FIONBIO
                else if (strcmp(*argv,"-nbio") == 0)
@@ -1261,6 +1310,19 @@ bad:
        if (next_proto.data)
                SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
 #endif
        if (next_proto.data)
                SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
 #endif
+#ifndef OPENSSL_NO_TLSEXT
+               if (serverinfo_types_count)
+                       {
+                       for (i = 0; i < serverinfo_types_count; i++)
+                               {
+                               SSL_CTX_set_custom_cli_ext(ctx,
+                                                          serverinfo_types[i],
+                                                          NULL, 
+                                                          serverinfo_cli_cb,
+                                                          NULL);
+                               }
+                       }
+#endif
 
        if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
 #if 0
 
        if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
 #if 0
index 657b042d07952d4fecaa1c67b20d54c340705fbb..bf61a8d09eefb9482576017b09ac22e777b90a84 100644 (file)
@@ -318,6 +318,8 @@ static int cert_chain = 0;
 #ifndef OPENSSL_NO_TLSEXT
 static BIO *authz_in = NULL;
 static const char *s_authz_file = NULL;
 #ifndef OPENSSL_NO_TLSEXT
 static BIO *authz_in = NULL;
 static const char *s_authz_file = NULL;
+static BIO *serverinfo_in = NULL;
+static const char *s_serverinfo_file = NULL;
 #endif
 
 #ifndef OPENSSL_NO_PSK
 #endif
 
 #ifndef OPENSSL_NO_PSK
@@ -479,6 +481,9 @@ static void sv_usage(void)
        BIO_printf(bio_err," -cert arg     - certificate file to use\n");
        BIO_printf(bio_err,"                 (default is %s)\n",TEST_CERT);
        BIO_printf(bio_err," -authz arg   -  binary authz file for certificate\n");
        BIO_printf(bio_err," -cert arg     - certificate file to use\n");
        BIO_printf(bio_err,"                 (default is %s)\n",TEST_CERT);
        BIO_printf(bio_err," -authz arg   -  binary authz file for certificate\n");
+#ifndef OPENSSL_NO_TLSEXT
+       BIO_printf(bio_err," -serverinfo arg - PEM serverinfo file for certificate\n");
+#endif
        BIO_printf(bio_err," -crl_check    - check the peer certificate has not been revoked by its CA.\n" \
                           "                 The CRL(s) are appended to the certificate file\n");
        BIO_printf(bio_err," -crl_check_all - check the peer certificate has not been revoked by its CA\n" \
        BIO_printf(bio_err," -crl_check    - check the peer certificate has not been revoked by its CA.\n" \
                           "                 The CRL(s) are appended to the certificate file\n");
        BIO_printf(bio_err," -crl_check_all - check the peer certificate has not been revoked by its CA\n" \
@@ -1093,6 +1098,11 @@ int MAIN(int argc, char *argv[])
                        if (--argc < 1) goto bad;
                        s_authz_file = *(++argv);
                        }
                        if (--argc < 1) goto bad;
                        s_authz_file = *(++argv);
                        }
+               else if (strcmp(*argv,"-serverinfo") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       s_serverinfo_file = *(++argv);
+                       }
 #endif
                else if (strcmp(*argv,"-certform") == 0)
                        {
 #endif
                else if (strcmp(*argv,"-certform") == 0)
                        {
@@ -1853,6 +1863,9 @@ bad:
 #ifndef OPENSSL_NO_TLSEXT
        if (s_authz_file != NULL && !SSL_CTX_use_authz_file(ctx, s_authz_file))
                goto end;
 #ifndef OPENSSL_NO_TLSEXT
        if (s_authz_file != NULL && !SSL_CTX_use_authz_file(ctx, s_authz_file))
                goto end;
+       if (s_serverinfo_file != NULL
+           && !SSL_CTX_use_serverinfo_file(ctx, s_serverinfo_file))
+               goto end;
 #endif
 #ifndef OPENSSL_NO_TLSEXT
        if (ctx2 && !set_cert_key_stuff(ctx2,s_cert2,s_key2, NULL, build_chain))
 #endif
 #ifndef OPENSSL_NO_TLSEXT
        if (ctx2 && !set_cert_key_stuff(ctx2,s_cert2,s_key2, NULL, build_chain))
@@ -2032,6 +2045,8 @@ end:
                EVP_PKEY_free(s_key2);
        if (authz_in != NULL)
                BIO_free(authz_in);
                EVP_PKEY_free(s_key2);
        if (authz_in != NULL)
                BIO_free(authz_in);
+       if (serverinfo_in != NULL)
+               BIO_free(serverinfo_in);
 #endif
        ssl_excert_free(exc);
        if (ssl_args)
 #endif
        ssl_excert_free(exc);
        if (ssl_args)
index 7ad8a541faacc114c879c1313bb96cf831928133..67eb92089e0b658d2e8d967db5efa87ad44f72f9 100644 (file)
@@ -3026,6 +3026,8 @@ void ssl3_free(SSL *s)
 #ifndef OPENSSL_NO_TLSEXT
        if (s->s3->tlsext_authz_client_types != NULL)
                OPENSSL_free(s->s3->tlsext_authz_client_types);
 #ifndef OPENSSL_NO_TLSEXT
        if (s->s3->tlsext_authz_client_types != NULL)
                OPENSSL_free(s->s3->tlsext_authz_client_types);
+       if (s->s3->tlsext_custom_types != NULL)
+               OPENSSL_free(s->s3->tlsext_custom_types);
 #endif
        OPENSSL_cleanse(s->s3,sizeof *s->s3);
        OPENSSL_free(s->s3);
 #endif
        OPENSSL_cleanse(s->s3,sizeof *s->s3);
        OPENSSL_free(s->s3);
@@ -3076,6 +3078,12 @@ void ssl3_clear(SSL *s)
                OPENSSL_free(s->s3->tlsext_authz_client_types);
                s->s3->tlsext_authz_client_types = NULL;
                }
                OPENSSL_free(s->s3->tlsext_authz_client_types);
                s->s3->tlsext_authz_client_types = NULL;
                }
+       if (s->s3->tlsext_custom_types != NULL)
+               {
+               OPENSSL_free(s->s3->tlsext_custom_types);
+               s->s3->tlsext_custom_types = NULL;
+               }
+       s->s3->tlsext_custom_types_count = 0;   
 #endif
 
        rp = s->s3->rbuf.buf;
 #endif
 
        rp = s->s3->rbuf.buf;
index e14a8d4fdb373be3c794e2fa13d24742ecee37d1..67397cf470f621d1bcce27a5bdf911f064cca9c7 100644 (file)
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -383,6 +383,56 @@ DECLARE_STACK_OF(SRTP_PROTECTION_PROFILE)
 typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
 typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
 
 typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
 typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
 
+#ifndef OPENSSL_NO_TLSEXT
+/* Callbacks and structures for handling custom TLS Extensions: 
+ *   cli_ext_first_cb  - sends data for ClientHello TLS Extension
+ *   cli_ext_second_cb - receives data from ServerHello TLS Extension
+ *   srv_ext_first_cb  - receives data from ClientHello TLS Extension
+ *   srv_ext_second_cb - sends data for ServerHello TLS Extension
+ *
+ *   All these functions return nonzero on success.  Zero will terminate
+ *   the handshake (and return a specific TLS Fatal alert, if the function
+ *   declaration has an "al" parameter).
+ * 
+ *   "ext_type" is a TLS "ExtensionType" from 0-65535.
+ *   "in" is a pointer to TLS "extension_data" being provided to the cb.
+ *   "out" is used by the callback to return a pointer to "extension data"
+ *     which OpenSSL will later copy into the TLS handshake.  The contents
+ *     of this buffer should not be changed until the handshake is complete.
+ *   "inlen" and "outlen" are TLS Extension lengths from 0-65535.
+ *   "al" is a TLS "AlertDescription" from 0-255 which WILL be sent as a 
+ *     fatal TLS alert, if the callback returns zero.
+ */
+typedef int (*custom_cli_ext_first_cb_fn)(SSL *s, unsigned short ext_type,
+                                         const unsigned char **out,
+                                         unsigned short *outlen, void *arg);
+typedef int (*custom_cli_ext_second_cb_fn)(SSL *s, unsigned short ext_type,
+                                          const unsigned char *in,
+                                          unsigned short inlen, int *al,
+                                          void *arg); 
+
+typedef int (*custom_srv_ext_first_cb_fn)(SSL *s, unsigned short ext_type,
+                                         const unsigned char *in,
+                                         unsigned short inlen, int *al,
+                                         void *arg);
+typedef int (*custom_srv_ext_second_cb_fn)(SSL *s, unsigned short ext_type,
+                                          const unsigned char **out,
+                                          unsigned short *outlen, void *arg); 
+
+typedef struct {
+       unsigned short ext_type;
+       custom_cli_ext_first_cb_fn fn1; 
+       custom_cli_ext_second_cb_fn fn2; 
+       void *arg;
+} custom_cli_ext_record;
+
+typedef struct {
+       unsigned short ext_type;
+       custom_srv_ext_first_cb_fn fn1; 
+       custom_srv_ext_second_cb_fn fn2; 
+       void *arg;
+} custom_srv_ext_record;
+#endif
 
 #ifndef OPENSSL_NO_SSL_INTERN
 
 
 #ifndef OPENSSL_NO_SSL_INTERN
 
@@ -1064,6 +1114,12 @@ struct ssl_ctx_st
 # endif /* OPENSSL_NO_EC */
        int (*tlsext_authz_server_audit_proof_cb)(SSL *s, void *arg);
        void *tlsext_authz_server_audit_proof_cb_arg;
 # endif /* OPENSSL_NO_EC */
        int (*tlsext_authz_server_audit_proof_cb)(SSL *s, void *arg);
        void *tlsext_authz_server_audit_proof_cb_arg;
+
+       /* Arrays containing the callbacks for custom TLS Extensions. */
+       custom_cli_ext_record *custom_cli_ext_records;
+       size_t custom_cli_ext_records_count;
+       custom_srv_ext_record *custom_srv_ext_records;
+       size_t custom_srv_ext_records_count;
        };
 
 #endif
        };
 
 #endif
@@ -1170,6 +1226,33 @@ const char *SSL_get_psk_identity_hint(const SSL *s);
 const char *SSL_get_psk_identity(const SSL *s);
 #endif
 
 const char *SSL_get_psk_identity(const SSL *s);
 #endif
 
+#ifndef OPENSSL_NO_TLSEXT
+/* Register callbacks to handle custom TLS Extensions as client or server.
+ * 
+ * Returns nonzero on success.  You cannot register twice for the same 
+ * extension number, and registering for an extension number already 
+ * handled by OpenSSL will succeed, but the callbacks will not be invoked.
+ *
+ * NULL can be registered for any callback function.  For the client
+ * functions, a NULL custom_cli_ext_first_cb_fn sends an empty ClientHello
+ * Extension, and a NULL custom_cli_ext_second_cb_fn ignores the ServerHello
+ * response (if any).
+ *
+ * For the server functions, a NULL custom_srv_ext_first_cb_fn means the
+ * ClientHello extension's data will be ignored, but the extension will still
+ * be noted and custom_srv_ext_second_cb_fn will still be invoked.  If 
+ * custom_srv_ext_second_cb_fn is NULL, an empty ServerHello extension is 
+ * sent.
+ */
+int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type,
+                              custom_cli_ext_first_cb_fn fn1, 
+                              custom_cli_ext_second_cb_fn fn2, void *arg);
+
+int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type,
+                              custom_srv_ext_first_cb_fn fn1, 
+                              custom_srv_ext_second_cb_fn fn2, void *arg);
+#endif
+
 #define SSL_NOTHING    1
 #define SSL_WRITING    2
 #define SSL_READING    3
 #define SSL_NOTHING    1
 #define SSL_WRITING    2
 #define SSL_READING    3
@@ -1934,6 +2017,14 @@ const unsigned char *SSL_CTX_get_authz_data(SSL_CTX *ctx, unsigned char type,
 int    SSL_CTX_use_authz_file(SSL_CTX *ctx, const char *file);
 int    SSL_use_authz_file(SSL *ssl, const char *file);
 #endif
 int    SSL_CTX_use_authz_file(SSL_CTX *ctx, const char *file);
 int    SSL_use_authz_file(SSL *ssl, const char *file);
 #endif
+
+/* Set serverinfo data for the current active cert. */
+int    SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo,
+                              size_t serverinfo_length);
+#ifndef OPENSSL_NO_STDIO
+int    SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file);
+#endif /* NO_STDIO */
+
 #endif
 
 #ifndef OPENSSL_NO_STDIO
 #endif
 
 #ifndef OPENSSL_NO_STDIO
@@ -2481,6 +2572,8 @@ void ERR_load_SSL_strings(void);
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY                         177
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1            178
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE            179
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY                         177
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1            178
 #define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE            179
+#define SSL_F_SSL_CTX_USE_SERVERINFO                    336
+#define SSL_F_SSL_CTX_USE_SERVERINFO_FILE               337
 #define SSL_F_SSL_DO_HANDSHAKE                          180
 #define SSL_F_SSL_GET_NEW_SESSION                       181
 #define SSL_F_SSL_GET_PREV_SESSION                      217
 #define SSL_F_SSL_DO_HANDSHAKE                          180
 #define SSL_F_SSL_GET_NEW_SESSION                       181
 #define SSL_F_SSL_GET_PREV_SESSION                      217
@@ -2655,6 +2748,7 @@ void ERR_load_SSL_strings(void);
 #define SSL_R_INVALID_COMPRESSION_ALGORITHM             341
 #define SSL_R_INVALID_NULL_CMD_NAME                     385
 #define SSL_R_INVALID_PURPOSE                           278
 #define SSL_R_INVALID_COMPRESSION_ALGORITHM             341
 #define SSL_R_INVALID_NULL_CMD_NAME                     385
 #define SSL_R_INVALID_PURPOSE                           278
+#define SSL_R_INVALID_SERVERINFO_DATA                   388
 #define SSL_R_INVALID_SRP_USERNAME                      357
 #define SSL_R_INVALID_STATUS_RESPONSE                   328
 #define SSL_R_INVALID_TICKET_KEYS_LENGTH                325
 #define SSL_R_INVALID_SRP_USERNAME                      357
 #define SSL_R_INVALID_STATUS_RESPONSE                   328
 #define SSL_R_INVALID_TICKET_KEYS_LENGTH                325
index d8ed725d2d7971d0938b1063701407474457560a..77747c4c1efd466a046b8b583cbf64fbfaa227b7 100644 (file)
@@ -577,6 +577,15 @@ typedef struct ssl3_state_st
         * server echoed our server_authz extension and therefore must send us
         * a supplemental data handshake message. */
        char tlsext_authz_server_promised;
         * server echoed our server_authz extension and therefore must send us
         * a supplemental data handshake message. */
        char tlsext_authz_server_promised;
+
+       /* tlsext_custom_types contains an array of TLS Extension types which 
+        * were advertised by the client in its ClientHello, which were not 
+        * otherwise handled by OpenSSL, and which the server has registered
+        * a custom_srv_ext_record to handle.
+        * The array does not contain any duplicates, and is in the same order
+        * as the types were received in the client hello. */
+       unsigned short *tlsext_custom_types;
+       size_t tlsext_custom_types_count; /* how many tlsext_custom_types */
 #endif
        } SSL3_STATE;
 
 #endif
        } SSL3_STATE;
 
index a0da7d3741ac112fba250bac6b48168fcf9f2029..f86511d81c39347b45aa8b9a84882eb9b8370cd9 100644 (file)
@@ -329,7 +329,8 @@ CERT *ssl_cert_dup(CERT *cert)
                                }
                        }
                rpk->valid_flags = 0;
                                }
                        }
                rpk->valid_flags = 0;
-                if (cert->pkeys[i].authz != NULL)
+#ifndef OPENSSL_NO_TLSEXT
+     if (cert->pkeys[i].authz != NULL)
                        {
                        /* Just copy everything. */
                        ret->pkeys[i].authz_length =
                        {
                        /* Just copy everything. */
                        ret->pkeys[i].authz_length =
@@ -339,12 +340,30 @@ CERT *ssl_cert_dup(CERT *cert)
                        if (ret->pkeys[i].authz == NULL)
                                {
                                SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
                        if (ret->pkeys[i].authz == NULL)
                                {
                                SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
-                               return(NULL);
+                               return NULL;
                                }
                        memcpy(ret->pkeys[i].authz,
                               cert->pkeys[i].authz,
                               cert->pkeys[i].authz_length);
                        }
                                }
                        memcpy(ret->pkeys[i].authz,
                               cert->pkeys[i].authz,
                               cert->pkeys[i].authz_length);
                        }
+
+               if (cert->pkeys[i].serverinfo != NULL)
+                       {
+                       /* Just copy everything. */
+                       ret->pkeys[i].serverinfo_length =
+                               cert->pkeys[i].serverinfo_length;
+                       ret->pkeys[i].serverinfo =
+                               OPENSSL_malloc(ret->pkeys[i].serverinfo_length);
+                       if (ret->pkeys[i].serverinfo == NULL)
+                               {
+                               SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
+                               return NULL;
+                               }
+                       memcpy(ret->pkeys[i].serverinfo,
+                              cert->pkeys[i].serverinfo,
+                              cert->pkeys[i].serverinfo_length);
+                       }
+#endif
                }
        
        ret->references=1;
                }
        
        ret->references=1;
@@ -460,8 +479,16 @@ void ssl_cert_clear_certs(CERT *c)
                        cpk->chain = NULL;
                        }
 #ifndef OPENSSL_NO_TLSEXT
                        cpk->chain = NULL;
                        }
 #ifndef OPENSSL_NO_TLSEXT
-                if (cpk->authz != NULL)
+               if (cpk->authz)
+                       {
                        OPENSSL_free(cpk->authz);
                        OPENSSL_free(cpk->authz);
+                       cpk->authz = NULL;
+                       }
+               if (cpk->serverinfo)
+                       {
+                       OPENSSL_free(cpk->serverinfo);
+                       cpk->serverinfo = NULL;
+                       }
 #endif
                /* Clear all flags apart from explicit sign */
                cpk->valid_flags &= CERT_PKEY_EXPLICIT_SIGN;
 #endif
                /* Clear all flags apart from explicit sign */
                cpk->valid_flags &= CERT_PKEY_EXPLICIT_SIGN;
index d8b4d70c13e16c89fb63e35959be176daabe8321..2a6ae5b993f216bf6313702059e9059e83599b4d 100644 (file)
@@ -233,6 +233,8 @@ static ERR_STRING_DATA SSL_str_functs[]=
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY),    "SSL_CTX_use_RSAPrivateKey"},
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1),       "SSL_CTX_use_RSAPrivateKey_ASN1"},
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE),       "SSL_CTX_use_RSAPrivateKey_file"},
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY),    "SSL_CTX_use_RSAPrivateKey"},
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1),       "SSL_CTX_use_RSAPrivateKey_ASN1"},
 {ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE),       "SSL_CTX_use_RSAPrivateKey_file"},
+{ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO),       "SSL_CTX_use_serverinfo"},
+{ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO_FILE),  "SSL_CTX_use_serverinfo_file"},
 {ERR_FUNC(SSL_F_SSL_DO_HANDSHAKE),     "SSL_do_handshake"},
 {ERR_FUNC(SSL_F_SSL_GET_NEW_SESSION),  "ssl_get_new_session"},
 {ERR_FUNC(SSL_F_SSL_GET_PREV_SESSION), "ssl_get_prev_session"},
 {ERR_FUNC(SSL_F_SSL_DO_HANDSHAKE),     "SSL_do_handshake"},
 {ERR_FUNC(SSL_F_SSL_GET_NEW_SESSION),  "ssl_get_new_session"},
 {ERR_FUNC(SSL_F_SSL_GET_PREV_SESSION), "ssl_get_prev_session"},
@@ -410,6 +412,7 @@ static ERR_STRING_DATA SSL_str_reasons[]=
 {ERR_REASON(SSL_R_INVALID_COMPRESSION_ALGORITHM),"invalid compression algorithm"},
 {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME) ,"invalid null cmd name"},
 {ERR_REASON(SSL_R_INVALID_PURPOSE)       ,"invalid purpose"},
 {ERR_REASON(SSL_R_INVALID_COMPRESSION_ALGORITHM),"invalid compression algorithm"},
 {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME) ,"invalid null cmd name"},
 {ERR_REASON(SSL_R_INVALID_PURPOSE)       ,"invalid purpose"},
+{ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA),"invalid serverinfo data"},
 {ERR_REASON(SSL_R_INVALID_SRP_USERNAME)  ,"invalid srp username"},
 {ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE),"invalid status response"},
 {ERR_REASON(SSL_R_INVALID_TICKET_KEYS_LENGTH),"invalid ticket keys length"},
 {ERR_REASON(SSL_R_INVALID_SRP_USERNAME)  ,"invalid srp username"},
 {ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE),"invalid status response"},
 {ERR_REASON(SSL_R_INVALID_TICKET_KEYS_LENGTH),"invalid ticket keys length"},
index c3b40323687cd0ddbd733044b83319150f00c9a8..b08e82688f23a53c36ffc5a3a9d31aa1c64361e3 100644 (file)
@@ -1697,6 +1697,61 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned
        ctx->next_proto_select_cb_arg = arg;
        }
 # endif
        ctx->next_proto_select_cb_arg = arg;
        }
 # endif
+
+int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type,
+                              custom_cli_ext_first_cb_fn fn1, 
+                              custom_cli_ext_second_cb_fn fn2, void* arg)
+       {
+       /* Check for duplicates */
+       size_t i;
+       custom_cli_ext_record* record;
+
+       for (i=0; i < ctx->custom_cli_ext_records_count; i++)
+               if (ext_type == ctx->custom_cli_ext_records[i].ext_type)
+                       return 0;
+
+       ctx->custom_cli_ext_records = OPENSSL_realloc(ctx->custom_cli_ext_records,
+                                                                                                               (ctx->custom_cli_ext_records_count+1) * sizeof(custom_cli_ext_record));
+       if (!ctx->custom_cli_ext_records) {
+               ctx->custom_cli_ext_records_count = 0;
+               return 0;
+       }
+       ctx->custom_cli_ext_records_count++;
+       record = &ctx->custom_cli_ext_records[ctx->custom_cli_ext_records_count - 1];
+       record->ext_type = ext_type;
+       record->fn1 = fn1;
+       record->fn2 = fn2;
+       record->arg = arg;
+       return 1;
+       }
+
+int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type,
+                                                                                                                        custom_srv_ext_first_cb_fn fn1, 
+                                                                                                                        custom_srv_ext_second_cb_fn fn2, void* arg)
+       {
+       /* Check for duplicates */
+       size_t i;
+       custom_srv_ext_record* record;
+
+       for (i=0; i < ctx->custom_srv_ext_records_count; i++)
+               if (ext_type == ctx->custom_srv_ext_records[i].ext_type)
+                       return 0;
+
+       ctx->custom_srv_ext_records = OPENSSL_realloc(ctx->custom_srv_ext_records,
+                                                                                                               (ctx->custom_srv_ext_records_count+1) * sizeof(custom_srv_ext_record));
+       if (!ctx->custom_srv_ext_records) {
+               ctx->custom_srv_ext_records_count = 0;
+               return 0;
+       }
+       ctx->custom_srv_ext_records_count++;
+       record = &ctx->custom_srv_ext_records[ctx->custom_srv_ext_records_count - 1];
+       record->ext_type = ext_type;
+       record->fn1 = fn1;
+       record->fn2 = fn2;
+       record->arg = arg;
+       return 1;
+       }
+
 #endif
 
 int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
 #endif
 
 int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
@@ -1896,6 +1951,10 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
 #ifndef OPENSSL_NO_SRP
        SSL_CTX_SRP_CTX_init(ret);
 #endif
 #ifndef OPENSSL_NO_SRP
        SSL_CTX_SRP_CTX_init(ret);
 #endif
+       ret->custom_cli_ext_records = NULL;
+       ret->custom_cli_ext_records_count = 0;
+       ret->custom_srv_ext_records = NULL;
+       ret->custom_srv_ext_records_count = 0;
 #ifndef OPENSSL_NO_BUF_FREELISTS
        ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
        ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
 #ifndef OPENSSL_NO_BUF_FREELISTS
        ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
        ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
@@ -2034,6 +2093,10 @@ void SSL_CTX_free(SSL_CTX *a)
 #ifndef OPENSSL_NO_SRP
        SSL_CTX_SRP_CTX_free(a);
 #endif
 #ifndef OPENSSL_NO_SRP
        SSL_CTX_SRP_CTX_free(a);
 #endif
+#ifndef OPENSSL_NO_TLSEXT
+       OPENSSL_free(a->custom_cli_ext_records);
+       OPENSSL_free(a->custom_srv_ext_records);
+#endif
 #ifndef OPENSSL_NO_ENGINE
        if (a->client_cert_engine)
                ENGINE_finish(a->client_cert_engine);
 #ifndef OPENSSL_NO_ENGINE
        if (a->client_cert_engine)
                ENGINE_finish(a->client_cert_engine);
@@ -2479,6 +2542,26 @@ unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length)
 
        return c->pkeys[i].authz;
        }
 
        return c->pkeys[i].authz;
        }
+
+int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo,
+                                  size_t *serverinfo_length)
+       {
+       CERT *c = NULL;
+       int i = 0;
+       *serverinfo_length = 0;
+
+       c = s->cert;
+       i = ssl_get_server_cert_index(s);
+
+       if (i == -1)
+               return 0;
+       if (c->pkeys[i].serverinfo == NULL)
+               return 0;
+
+       *serverinfo = c->pkeys[i].serverinfo;
+       *serverinfo_length = c->pkeys[i].serverinfo_length;
+       return 1;
+       }
 #endif
 
 void ssl_update_cache(SSL *s,int mode)
 #endif
 
 void ssl_update_cache(SSL *s,int mode)
index d06f9123433de769d3348cfebe669706f91ee910..56f9b4b260d9a7bf1cc05660aea4dec508af364f 100644 (file)
@@ -510,6 +510,14 @@ typedef struct cert_pkey_st
         *   uint8_t data[length]; */
        unsigned char *authz;
        size_t authz_length;
         *   uint8_t data[length]; */
        unsigned char *authz;
        size_t authz_length;
+
+       /* serverinfo data for this certificate.  The data is in TLS Extension
+        * wire format, specifically it's a series of records like:
+        *   uint16_t extension_type; // (RFC 5246, 7.4.1.4, Extension)
+        *   uint16_t length;
+        *   uint8_t data[length]; */
+       unsigned char *serverinfo;
+       size_t serverinfo_length;
 #endif
        /* Set if CERT_PKEY can be used with current SSL session: e.g.
         * appropriate curve, signature algorithms etc. If zero it can't be
 #endif
        /* Set if CERT_PKEY can be used with current SSL session: e.g.
         * appropriate curve, signature algorithms etc. If zero it can't be
@@ -1000,7 +1008,11 @@ int ssl_undefined_function(SSL *s);
 int ssl_undefined_void_function(void);
 int ssl_undefined_const_function(const SSL *s);
 CERT_PKEY *ssl_get_server_send_pkey(const SSL *s);
 int ssl_undefined_void_function(void);
 int ssl_undefined_const_function(const SSL *s);
 CERT_PKEY *ssl_get_server_send_pkey(const SSL *s);
+#ifndef OPENSSL_NO_TLSEXT
 unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length);
 unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length);
+int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo,
+                                  size_t *serverinfo_length);
+#endif
 EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *c, const EVP_MD **pmd);
 int ssl_cert_type(X509 *x,EVP_PKEY *pkey);
 void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher);
 EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *c, const EVP_MD **pmd);
 int ssl_cert_type(X509 *x,EVP_PKEY *pkey);
 void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher);
index 1babdef3c645e7d1eaa327ad6efb68cb4b27dc14..ac16b8506d6bf7aa2a9065f4e269aacb54240aa4 100644 (file)
@@ -471,6 +471,14 @@ static int ssl_set_cert(CERT *c, X509 *x)
                c->pkeys[i].authz = NULL;
                c->pkeys[i].authz_length = 0;
                }
                c->pkeys[i].authz = NULL;
                c->pkeys[i].authz_length = 0;
                }
+
+       /* Free the old serverinfo data, if it exists. */
+       if (c->pkeys[i].serverinfo != NULL)
+               {
+               OPENSSL_free(c->pkeys[i].serverinfo);
+               c->pkeys[i].serverinfo = NULL;
+               c->pkeys[i].serverinfo_length = 0;
+               }
 #endif
        c->key= &(c->pkeys[i]);
 
 #endif
        c->key= &(c->pkeys[i]);
 
@@ -855,6 +863,116 @@ static char authz_validate(const unsigned char *authz, size_t length)
                }
        }
 
                }
        }
 
+static int serverinfo_find_extension(const unsigned char *serverinfo,
+                                    size_t serverinfo_length,
+                                    unsigned short extension_type,
+                                    const unsigned char **extension_data,
+                                    unsigned short *extension_length)
+       {
+       *extension_data = NULL;
+       *extension_length = 0;
+       if (serverinfo == NULL || serverinfo_length == 0)
+               return 0;
+       for (;;)
+               {
+               unsigned short type = 0; /* uint16 */
+               unsigned short len = 0;  /* uint16 */
+
+               /* end of serverinfo */
+               if (serverinfo_length == 0)
+                       return 0;
+
+               /* read 2-byte type field */
+               if (serverinfo_length < 2)
+                       return 0;       /* error */
+               type = (serverinfo[0] << 8) + serverinfo[1];
+               serverinfo += 2;
+               serverinfo_length -= 2;
+
+               /* read 2-byte len field */
+               if (serverinfo_length < 2)
+                       return 0;       /* error */
+               len = (serverinfo[0] << 8) + serverinfo[1];
+               serverinfo += 2;
+               serverinfo_length -= 2;
+
+               if (len > serverinfo_length)
+                       return 0;       /* error */
+
+               if (type == extension_type)
+                       {
+                       *extension_data = serverinfo;
+                       *extension_length = len;
+                       return 1;
+                       }
+
+               serverinfo += len;
+               serverinfo_length -= len;
+               }
+       return 0;
+       }
+
+static int serverinfo_srv_cb(SSL *s, unsigned short ext_type,
+                            const unsigned char **out, unsigned short *outlen, 
+                            void *arg)
+       {
+       const unsigned char *serverinfo = NULL;
+       size_t serverinfo_length = 0;
+
+       /* Is there a serverinfo for the chosen server cert? */
+       if ((ssl_get_server_cert_serverinfo(s, &serverinfo,
+                                           &serverinfo_length)) != 0)
+               {
+               /* Find the relevant extension from the serverinfo */
+               serverinfo_find_extension(serverinfo, serverinfo_length,
+                                         ext_type, out, outlen);
+               }
+               return 1;
+       }
+
+static int serverinfo_validate(const unsigned char *serverinfo, 
+                              size_t serverinfo_length, SSL_CTX *ctx)
+       {
+       if (serverinfo == NULL || serverinfo_length == 0)
+               return 0;
+       for (;;)
+               {
+               unsigned short ext_type = 0; /* uint16 */
+               unsigned short len = 0;  /* uint16 */
+
+               /* end of serverinfo */
+               if (serverinfo_length == 0)
+                       return 1;
+
+               /* read 2-byte type field */
+               if (serverinfo_length < 2)
+                       return 0;
+               /* FIXME: check for types we understand explicitly? */
+
+               /* Register callbacks for extensions */
+               ext_type = (serverinfo[0] << 8) + serverinfo[1];
+               if (ctx && !SSL_CTX_set_custom_srv_ext(ctx, ext_type, NULL,
+                                                      serverinfo_srv_cb, NULL))
+                       return 0;
+
+               serverinfo += 2;
+               serverinfo_length -= 2;
+
+               /* read 2-byte len field */
+               if (serverinfo_length < 2)
+                       return 0;
+               len = (serverinfo[0] << 8) + serverinfo[1];
+               serverinfo += 2;
+               serverinfo_length -= 2;
+
+               if (len > serverinfo_length)
+                       return 0;
+
+               serverinfo += len;
+               serverinfo_length -= len;
+               }
+       }
+
 static const unsigned char *authz_find_data(const unsigned char *authz,
                                            size_t authz_length,
                                            unsigned char data_type,
 static const unsigned char *authz_find_data(const unsigned char *authz,
                                            size_t authz_length,
                                            unsigned char data_type,
@@ -906,13 +1024,18 @@ static int ssl_set_authz(CERT *c, unsigned char *authz, size_t authz_length)
                return(0);
                }
        current_key->authz = OPENSSL_realloc(current_key->authz, authz_length);
                return(0);
                }
        current_key->authz = OPENSSL_realloc(current_key->authz, authz_length);
+       if (current_key->authz == NULL)
+               {
+               SSLerr(SSL_F_SSL_SET_AUTHZ,ERR_R_MALLOC_FAILURE);
+               return 0;
+               }
        current_key->authz_length = authz_length;
        memcpy(current_key->authz, authz, authz_length);
        return 1;
        }
 
 int SSL_CTX_use_authz(SSL_CTX *ctx, unsigned char *authz,
        current_key->authz_length = authz_length;
        memcpy(current_key->authz, authz, authz_length);
        return 1;
        }
 
 int SSL_CTX_use_authz(SSL_CTX *ctx, unsigned char *authz,
-       size_t authz_length)
+                     size_t authz_length)
        {
        if (authz == NULL)
                {
        {
        if (authz == NULL)
                {
@@ -927,6 +1050,49 @@ int SSL_CTX_use_authz(SSL_CTX *ctx, unsigned char *authz,
        return ssl_set_authz(ctx->cert, authz, authz_length);
        }
 
        return ssl_set_authz(ctx->cert, authz, authz_length);
        }
 
+int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo,
+                          size_t serverinfo_length)
+       {
+       if (ctx == NULL || serverinfo == NULL || serverinfo_length == 0)
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_PASSED_NULL_PARAMETER);
+               return 0;
+               }
+       if (!serverinfo_validate(serverinfo, serverinfo_length, NULL))
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,SSL_R_INVALID_SERVERINFO_DATA);
+               return(0);
+               }
+       if (!ssl_cert_inst(&ctx->cert))
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE);
+               return 0;
+               }
+       if (ctx->cert->key == NULL)
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_INTERNAL_ERROR);
+               return 0;
+               }
+       ctx->cert->key->serverinfo = OPENSSL_realloc(ctx->cert->key->serverinfo,
+                                                                                               serverinfo_length);
+       if (ctx->cert->key->serverinfo == NULL)
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE);
+               return 0;
+               }
+       memcpy(ctx->cert->key->serverinfo, serverinfo, serverinfo_length);
+       ctx->cert->key->serverinfo_length = serverinfo_length;
+
+       /* Now that the serverinfo is validated and stored, go ahead and 
+        * register callbacks. */
+       if (!serverinfo_validate(serverinfo, serverinfo_length, ctx))
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,SSL_R_INVALID_SERVERINFO_DATA);
+               return(0);
+               }
+       return 1;
+       }
+
 int SSL_use_authz(SSL *ssl, unsigned char *authz, size_t authz_length)
        {
        if (authz == NULL)
 int SSL_use_authz(SSL *ssl, unsigned char *authz, size_t authz_length)
        {
        if (authz == NULL)
@@ -1026,5 +1192,81 @@ int SSL_use_authz_file(SSL *ssl, const char *file)
        OPENSSL_free(authz);
        return ret;
        }
        OPENSSL_free(authz);
        return ret;
        }
+
+int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file)
+       {
+       unsigned char *serverinfo = NULL;
+       size_t serverinfo_length = 0;
+       unsigned char* extension = 0;
+       long extension_length = 0;
+       char* name = NULL;
+       char* header = NULL;
+       int ret = 0;
+       BIO *bin = NULL;
+       size_t num_extensions = 0;
+
+       if (ctx == NULL || file == NULL)
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE,ERR_R_PASSED_NULL_PARAMETER);
+               goto end;
+               }
+
+       bin = BIO_new(BIO_s_file_internal());
+       if (bin == NULL)
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_BUF_LIB);
+               goto end;
+               }
+       if (BIO_read_filename(bin, file) <= 0)
+               {
+               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_SYS_LIB);
+               goto end;
+               }
+
+       for (num_extensions=0;; num_extensions++)
+               {
+               if (PEM_read_bio(bin, &name, &header, &extension, &extension_length) == 0)
+                       {
+                       /* There must be at least one extension in this file */
+                       if (num_extensions == 0)
+                               {
+                               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PEM_LIB);
+                               goto end;
+                               }
+                       else /* End of file, we're done */
+                               break;
+                       }
+               /* Check that the decoded PEM data is plausible (valid length field) */
+               if (extension_length < 4 || (extension[2] << 8) + extension[3] != extension_length - 4)
+                       {
+                               SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PEM_LIB);
+                               goto end;
+                       }
+               /* Append the decoded extension to the serverinfo buffer */
+               serverinfo = OPENSSL_realloc(serverinfo, serverinfo_length + extension_length);
+               if (serverinfo == NULL)
+                       {
+                       SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_MALLOC_FAILURE);
+                       goto end;
+                       }
+               memcpy(serverinfo + serverinfo_length, extension, extension_length);
+               serverinfo_length += extension_length;
+
+               OPENSSL_free(name); name = NULL;
+               OPENSSL_free(header); header = NULL;
+               OPENSSL_free(extension); extension = NULL;
+               }
+
+       ret = SSL_CTX_use_serverinfo(ctx, serverinfo, serverinfo_length);
+end:
+       /* SSL_CTX_use_serverinfo makes a local copy of the serverinfo. */
+       OPENSSL_free(name);
+       OPENSSL_free(header);
+       OPENSSL_free(extension);
+       OPENSSL_free(serverinfo);
+       if (bin != NULL)
+               BIO_free(bin);
+       return ret;
+       }
 #endif /* OPENSSL_NO_STDIO */
 #endif /* OPENSSL_NO_TLSEXT */
 #endif /* OPENSSL_NO_STDIO */
 #endif /* OPENSSL_NO_TLSEXT */
index db87b1a35ea1af9f76ae6f6d4cea17c293ac113b..f67c7eab8e1a5515f3f6f8b4076bf57179cc15bd 100644 (file)
@@ -370,6 +370,41 @@ static int verify_npn(SSL *client, SSL *server)
        }
 #endif
 
        }
 #endif
 
+#define SCT_EXT_TYPE 18
+#define TACK_EXT_TYPE 62208
+
+/* These set from cmdline */
+char* serverinfo_file = NULL;
+int serverinfo_sct = 0;
+int serverinfo_tack = 0;
+int serverinfo_sct_seen = 0;
+int serverinfo_tack_seen = 0;
+int serverinfo_other_seen = 0;
+
+static int serverinfo_cli_cb(SSL* s, unsigned short ext_type,
+                                                                                                    const unsigned char* in, unsigned short inlen, 
+                                                                                                    int* al, void* arg)
+       {
+               if (ext_type == SCT_EXT_TYPE)
+                       serverinfo_sct_seen++;
+               else if (ext_type == TACK_EXT_TYPE)
+                       serverinfo_tack_seen++;
+               else
+                       serverinfo_other_seen++;
+               return 1;
+       }
+
+static int verify_serverinfo()
+       {
+       if (serverinfo_sct != serverinfo_sct_seen)
+               return -1;
+       if (serverinfo_tack != serverinfo_tack_seen)
+               return -1;
+       if (serverinfo_other_seen)
+               return -1;
+       return 0;
+       }
+
 static char *cipher=NULL;
 static int verbose=0;
 static int debug=0;
 static char *cipher=NULL;
 static int verbose=0;
 static int debug=0;
@@ -449,6 +484,9 @@ static void sv_usage(void)
        fprintf(stderr," -npn_server - have server side offer NPN\n");
        fprintf(stderr," -npn_server_reject - have server reject NPN\n");
 #endif
        fprintf(stderr," -npn_server - have server side offer NPN\n");
        fprintf(stderr," -npn_server_reject - have server reject NPN\n");
 #endif
+       fprintf(stderr," -serverinfo_file - have server use this file\n");
+       fprintf(stderr," -serverinfo_sct  - have client offer and expect SCT\n");
+       fprintf(stderr," -serverinfo_tack - have client offer and expect TACK\n");
        }
 
 static void print_details(SSL *c_ssl, const char *prefix)
        }
 
 static void print_details(SSL *c_ssl, const char *prefix)
@@ -861,6 +899,19 @@ int main(int argc, char *argv[])
                        npn_server_reject = 1;
                        }
 #endif
                        npn_server_reject = 1;
                        }
 #endif
+               else if (strcmp(*argv,"-serverinfo_sct") == 0)
+                       {
+                       serverinfo_sct = 1;
+                       }
+               else if (strcmp(*argv,"-serverinfo_tack") == 0)
+                       {
+                       serverinfo_tack = 1;
+                       }
+               else if (strcmp(*argv,"-serverinfo_file") == 0)
+                       {
+                       if (--argc < 1) goto bad;
+                       serverinfo_file = *(++argv);
+                       }
                else
                        {
                        fprintf(stderr,"unknown option %s\n",*argv);
                else
                        {
                        fprintf(stderr,"unknown option %s\n",*argv);
@@ -1186,6 +1237,18 @@ bad:
                }
 #endif
 
                }
 #endif
 
+       if (serverinfo_sct)
+               SSL_CTX_set_custom_cli_ext(c_ctx, SCT_EXT_TYPE, NULL, serverinfo_cli_cb, NULL);
+       if (serverinfo_tack)
+               SSL_CTX_set_custom_cli_ext(c_ctx, TACK_EXT_TYPE, NULL, serverinfo_cli_cb, NULL);
+
+       if (serverinfo_file)
+               if (!SSL_CTX_use_serverinfo_file(s_ctx, serverinfo_file))
+                       {
+                       BIO_printf(bio_err, "missing serverinfo file\n");
+                       goto end;
+                       }
+
        c_ssl=SSL_new(c_ctx);
        s_ssl=SSL_new(s_ctx);
 
        c_ssl=SSL_new(c_ctx);
        s_ssl=SSL_new(s_ctx);
 
@@ -1643,6 +1706,12 @@ int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count,
                goto end;
                }
 #endif
                goto end;
                }
 #endif
+       if (verify_serverinfo() < 0)
+               {
+               ret = 1;
+               goto err;
+               }
+
 end:
        ret = 0;
 
 end:
        ret = 0;
 
@@ -1945,6 +2014,11 @@ int doit(SSL *s_ssl, SSL *c_ssl, long count)
                goto err;
                }
 #endif
                goto err;
                }
 #endif
+       if (verify_serverinfo() < 0)
+               {
+               ret = 1;
+               goto err;
+               }
        ret=0;
 err:
        /* We have to set the BIO's to NULL otherwise they will be
        ret=0;
 err:
        /* We have to set the BIO's to NULL otherwise they will be
index 31daa50d3ee6f115fb8a768745aa9532e5a0504a..19769c5888a29cd99f3aadb2d8ef1b7c3939a532 100644 (file)
@@ -1443,6 +1443,31 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                *(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof;
                }
 
                *(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof;
                }
 
+       /* Add custom TLS Extensions to ClientHello */
+       if (s->ctx->custom_cli_ext_records_count)
+               {
+               size_t i;
+               custom_cli_ext_record* record;
+
+               for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
+                       {
+                       const unsigned char* out = NULL;
+                       unsigned short outlen = 0;
+
+                       record = &s->ctx->custom_cli_ext_records[i];
+                       if (record->fn1 && !record->fn1(s, record->ext_type,
+                                                       &out, &outlen,
+                                                       record->arg))
+                               return NULL;
+                       if (limit < ret + 4 + outlen)
+                               return NULL;
+                       s2n(record->ext_type, ret);
+                       s2n(outlen, ret);
+                       memcpy(ret, out, outlen);
+                       ret += outlen;
+                       }
+               }
+
        if ((extdatalen = ret-p-2) == 0)
                return p;
 
        if ((extdatalen = ret-p-2) == 0)
                return p;
 
@@ -1709,6 +1734,40 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
                        }
                }
 
                        }
                }
 
+       /* If custom types were sent in ClientHello, add ServerHello responses */
+       if (s->s3->tlsext_custom_types_count)
+               {
+               size_t i;
+
+               for (i = 0; i < s->s3->tlsext_custom_types_count; i++)
+                       {
+                       size_t j;
+                       custom_srv_ext_record *record;
+
+                       for (j = 0; j < s->ctx->custom_srv_ext_records_count; j++)
+                               {
+                               record = &s->ctx->custom_srv_ext_records[j];
+                               if (s->s3->tlsext_custom_types[i] == record->ext_type)
+                                       {
+                                       const unsigned char *out = NULL;
+                                       unsigned short outlen = 0;
+                                       if (record->fn2
+                                           && !record->fn2(s, record->ext_type,
+                                                           &out, &outlen,
+                                                           record->arg))
+                                               return NULL;
+                                       if (limit < ret + 4 + outlen)
+                                               return NULL;
+                                       s2n(record->ext_type, ret);
+                                       s2n(outlen, ret);
+                                       memcpy(ret, out, outlen);
+                                       ret += outlen;
+                                       break;
+                                       }
+                               }
+                       }
+               }
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -2274,6 +2333,54 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                                }
                        }
 
                                }
                        }
 
+               /* If this ClientHello extension was unhandled and this is 
+                * a nonresumed connection, check whether the extension is a 
+                * custom TLS Extension (has a custom_srv_ext_record), and if
+                * so call the callback and record the extension number so that
+                * an appropriate ServerHello may be later returned.
+                */
+               else if (!s->hit && s->ctx->custom_srv_ext_records_count)
+                       {
+                       custom_srv_ext_record *record;
+
+                       for (i=0; i < s->ctx->custom_srv_ext_records_count; i++)
+                               {
+                               record = &s->ctx->custom_srv_ext_records[i];
+                               if (type == record->ext_type)
+                                       {
+                                       /* Error on duplicate TLS Extensions */
+                                       size_t j;
+
+                                       for (j = 0; j < s->s3->tlsext_custom_types_count; j++)
+                                               {
+                                               if (s->s3->tlsext_custom_types[j] == type)
+                                                       {
+                                                       *al = TLS1_AD_DECODE_ERROR;
+                                                       return 0;
+                                                       }
+                                               }
+
+                                       /* Callback */
+                                       if (record->fn1 && !record->fn1(s, type, data, size, al, record->arg))
+                                               return 0;
+                                               
+                                       /* Add the (non-duplicated) entry */
+                                       s->s3->tlsext_custom_types_count++;
+                                       s->s3->tlsext_custom_types = OPENSSL_realloc(
+                                                       s->s3->tlsext_custom_types,
+                                                       s->s3->tlsext_custom_types_count*2);
+                                       if (s->s3->tlsext_custom_types == NULL)
+                                               {
+                                               s->s3->tlsext_custom_types = 0;
+                                               *al = TLS1_AD_INTERNAL_ERROR;
+                                               return 0;
+                                               }
+                                       s->s3->tlsext_custom_types[
+                                                       s->s3->tlsext_custom_types_count-1] = type;
+                                       }                                               
+                               }
+                       }
+
                data+=size;
                }
 
                data+=size;
                }
 
@@ -2578,6 +2685,26 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
 
                        s->s3->tlsext_authz_server_promised = 1;
                        }
 
                        s->s3->tlsext_authz_server_promised = 1;
                        }
+
+               /* If this extension type was not otherwise handled, but 
+                * matches a custom_cli_ext_record, then send it to the c
+                * callback */
+               else if (s->ctx->custom_cli_ext_records_count)
+                       {
+                       size_t i;
+                       custom_cli_ext_record* record;
+
+                       for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
+                               {
+                               record = &s->ctx->custom_cli_ext_records[i];
+                               if (record->ext_type == type)
+                                       {
+                                       if (record->fn2 && !record->fn2(s, type, data, size, al, record->arg))
+                                               return 0;
+                                       break;
+                                       }
+                               }                       
+                       }
  
                data += size;
                }
  
                data += size;
                }
index f8ffc52f21fdc7399892745e108c17cb8336bda6..52056e28dc78786c288e81b9645333bffd43581f 100644 (file)
@@ -331,7 +331,7 @@ test_engine: $(ENGINETEST)
 
 test_ssl: keyU.ss certU.ss certCA.ss certP1.ss keyP1.ss certP2.ss keyP2.ss \
                intP1.ss intP2.ss $(SSLTEST) testssl testsslproxy \
 
 test_ssl: keyU.ss certU.ss certCA.ss certP1.ss keyP1.ss certP2.ss keyP2.ss \
                intP1.ss intP2.ss $(SSLTEST) testssl testsslproxy \
-               ../apps/server2.pem
+               ../apps/server2.pem serverinfo.pem
        @echo "test SSL protocol"
        ../util/shlib_wrap.sh ./$(SSLTEST) -test_cipherlist
        @sh ./testssl keyU.ss certU.ss certCA.ss
        @echo "test SSL protocol"
        ../util/shlib_wrap.sh ./$(SSLTEST) -test_cipherlist
        @sh ./testssl keyU.ss certU.ss certCA.ss
diff --git a/test/serverinfo.pem b/test/serverinfo.pem
new file mode 100644 (file)
index 0000000..0eb020a
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN SCT-----
+ABIAZMevsj4TC5rgwjZNciLGwh15YXoIK9t5aypGJIG4QzyMowmwwDdqxudkUcGa
+DvuqlYL7psO5j4/BIHTe677CAZBBH3Ho2NOM5q1zub4AbfUMlKeufuQgeQ2Tj1oe
+LJLRzrwDnPs=
+-----END SCT-----
+
+-----BEGIN TACK EXTENSION-----
+8wABTwFMh1Dz+3W6zULWJKjav5TNaFEXL1h98YtCXeyZnORYg4mbKpxH5CMbjpgx
+To3amSqUPF4Ntjc/i9+poutxebYkbgAAAkMcxb8+RaM9YEywaJEGViKJJmpYG/gJ
+HgfGaefI9kKbXSDmP9ntg8dLvDzuyYw14ktM2850Q9WvBiltpekilZxVuT2bFtfs
+cmS++SAK9YOM8RrKhL1TLmrktoBEJZ6z5GTukYdQ8/t1us1C1iSo2r+UzWhRFy9Y
+ffGLQl3smZzkWIOJmyqcR+QjG46YMU6N2pkqlDxeDbY3P4vfqaLrcXm2JG4AAAGN
+xXQJPbdniI9rEydVXb1Cu1yT/t7FBEx6hLxuoypXjCI1wCGpXsd8zEnloR0Ank5h
+VO/874E/BZlItzSPpcmDKl5Def6BrAJTErQlE9npo52S05YWORxJw1+VYBdqQ09A
+x3wA
+-----END TACK EXTENSION-----
index 242cb0a56307da190d88c002d05ba3c00f235bc3..61416885b1ce3d214857c403b506f8c27ef9e29b 100644 (file)
@@ -30,6 +30,8 @@ else
   extra="$4"
 fi
 
   extra="$4"
 fi
 
+serverinfo="./serverinfo.pem"
+
 #############################################################################
 
 echo test sslv2
 #############################################################################
 
 echo test sslv2
@@ -176,6 +178,14 @@ $ssltest -bio_pair -tls1 -npn_client -npn_server || exit 1
 $ssltest -bio_pair -tls1 -npn_client -npn_server -num 2 || exit 1
 $ssltest -bio_pair -tls1 -npn_client -npn_server -num 2 -reuse || exit 1
 
 $ssltest -bio_pair -tls1 -npn_client -npn_server -num 2 || exit 1
 $ssltest -bio_pair -tls1 -npn_client -npn_server -num 2 -reuse || exit 1
 
+#############################################################################
+# Serverinfo tests
+
+$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo || exit 1
+$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_sct || exit 1
+$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_tack || exit 1
+$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_sct -serverinfo_tack || exit 1
+
 if ../util/shlib_wrap.sh ../apps/openssl no-srp; then
   echo skipping SRP tests
 else
 if ../util/shlib_wrap.sh ../apps/openssl no-srp; then
   echo skipping SRP tests
 else
index 40e361b2f98ea661c10093a27f0b2f00f10fbb61..9c258c4df7b33b862d3676eb75a1f7a6524a008e 100644 (file)
@@ -396,6 +396,7 @@ sub get_tests
                 'testss',
                 'testssl',
                 'testsslproxy',
                 'testss',
                 'testssl',
                 'testsslproxy',
+                'serverinfo.pem',
               );
   my $copies = copy_scripts(1, 'test', @copies);
   $copies .= copy_scripts(0, 'test', ('smcont.txt'));
               );
   my $copies = copy_scripts(1, 'test', @copies);
   $copies .= copy_scripts(0, 'test', ('smcont.txt'));