The following diff adds ALPN support to libtls via: tls_config_set_alpn() - set the ALPN protocols supported by this client/server tls_conn_alpn_selected() - get the ALPN protocol selected for this connection
ok? Index: tls.c =================================================================== RCS file: /cvs/src/lib/libtls/tls.c,v retrieving revision 1.41 diff -u -p -r1.41 tls.c --- tls.c 7 Jul 2016 14:09:03 -0000 1.41 +++ tls.c 27 Jul 2016 16:57:06 -0000 @@ -310,6 +310,14 @@ tls_configure_ssl(struct tls *ctx) if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); + if (ctx->config->alpn != NULL) { + if (SSL_CTX_set_alpn_protos(ctx->ssl_ctx, ctx->config->alpn, + ctx->config->alpn_len) != 0) { + tls_set_errorx(ctx, "failed to set alpn"); + goto err; + } + } + if (ctx->config->ciphers != NULL) { if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, ctx->config->ciphers) != 1) { Index: tls.h =================================================================== RCS file: /cvs/src/lib/libtls/tls.h,v retrieving revision 1.29 diff -u -p -r1.29 tls.h --- tls.h 27 May 2016 14:21:24 -0000 1.29 +++ tls.h 27 Jul 2016 16:57:06 -0000 @@ -52,6 +52,7 @@ const char *tls_error(struct tls *_ctx); struct tls_config *tls_config_new(void); void tls_config_free(struct tls_config *_config); +int tls_config_set_alpn(struct tls_config *_config, const char *_alpn); int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file); int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path); int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, @@ -116,8 +117,9 @@ const char *tls_peer_cert_subject(struct time_t tls_peer_cert_notbefore(struct tls *_ctx); time_t tls_peer_cert_notafter(struct tls *_ctx); -const char *tls_conn_version(struct tls *_ctx); +const char *tls_conn_alpn_selected(struct tls *_ctx); const char *tls_conn_cipher(struct tls *_ctx); +const char *tls_conn_version(struct tls *_ctx); uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password); Index: tls_config.c =================================================================== RCS file: /cvs/src/lib/libtls/tls_config.c,v retrieving revision 1.22 diff -u -p -r1.22 tls_config.c --- tls_config.c 13 Jul 2016 16:30:48 -0000 1.22 +++ tls_config.c 27 Jul 2016 16:57:06 -0000 @@ -166,6 +166,7 @@ tls_config_free(struct tls_config *confi free(config->error.msg); + free(config->alpn); free((char *)config->ca_file); free((char *)config->ca_mem); free((char *)config->ca_path); @@ -247,6 +248,72 @@ tls_config_parse_protocols(uint32_t *pro free(s); return (0); +} + +static int +tls_config_parse_alpn(struct tls_config *config, const char *alpn, + char **alpn_data, size_t *alpn_len) +{ + size_t buf_len, i, len; + char *buf = NULL; + char *s = NULL; + char *p, *q; + + if ((buf_len = strlen(alpn) + 1) > 65535) { + tls_config_set_errorx(config, "alpn too large"); + goto err; + } + + if ((buf = malloc(buf_len)) == NULL) { + tls_config_set_errorx(config, "out of memory"); + goto err; + } + + if ((s = strdup(alpn)) == NULL) { + tls_config_set_errorx(config, "out of memory"); + goto err; + } + + i = 0; + q = s; + while ((p = strsep(&q, ",")) != NULL) { + if ((len = strlen(p)) == 0) { + tls_config_set_errorx(config, + "alpn protocol with zero length"); + goto err; + } + if (len > 255) { + tls_config_set_errorx(config, + "alpn protocol too long"); + goto err; + } + buf[i++] = len & 0xff; + memcpy(&buf[i], p, len); + i += len; + } + + free(s); + + *alpn_data = buf; + *alpn_len = buf_len; + + return (0); + + err: + free(buf); + free(s); + + *alpn_data = NULL; + *alpn_len = 0; + + return (-1); +} + +int +tls_config_set_alpn(struct tls_config *config, const char *alpn) +{ + return tls_config_parse_alpn(config, alpn, &config->alpn, + &config->alpn_len); } int Index: tls_conninfo.c =================================================================== RCS file: /cvs/src/lib/libtls/tls_conninfo.c,v retrieving revision 1.5 diff -u -p -r1.5 tls_conninfo.c --- tls_conninfo.c 7 Oct 2015 23:33:38 -0000 1.5 +++ tls_conninfo.c 27 Jul 2016 16:57:06 -0000 @@ -150,6 +150,26 @@ tls_get_peer_cert_times(struct tls *ctx, return (rv); } +static int +tls_conninfo_alpn_proto(struct tls *ctx) +{ + const unsigned char *p; + unsigned int len; + + free(ctx->conninfo->alpn); + ctx->conninfo->alpn = NULL; + + SSL_get0_alpn_selected(ctx->ssl_conn, &p, &len); + if (len > 0) { + if ((ctx->conninfo->alpn = malloc(len + 1)) == NULL) + return (-1); + memcpy(ctx->conninfo->alpn, p, len); + ctx->conninfo->alpn[len] = '\0'; + } + + return (0); +} + int tls_get_conninfo(struct tls *ctx) { const char * tmp; @@ -175,6 +195,9 @@ tls_get_conninfo(struct tls *ctx) { ctx->conninfo->cipher = strdup(tmp); if (ctx->conninfo->cipher == NULL) goto err; + if (tls_conninfo_alpn_proto(ctx) == -1) + goto err; + return (0); err: tls_free_conninfo(ctx->conninfo); @@ -184,6 +207,8 @@ err: void tls_free_conninfo(struct tls_conninfo *conninfo) { if (conninfo != NULL) { + free(conninfo->alpn); + conninfo->alpn = NULL; free(conninfo->hash); conninfo->hash = NULL; free(conninfo->subject); @@ -195,6 +220,14 @@ tls_free_conninfo(struct tls_conninfo *c free(conninfo->cipher); conninfo->cipher = NULL; } +} + +const char * +tls_conn_alpn_selected(struct tls *ctx) +{ + if (ctx->conninfo == NULL) + return (NULL); + return (ctx->conninfo->alpn); } const char * Index: tls_init.3 =================================================================== RCS file: /cvs/src/lib/libtls/tls_init.3,v retrieving revision 1.62 diff -u -p -r1.62 tls_init.3 --- tls_init.3 13 Jul 2016 16:30:48 -0000 1.62 +++ tls_init.3 27 Jul 2016 16:57:06 -0000 @@ -24,6 +24,7 @@ .Nm tls_config_new , .Nm tls_config_free , .Nm tls_config_parse_protocols , +.Nm tls_config_set_alpn , .Nm tls_config_set_ca_file , .Nm tls_config_set_ca_path , .Nm tls_config_set_ca_mem , @@ -54,8 +55,9 @@ .Nm tls_peer_cert_hash , .Nm tls_peer_cert_notbefore , .Nm tls_peer_cert_notafter , -.Nm tls_conn_version , +.Nm tls_conn_alpn_selected , .Nm tls_conn_cipher , +.Nm tls_conn_version , .Nm tls_load_file , .Nm tls_client , .Nm tls_server , @@ -88,6 +90,8 @@ .Ft "int" .Fn tls_config_parse_protocols "uint32_t *protocols" "const char *protostr" .Ft "int" +.Fn tls_config_set_alpn "struct tls_config *config" "const char *alpn" +.Ft "int" .Fn tls_config_set_ca_file "struct tls_config *config" "const char *ca_file" .Ft "int" .Fn tls_config_set_ca_path "struct tls_config *config" "const char *ca_path" @@ -148,9 +152,11 @@ .Ft "time_t" .Fn tls_peer_cert_notafter "struct tls *ctx" .Ft "const char *" -.Fn tls_conn_version "struct tls *ctx" +.Fn tls_conn_alpn_selected "struct tls *ctx" .Ft "const char *" .Fn tls_conn_cipher "struct tls *ctx" +.Ft "const char *" +.Fn tls_conn_version "struct tls *ctx" .Ft "uint8_t *" .Fn tls_load_file "const char *file" "size_t *len" "char *password" .Ft "struct tls *" @@ -295,6 +301,11 @@ The following functions modify a configu Configuration options may apply to only clients or only servers or both. .Bl -bullet -offset four .It +.Fn tls_config_set_alpn +sets the ALPN protocols that are supported. +The alpn string is a comma separated list of protocols, in order of preference. +.Em (Client and Server) +.It .Fn tls_config_set_ca_file sets the filename used to load a file containing the root certificates. @@ -480,13 +491,14 @@ the peer certificate from will only succeed after the handshake is complete. .Em (Server and client) .It -.Fn tls_conn_version -returns a string -corresponding to a TLS version negotiated with the peer +.Fn tls_conn_alpn_selected +returns a string that specifies the ALPN protocol selected for use with the peer connected to .Ar ctx . -.Fn tls_conn_version +If no protocol was selected then NULL is returned. +.Fn tls_conn_alpn_selected will only succeed after the handshake is complete. +.Em (Server and Client) .It .Fn tls_conn_cipher returns a string @@ -496,6 +508,14 @@ connected to .Fn tls_conn_cipher will only succeed after the handshake is complete. .Em (Server and client) +.It +.Fn tls_conn_version +returns a string +corresponding to a TLS version negotiated with the peer +connected to +.Ar ctx . +.Fn tls_conn_version +will only succeed after the handshake is complete. .It .Fn tls_load_file loads a certificate or key from disk into memory to be loaded with Index: tls_internal.h =================================================================== RCS file: /cvs/src/lib/libtls/tls_internal.h,v retrieving revision 1.32 diff -u -p -r1.32 tls_internal.h --- tls_internal.h 13 Jul 2016 16:30:48 -0000 1.32 +++ tls_internal.h 27 Jul 2016 16:57:06 -0000 @@ -55,6 +55,8 @@ struct tls_keypair { struct tls_config { struct tls_error error; + char *alpn; + size_t alpn_len; const char *ca_file; const char *ca_path; char *ca_mem; @@ -73,6 +75,7 @@ struct tls_config { }; struct tls_conninfo { + char *alpn; char *issuer; char *subject; char *hash; @@ -104,6 +107,7 @@ struct tls { SSL *ssl_conn; SSL_CTX *ssl_ctx; X509 *ssl_peer_cert; + struct tls_conninfo *conninfo; }; Index: tls_server.c =================================================================== RCS file: /cvs/src/lib/libtls/tls_server.c,v retrieving revision 1.19 diff -u -p -r1.19 tls_server.c --- tls_server.c 28 Apr 2016 17:05:59 -0000 1.19 +++ tls_server.c 27 Jul 2016 16:57:06 -0000 @@ -48,6 +48,20 @@ tls_server_conn(struct tls *ctx) return (conn_ctx); } +static int +tls_server_alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ + struct tls *ctx = arg; + + if (SSL_select_next_proto((unsigned char**)out, outlen, + ctx->config->alpn, ctx->config->alpn_len, in, inlen) == + OPENSSL_NPN_NEGOTIATED) + return (SSL_TLSEXT_ERR_OK); + + return (SSL_TLSEXT_ERR_NOACK); +} + int tls_configure_server(struct tls *ctx) { @@ -70,6 +84,10 @@ tls_configure_server(struct tls *ctx) if (tls_configure_ssl_verify(ctx, verify) == -1) goto err; } + + if (ctx->config->alpn != NULL) + SSL_CTX_set_alpn_select_cb(ctx->ssl_ctx, tls_server_alpn_cb, + ctx); if (ctx->config->dheparams == -1) SSL_CTX_set_dh_auto(ctx->ssl_ctx, 1);