From 7b2f3298f7eb7ce5cfd1c3eb55b1ecc89118f52c Mon Sep 17 00:00:00 2001 From: jsing <> Date: Wed, 10 Dec 2014 15:24:01 +0000 Subject: Add ALPN support to openssl(1). Based on OpenSSL. --- src/usr.bin/openssl/s_client.c | 32 ++++++++++++++++-- src/usr.bin/openssl/s_server.c | 74 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/usr.bin/openssl/s_client.c b/src/usr.bin/openssl/s_client.c index 94e24dacaa..4476852cdb 100644 --- a/src/usr.bin/openssl/s_client.c +++ b/src/usr.bin/openssl/s_client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: s_client.c,v 1.9 2014/12/02 19:44:49 deraadt Exp $ */ +/* $OpenBSD: s_client.c,v 1.10 2014/12/10 15:24:01 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -253,6 +253,7 @@ 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 + BIO_printf(bio_err, " -alpn arg - enable ALPN extension, considering named protocols supported (comma-separated list)\n"); #ifndef OPENSSL_NO_SRTP BIO_printf(bio_err, " -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n"); #endif @@ -374,6 +375,7 @@ s_client_main(int argc, char **argv) #ifndef OPENSSL_NO_NEXTPROTONEG const char *next_proto_neg_in = NULL; #endif + const char *alpn_in = NULL; char *sess_in = NULL; char *sess_out = NULL; struct sockaddr peer; @@ -544,7 +546,11 @@ s_client_main(int argc, char **argv) next_proto_neg_in = *(++argv); } #endif - else if (strcmp(*argv, "-serverpref") == 0) + else if (strcmp(*argv, "-alpn") == 0) { + if (--argc < 1) + goto bad; + alpn_in = *(++argv); + } else if (strcmp(*argv, "-serverpref") == 0) off |= SSL_OP_CIPHER_SERVER_PREFERENCE; else if (strcmp(*argv, "-legacy_renegotiation") == 0) ; /* no-op */ @@ -736,6 +742,17 @@ bad: if (next_proto.data) SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); #endif + if (alpn_in) { + unsigned short alpn_len; + unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); + + if (alpn == NULL) { + BIO_printf(bio_err, "Error parsing -alpn argument\n"); + goto end; + } + SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len); + free(alpn); + } if (state) SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); @@ -1423,6 +1440,17 @@ print_stuff(BIO * bio, SSL * s, int full) BIO_write(bio, "\n", 1); } #endif + { + const unsigned char *proto; + unsigned int proto_len; + SSL_get0_alpn_selected(s, &proto, &proto_len); + if (proto_len > 0) { + BIO_printf(bio, "ALPN protocol: "); + BIO_write(bio, proto, proto_len); + BIO_write(bio, "\n", 1); + } else + BIO_printf(bio, "No ALPN negotiated\n"); + } #ifndef OPENSSL_NO_SRTP { diff --git a/src/usr.bin/openssl/s_server.c b/src/usr.bin/openssl/s_server.c index b3cdb30a61..35ed6d169c 100644 --- a/src/usr.bin/openssl/s_server.c +++ b/src/usr.bin/openssl/s_server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: s_server.c,v 1.7 2014/12/02 19:44:49 deraadt Exp $ */ +/* $OpenBSD: s_server.c,v 1.8 2014/12/10 15:24:01 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -351,6 +351,7 @@ sv_usage(void) #ifndef OPENSSL_NO_NEXTPROTONEG BIO_printf(bio_err, " -nextprotoneg arg - set the advertised protocols for the NPN extension (comma-separated list)\n"); #endif + BIO_printf(bio_err," -alpn arg - set the advertised protocols for the ALPN extension (comma-separated list)\n"); #ifndef OPENSSL_NO_SRTP BIO_printf(bio_err, " -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n"); #endif @@ -545,6 +546,45 @@ next_proto_cb(SSL * s, const unsigned char **data, unsigned int *len, void *arg) #endif /* ndef OPENSSL_NO_NEXTPROTONEG */ +/* This the context that we pass to alpn_cb */ +typedef struct tlsextalpnctx_st { + unsigned char *data; + unsigned short len; +} tlsextalpnctx; + +static int +alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ + tlsextalpnctx *alpn_ctx = arg; + + if (!s_quiet) { + /* We can assume that in is syntactically valid. */ + unsigned i; + + BIO_printf(bio_s_out, + "ALPN protocols advertised by the client: "); + for (i = 0; i < inlen; ) { + if (i) + BIO_write(bio_s_out, ", ", 2); + BIO_write(bio_s_out, &in[i + 1], in[i]); + i += in[i] + 1; + } + BIO_write(bio_s_out, "\n", 1); + } + + if (SSL_select_next_proto((unsigned char**)out, outlen, alpn_ctx->data, + alpn_ctx->len, in, inlen) != OPENSSL_NPN_NEGOTIATED) + return (SSL_TLSEXT_ERR_NOACK); + + if (!s_quiet) { + BIO_printf(bio_s_out, "ALPN protocols selected: "); + BIO_write(bio_s_out, *out, *outlen); + BIO_write(bio_s_out, "\n", 1); + } + + return (SSL_TLSEXT_ERR_OK); +} int s_server_main(int, char **); @@ -583,8 +623,10 @@ s_server_main(int argc, char *argv[]) tlsextctx tlsextcbp = {NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING}; #ifndef OPENSSL_NO_NEXTPROTONEG const char *next_proto_neg_in = NULL; - tlsextnextprotoctx next_proto; + tlsextnextprotoctx next_proto = { NULL, 0 }; #endif + const char *alpn_in = NULL; + tlsextalpnctx alpn_ctx = { NULL, 0 }; meth = SSLv23_server_method(); local_argc = argc; @@ -838,6 +880,11 @@ s_server_main(int argc, char *argv[]) next_proto_neg_in = *(++argv); } #endif + else if (strcmp(*argv,"-alpn") == 0) { + if (--argc < 1) + goto bad; + alpn_in = *(++argv); + } #ifndef OPENSSL_NO_SRTP else if (strcmp(*argv, "-use_srtp") == 0) { if (--argc < 1) @@ -916,7 +963,7 @@ bad: } } } -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) +#if !defined(OPENSSL_NO_NEXTPROTONEG) if (next_proto_neg_in) { unsigned short len; next_proto.data = next_protos_parse(&len, next_proto_neg_in); @@ -927,7 +974,14 @@ bad: next_proto.data = NULL; } #endif - + alpn_ctx.data = NULL; + if (alpn_in) { + unsigned short len; + alpn_ctx.data = next_protos_parse(&len, alpn_in); + if (alpn_ctx.data == NULL) + goto end; + alpn_ctx.len = len; + } if (s_dcert_file) { @@ -957,8 +1011,7 @@ bad: bio_s_out = BIO_new_fp(stdout, BIO_NOCLOSE); } } - if (nocert) - { + if (nocert) { s_cert_file = NULL; s_key_file = NULL; s_dcert_file = NULL; @@ -1076,6 +1129,8 @@ bad: if (next_proto.data) SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, &next_proto); #endif + if (alpn_ctx.data) + SSL_CTX_set_alpn_select_cb(ctx, alpn_cb, &alpn_ctx); #ifndef OPENSSL_NO_DH if (!no_dhe) { @@ -1241,6 +1296,8 @@ end: X509_free(s_cert2); if (s_key2) EVP_PKEY_free(s_key2); + free(next_proto.data); + free(alpn_ctx.data); if (bio_s_out != NULL) { BIO_free(bio_s_out); bio_s_out = NULL; @@ -1603,13 +1660,12 @@ init_ssl_connection(SSL * con) X509 *peer; long verify_error; char buf[BUFSIZ]; -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) +#if !defined(OPENSSL_NO_NEXTPROTONEG) const unsigned char *next_proto_neg; unsigned next_proto_neg_len; #endif unsigned char *exportedkeymat; - i = SSL_accept(con); if (i <= 0) { if (BIO_sock_should_retry(i)) { @@ -1642,7 +1698,7 @@ init_ssl_connection(SSL * con) str = SSL_CIPHER_get_name(SSL_get_current_cipher(con)); BIO_printf(bio_s_out, "CIPHER is %s\n", (str != NULL) ? str : "(NONE)"); -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) +#if !defined(OPENSSL_NO_NEXTPROTONEG) SSL_get0_next_proto_negotiated(con, &next_proto_neg, &next_proto_neg_len); if (next_proto_neg) { BIO_printf(bio_s_out, "NEXTPROTO is "); -- cgit v1.2.3-55-g6feb