Contributed by Trevor Perrin.
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.
# 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");
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
# 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;
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)
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
#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
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" \
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)
{
#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))
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)
#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);
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;
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
# 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
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
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
#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_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
* 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;
}
}
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 =
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;
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;
{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_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"},
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,
#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_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);
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)
* 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
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);
+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);
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]);
}
}
+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,
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,
- size_t authz_length)
+ size_t authz_length)
{
if (authz == NULL)
{
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)
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
+#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;
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)
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);
}
#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);
goto end;
}
#endif
+ if (verify_serverinfo() < 0)
+ {
+ ret = 1;
+ goto err;
+ }
+
end:
ret = 0;
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++) = 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 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 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;
}
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;
}
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
--- /dev/null
+-----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-----
extra="$4"
fi
+serverinfo="./serverinfo.pem"
+
#############################################################################
echo test sslv2
$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
'testss',
'testssl',
'testsslproxy',
+ 'serverinfo.pem',
);
my $copies = copy_scripts(1, 'test', @copies);
$copies .= copy_scripts(0, 'test', ('smcont.txt'));