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