Skip to content
Permalink
Browse files

Add support for arbitrary TLS extensions.

Contributed by Trevor Perrin.
  • Loading branch information...
Trevor authored and benlaurie committed May 13, 2013
1 parent 6d84daa commit a398f821fa98b9923a426cf45b268cf4d56c89bd
Showing with 791 additions and 5 deletions.
  1. +3 −0 CHANGES
  2. +62 −0 apps/s_client.c
  3. +15 −0 apps/s_server.c
  4. +8 −0 ssl/s3_lib.c
  5. +94 −0 ssl/ssl.h
  6. +9 −0 ssl/ssl3.h
  7. +30 −3 ssl/ssl_cert.c
  8. +3 −0 ssl/ssl_err.c
  9. +83 −0 ssl/ssl_lib.c
  10. +12 −0 ssl/ssl_locl.h
  11. +243 −1 ssl/ssl_rsa.c
  12. +74 −0 ssl/ssltest.c
  13. +127 −0 ssl/t1_lib.c
  14. +1 −1 test/Makefile
  15. +16 −0 test/serverinfo.pem
  16. +10 −0 test/testssl
  17. +1 −0 util/pl/unix.pl
@@ -4,6 +4,9 @@

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.
@@ -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_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");
@@ -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 */

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
@@ -614,6 +637,9 @@ int MAIN(int argc, char **argv)
# 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;
@@ -968,6 +994,29 @@ static char *jpake_secret = NULL;
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)
@@ -1261,6 +1310,19 @@ static char *jpake_secret = NULL;
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
@@ -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;
static BIO *serverinfo_in = NULL;
static const char *s_serverinfo_file = NULL;
#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");
#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" \
@@ -1093,6 +1098,11 @@ int MAIN(int argc, char *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)
{
@@ -1853,6 +1863,9 @@ int MAIN(int argc, char *argv[])
#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))
@@ -2032,6 +2045,8 @@ int MAIN(int argc, char *argv[])
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)
@@ -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);
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);
@@ -3076,6 +3078,12 @@ void ssl3_clear(SSL *s)
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;
@@ -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);

#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

@@ -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;

/* 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
@@ -1170,6 +1226,33 @@ const char *SSL_get_psk_identity_hint(const SSL *s);
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
@@ -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

/* 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
@@ -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_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
@@ -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_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
@@ -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;

/* 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;

@@ -329,7 +329,8 @@ CERT *ssl_cert_dup(CERT *cert)
}
}
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 =
@@ -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);
return(NULL);
return NULL;
}
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;
@@ -460,8 +479,16 @@ void ssl_cert_clear_certs(CERT *c)
cpk->chain = NULL;
}
#ifndef OPENSSL_NO_TLSEXT
if (cpk->authz != NULL)
if (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;

0 comments on commit a398f82

Please sign in to comment.
You can’t perform that action at this time.