]> granicus.if.org Git - apache/commitdiff
ALPN support, based on mod_spdy/mod_h2 patch set
authorJim Jagielski <jim@apache.org>
Tue, 31 Mar 2015 17:12:51 +0000 (17:12 +0000)
committerJim Jagielski <jim@apache.org>
Tue, 31 Mar 2015 17:12:51 +0000 (17:12 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1670397 13f79535-47bb-0310-9956-ffa450edef68

modules/ssl/mod_ssl.c
modules/ssl/mod_ssl.h
modules/ssl/ssl_engine_config.c
modules/ssl/ssl_engine_io.c
modules/ssl/ssl_private.h

index afd051a582083ee4aa01eefed2ea26985a88f89f..4aebbd400c544e2556f3fc7a3e2725ab14404635 100644 (file)
@@ -283,6 +283,12 @@ static const command_rec ssl_config_cmds[] = {
                 "OpenSSL configuration command")
 #endif
 
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+    SSL_CMD_SRV(AlpnPreference, ITERATE,
+                "Preference in Application-Layer Protocol Negotiation (ALPN), "
+                "protocols are chosed in the specified order")
+#endif
+    
     /* Deprecated directives. */
     AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
       "SSLLog directive is no longer supported - use ErrorLog."),
@@ -473,6 +479,37 @@ static int modssl_register_npn(conn_rec *c,
 #endif
 }
 
+static int modssl_register_alpn(conn_rec *c,
+                               ssl_alpn_propose_protos advertisefn,
+                               ssl_alpn_proto_negotiated negotiatedfn)
+{
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+    SSLConnRec *sslconn = myConnConfig(c);
+    
+    if (!sslconn) {
+        return DECLINED;
+    }
+    
+    if (!sslconn->alpn_proposefns) {
+        sslconn->alpn_proposefns =
+        apr_array_make(c->pool, 5, sizeof(ssl_alpn_propose_protos));
+        sslconn->alpn_negofns =
+        apr_array_make(c->pool, 5, sizeof(ssl_alpn_proto_negotiated));
+    }
+    
+    if (advertisefn)
+        APR_ARRAY_PUSH(sslconn->alpn_proposefns, ssl_alpn_propose_protos) =
+            advertisefn;
+    if (negotiatedfn)
+        APR_ARRAY_PUSH(sslconn->alpn_negofns, ssl_alpn_proto_negotiated) =
+            negotiatedfn;
+    
+    return OK;
+#else
+    return DECLINED;
+#endif
+}
+
 int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
 {
     SSLSrvConfigRec *sc;
@@ -642,6 +679,7 @@ static void ssl_register_hooks(apr_pool_t *p)
     APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
     APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
     APR_REGISTER_OPTIONAL_FN(modssl_register_npn);
+    APR_REGISTER_OPTIONAL_FN(modssl_register_alpn);
 
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
                               AUTHZ_PROVIDER_VERSION,
index 2a45652853c648e105c6e6a9da424989f8507b10..a3ea694ec384074552c8631ecee4f9160384cd8f 100644 (file)
@@ -128,5 +128,46 @@ APR_DECLARE_OPTIONAL_FN(int, modssl_register_npn, (conn_rec *conn,
                                                    ssl_npn_advertise_protos advertisefn,
                                                    ssl_npn_proto_negotiated negotiatedfn));
 
+/** The alpn_propose_proto callback allows other modules to propose
+ * the name of the protocol that will be chosen during the
+ * Application-Layer Protocol Negotiation (ALPN) portion of the SSL handshake.
+ * The callback is given the connection and a list of NULL-terminated
+ * protocol strings as supported by the client.  If this client_protos is 
+ * non-empty, it must pick its preferred protocol from that list. Otherwise
+ * it should add its supported protocols in order of precedence.
+ * The callback should not yet modify the connection or install any filters
+ * as its proposal(s) may be overridden by another callback or server 
+ * configuration. 
+ * It should return OK or, to prevent further processing of (other modules') 
+ * callbacks, return DONE.
+ */
+typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
+                                    apr_array_header_t *client_protos,
+                                    apr_array_header_t *proposed_protos);
+
+/** The alpn_proto_negotiated callback allows other modules to discover
+ * the name of the protocol that was chosen during the Application-Layer
+ * Protocol Negotiation (ALPN) portion of the SSL handshake.  
+ * The callback is given the connection, a
+ * non-NUL-terminated string containing the protocol name, and the
+ * length of the string; it should do something appropriate
+ * (i.e. insert or remove filters) and return OK. To prevent further
+ * processing of (other modules') callbacks, return DONE. */
+typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
+                                        const char *proto_name,
+                                        apr_size_t proto_name_len);
+
+/* An optional function which can be used to register a pair of callbacks 
+ * for ALPN handling.
+ * This optional function should be invoked from a pre_connection hook 
+ * which runs *after* mod_ssl.c's pre_connection hook.  The function returns 
+ * OK if the callbacks are registered, or DECLINED otherwise (for example if 
+ * mod_ssl does not support ALPN).
+ */
+APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
+                        (conn_rec *conn,
+                         ssl_alpn_propose_protos proposefn,
+                         ssl_alpn_proto_negotiated negotiatedfn));
+
 #endif /* __MOD_SSL_H__ */
 /** @} */
index 9f2348d7e58394aaeb86c582774ea8995a41b897..b15302f033fa9c60c6b45d1653f831e108b8d54f 100644 (file)
@@ -160,6 +160,9 @@ static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p)
     SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
     mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
 #endif
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+    mctx->ssl_alpn_pref = apr_array_make(p, 5, sizeof(const char *));
+#endif
 }
 
 static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc,
@@ -304,6 +307,9 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
 #ifdef HAVE_SSL_CONF_CMD
     cfgMergeArray(ssl_ctx_param);
 #endif
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+    cfgMergeArray(ssl_alpn_pref);
+#endif
 }
 
 static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
@@ -1857,6 +1863,16 @@ const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg,
 }
 #endif
 
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+const char *ssl_cmd_SSLAlpnPreference(cmd_parms *cmd, void *dcfg,
+                                      const char *protocol)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    APR_ARRAY_PUSH(sc->server->ssl_alpn_pref, const char *) = protocol;
+    return NULL;
+}
+#endif
+
 #ifdef HAVE_SRP
 
 const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
index f0f970f8275b3d336358b6ca8797beb5565c4527..1480ee3386494af3876656fe3e00d27ad068d120 100644 (file)
@@ -316,6 +316,7 @@ typedef struct {
     char buffer[AP_IOBUFSIZE];
     ssl_filter_ctx_t *filter_ctx;
     int npn_finished;  /* 1 if NPN has finished, 0 otherwise */
+    int alpn_finished;  /* 1 if ALPN has finished, 0 otherwise */
 } bio_filter_in_ctx_t;
 
 /*
@@ -1483,6 +1484,37 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f,
         APR_BRIGADE_INSERT_TAIL(bb, bucket);
     }
 
+#ifdef HAVE_TLS_ALPN
+    /* By this point, Application-Layer Protocol Negotiation (ALPN) should be 
+     * completed (if our version of OpenSSL supports it). If we haven't already, 
+     * find out which protocol was decided upon and inform other modules 
+     * by calling alpn_proto_negotiated_hook. 
+     */
+    if (!inctx->alpn_finished) {
+        SSLConnRec *sslconn = myConnConfig(f->c);
+        const unsigned char *next_proto = NULL;
+        unsigned next_proto_len = 0;
+        int n;
+        
+        if (sslconn->alpn_negofns) {
+            SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
+                          APLOGNO() "SSL negotiated protocol: '%s'",
+                          (next_proto && next_proto_len)?
+                         apr_pstrmemdup(f->c->pool, (const char *)next_proto,
+                              next_proto_len) : "(null)");
+            for (n = 0; n < sslconn->alpn_negofns->nelts; n++) {
+                ssl_alpn_proto_negotiated fn =
+                APR_ARRAY_IDX(sslconn->alpn_negofns, n, ssl_alpn_proto_negotiated);
+                
+                if (fn(f->c, (const char *)next_proto, next_proto_len) == DONE)
+                break;
+            }
+        }
+        inctx->alpn_finished = 1;
+    }
+#endif
+
 #ifdef HAVE_TLS_NPN
     /* By this point, Next Protocol Negotiation (NPN) should be completed (if
      * our version of OpenSSL supports it).  If we haven't already, find out
@@ -1995,6 +2027,7 @@ static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c,
     inctx->pool = c->pool;
     inctx->filter_ctx = filter_ctx;
     inctx->npn_finished = 0;
+    inctx->alpn_finished = 0;
 }
 
 /* The request_rec pointer is passed in here only to ensure that the
index 9553e594dccd133a3f2ed9991e32a372e509b282..b21e7948af0691ff127a96d5debf6f40a75e6059 100644 (file)
 #define HAVE_TLS_NPN
 #endif
 
+/* ALPN Protocol Negotiation */
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT)
+#define HAVE_TLS_ALPN
+#endif
+
+/* Next Protocol Negotiation */
+#if !defined(OPENSSL_NO_NEXTPROTONEG) && !defined(OPENSSL_NO_TLSEXT) && defined(OPENSSL_NPN_NEGOTIATED)
+#define HAVE_TLS_NPN
+#endif
+
 /* Secure Remote Password */
 #if !defined(OPENSSL_NO_SRP) && defined(SSL_CTRL_SET_TLS_EXT_SRP_USERNAME_CB)
 #define HAVE_SRP
@@ -444,6 +454,12 @@ typedef struct {
     apr_array_header_t *npn_negofns; /* list of ssl_npn_proto_negotiated callbacks. */
 #endif
 
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+    /* Poor man's inter-module optional hooks for NPN. */
+    apr_array_header_t *alpn_proposefns; /* list of ssl_alpn_propose_protos callbacks */
+    apr_array_header_t *alpn_negofns; /* list of ssl_alpn_proto_negotiated callbacks. */
+#endif
+
     server_rec *server;
 } SSLConnRec;
 
@@ -624,6 +640,10 @@ typedef struct {
     SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
     apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
 #endif
+  
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+  apr_array_header_t *ssl_alpn_pref; /* protocol names in order of preference */
+#endif
 } modssl_ctx_t;
 
 struct SSLSrvConfigRec {
@@ -750,6 +770,10 @@ const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, int flag);
 const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
 #endif
 
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+const char *ssl_cmd_SSLAlpnPreference(cmd_parms *cmd, void *dcfg, const char *protocol);
+#endif
+
 #ifdef HAVE_SRP
 const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char *arg);
 const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const char *arg);
@@ -799,6 +823,15 @@ int         ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *,
 #endif
 int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg);
 
+#ifdef HAVE_TLS_ALPN
+int ssl_callback_alpn_select(SSL *ssl, const unsigned char **out,
+                             unsigned char *outlen, const unsigned char *in,
+                             unsigned int inlen, void *arg);
+#endif
+#if defined(HAVE_TLS_NPN)
+int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg);
+#endif
+
 /**  Session Cache Support  */
 apr_status_t ssl_scache_init(server_rec *, apr_pool_t *);
 void         ssl_scache_status_register(apr_pool_t *p);