Franziskus Kiefer wrote:
Thanks for your reply.
But that doesn't seem to change anything :/

I'm not sure. I worked on this in the spring and made a little bit of progress when using curl as a client but failed with Firefox. I'll attach the patch I was working on. Be aware that it is pretty much a mess with lots of hardcoded values and such.

rob


Cheers

On Tue, Jul 19, 2016 at 3:32 PM, Rob Crittenden <[email protected]
<mailto:[email protected]>> wrote:

    Franziskus Kiefer wrote:

        Hi Rob, all,

        I was wondering if you or anyone else has more infos on [1],
        i.e. the
        comment that this requires changes to mod_http2 in order to get
        H2 running.
        I have a patch that adds ALPN support to mod_nss but I don't get
        Apache
        to actually do H2 (it negotiates H2 but then doesn't send necessary
        messages such as settings).


    In Apache modules/http2/mod_http2.c in h2_hooks() you need to
    include mod_nss in the SSL modules ordering list:

    static const char *const mod_ssl[] = { "mod_ssl.c", "mod_nss.c", NULL};

    rob



>From e0eafd6381a2d00417d5cf1be3b04235cddadf8f Mon Sep 17 00:00:00 2001
From: Rob Crittenden <[email protected]>
Date: Mon, 28 Mar 2016 22:11:54 -0400
Subject: [PATCH] ALPN WIP - basically works, no client proxy

Tests pass with hardcoded alpn values, and no alpn module loaded

proxy test fails
---
 mod_nss.c         |  70 ++++++++++++++++++++++-
 mod_nss.h         |  11 ++++
 nss_engine_init.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 nss_engine_io.c   |  27 ++++++++-
 4 files changed, 268 insertions(+), 6 deletions(-)

diff --git a/mod_nss.c b/mod_nss.c
index 38098c8..ab26413 100644
--- a/mod_nss.c
+++ b/mod_nss.c
@@ -350,11 +350,20 @@ static int nss_hook_pre_connection(conn_rec *c, void *csd)
     SSLConnRec *sslconn = myConnConfig(c);
     modnss_ctx_t *mctx;
 
+#if 0
+    /* FIXME: Do I want to add server to construct? */
+    if (sslconn) {
+        sc = mySrvConfig(sslconn->server);
+    } else {
+        sc = mySrvConfig(c->base_server);
+    }
+#endif
+
     /*
      * Immediately stop processing if SSL is disabled for this connection
      */
-    if (!(sc && (sc->enabled ||
-                 (sslconn && sslconn->is_proxy))))
+    if (c->master || !(sc && (sc->enabled ||
+                              (sslconn && sslconn->is_proxy))))
     {
         return DECLINED;
     }
@@ -433,9 +442,64 @@ static int nss_hook_pre_connection(conn_rec *c, void *csd)
         }
     }
 
+#ifdef SSL_ENABLE_ALPN
+#define MAX_ALPN_LENGTH 255
+    core_server_config *conf;
+    unsigned char protos[MAX_ALPN_LENGTH];
+    size_t offset = 0;
+
+    /* Cheat and get the allowed protocols and see that into NSS */
+    conf = ap_get_core_module_config(c->base_server->module_config);
+    if (conf->protocols->nelts <= 0) {
+        strcpy(protos, "\bhttp/1.1\0"); /* HTTP/1.1 */
+        offset = 9;
+    } else {
+        unsigned int len = 0;
+        int i;
+
+        for (i = 0; i < conf->protocols->nelts; ++i) {
+            const char *p = APR_ARRAY_IDX(conf->protocols, i, const char *);
+            len = strlen(p);
+            protos[offset++] = len;
+            memcpy(protos + offset, p, len);
+            offset += len;
+        }
+        protos[offset] = '\0';
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server,
+            "protos %s offset %d", protos, offset);
+    }
+    SSL_HandshakeCallback(ssl, (SSLHandshakeCallback)NSSHandshakeCallback, c);
+    if (SSL_SetNextProtoNego(ssl, "\bhttp/1.1\002h2", 12) != SECSuccess) {
+//    if (SSL_SetNextProtoNego(ssl, "\002h2", 3) != SECSuccess) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server,
+                    "Unable to set ALPN string.");
+            return DECLINED;
+    }
+#endif
+
     return APR_SUCCESS;
 }
 
+static int nss_hook_process_connection(conn_rec* c)
+{
+    SSLConnRec *sslconn = myConnConfig(c);
+
+    if (sslconn && !sslconn->disabled) {
+        /* On an active SSL connection, let the input filters initialize
+         * themselves which triggers the handshake, which again triggers
+         * all kinds of useful things such as SNI and ALPN.
+        */
+        apr_bucket_brigade* temp;
+
+        temp = apr_brigade_create(c->pool, c->bucket_alloc);
+        ap_get_brigade(c->input_filters, temp,
+                       AP_MODE_INIT, APR_BLOCK_READ, 0);
+        apr_brigade_destroy(temp);
+    }
+
+    return DECLINED;
+}
+
 static const char *nss_hook_http_scheme(const request_rec *r)
 {
     SSLSrvConfigRec *sc = mySrvConfig(r->server);
@@ -471,6 +535,8 @@ static void nss_register_hooks(apr_pool_t *p)
     nss_io_filter_register(p);
 
     ap_hook_pre_connection(nss_hook_pre_connection,NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_process_connection(nss_hook_process_connection,
+                                                   NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_post_config   (nss_init_Module,        NULL,NULL, APR_HOOK_MIDDLE);
 #if AP_SERVER_MINORVERSION_NUMBER < 2 /* See comment in mod_nss.h */
     ap_hook_http_method   (nss_hook_http_scheme,   NULL,NULL, APR_HOOK_MIDDLE);
diff --git a/mod_nss.h b/mod_nss.h
index 226f7a8..ddf324f 100644
--- a/mod_nss.h
+++ b/mod_nss.h
@@ -520,4 +520,15 @@ const char * SECItem_to_ascii(apr_pool_t *p, SECItem *item);
 const char * SECItem_to_hex(apr_pool_t *p,const SECItem * item);
 const char * SECItem_to_ipaddr(apr_pool_t *p, SECItem *item);
 const char * SECItem_get_oid(apr_pool_t *p, SECItem *oid);
+
+/* MOVE ME */
+SECStatus ownALPNCallback(    void *arg,
+    PRFileDesc *fd,
+    const unsigned char* protos,
+    unsigned int protosLen,
+    unsigned char* protoOut,
+    unsigned int* protoOutLen,
+    unsigned int protoMaxOut);
+
+SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg);
 #endif /* __MOD_NSS_H__ */
diff --git a/nss_engine_init.c b/nss_engine_init.c
index cd71989..00f4838 100644
--- a/nss_engine_init.c
+++ b/nss_engine_init.c
@@ -32,7 +32,7 @@
 
 static SECStatus ownBadCertHandler(void *arg, PRFileDesc * socket);
 static SECStatus ownHandshakeCallback(PRFileDesc * socket, void *arg);
-static SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg);
+//static SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg);
 static CERTCertificate* FindServerCertFromNickname(const char* name, const CERTCertList* clist);
 SECStatus nss_AuthCertificate(void *arg, PRFileDesc *socket, PRBool checksig, PRBool isServer);
 PRInt32 nssSSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr, PRUint32 sniNameArrSize, void *arg);
@@ -687,6 +687,31 @@ static void nss_init_ctx_socket(server_rec *s,
             nss_die();
     }
 #endif
+#ifdef SSL_ENABLE_ALPN
+    if (mctx->as_server) {
+        if (SSL_OptionSet(mctx->model, SSL_ENABLE_NPN, PR_FALSE)
+            != SECSuccess) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                    "Unable to disable NPN.");
+            nss_log_nss_error(APLOG_MARK, APLOG_ERR, s);
+            nss_die();
+        }
+        if (SSL_OptionSet(mctx->model, SSL_ENABLE_ALPN, PR_TRUE)
+            != SECSuccess) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                    "Unable to enable ALPN.");
+            nss_log_nss_error(APLOG_MARK, APLOG_ERR, s);
+            nss_die();
+        }
+    if (SSL_SetNextProtoNego(mctx->model, "\bhttp/1.1\002h2", 12)
+        != SECSuccess) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                    "Unable to set ALPN string.");
+            nss_log_nss_error(APLOG_MARK, APLOG_ERR, s);
+            nss_die();
+        }
+    }
+#endif
 }
 
 static void nss_init_ctx_protocol(server_rec *s,
@@ -1404,7 +1429,7 @@ static void nss_init_server_certs(server_rec *s,
             "Error setting PKCS11 pin argument: '%s'", mctx->nickname);
         nss_die();
     }
-    secstatus = (SECStatus)SSL_HandshakeCallback(mctx->model, (SSLHandshakeCallback)NSSHandshakeCallback, NULL);
+    secstatus = (SECStatus)SSL_HandshakeCallback(mctx->model, (SSLHandshakeCallback)NSSHandshakeCallback, s);
     if (secstatus != SECSuccess)
     {
         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
@@ -1417,6 +1442,15 @@ static void nss_init_server_certs(server_rec *s,
                 "Configured proxy nickname as '%s'", mctx->nickname);
         }
     }
+#ifdef SSL_ENABLE_ALPN
+    /* Bug in NSS that this isn't being copied in ImportFD */
+    if (SSL_SetNextProtoCallback(mctx->model, ownALPNCallback, NULL) != SECSuccess) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                "SSL_SetNextProtoCallback failed");
+        nss_log_nss_error(APLOG_MARK, APLOG_ERR, s);
+        nss_die();
+    }
+#endif
 
 }
 
@@ -1659,6 +1693,30 @@ SECStatus ownHandshakeCallback(PRFileDesc * socket, void *arg)
 }
 
 /*
+ * arg should be the list of supported protocols. This will need to
+ * be set when the callback is added. Not sure how I can do this in
+ * a child.
+ *
+ * Incoming looks like: \bhttp/1.1\002h2 (len 12)
+ *
+ * <length><string> until EOF
+ *
+ * See ssl_callback_alpn_select in ssl_engine_kernel.c for how mod_ssl
+ * selects protocol. It uses Apache fn ap_select_protocol to do the
+ * real work, after creating the right lists.
+ */
+SECStatus ownALPNCallback(void *arg, PRFileDesc *fd,
+                          const unsigned char* protos, unsigned int protosLen,
+                          unsigned char* protoOut, unsigned int* protoOutLen,
+                          unsigned int protoMaxOut)
+{
+    strcpy(protoOut, "h2");
+    *protoOutLen = strlen(protoOut);
+
+    return SECSuccess;
+}
+
+/*
  * Duplicated, non-exported function from NSS that compares 2 certificate
  * times.
  */
@@ -1806,8 +1864,112 @@ FindServerCertFromNickname(const char* name, const CERTCertList* clist)
  * Executed automatically when the SSL handshake is completed.
  * We don't do anything special here.
  */
+#define MAX_ALPN_LENGTH 255 /* really? */
 SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg)
 {
+
+    /* TODO: check if ALPN is set at all */
+
+    server_rec *s;
+    conn_rec *c;
+    SSLNextProtoState alpnState;
+    char chosenAlpn[MAX_ALPN_LENGTH];
+    unsigned int chosenAlpnLen;
+    char * protocol = NULL;
+
+    const char *proposed;
+    apr_array_header_t *client_protos;
+
+    if (!arg)
+        return SECFailure;
+
+    c = (conn_rec *) arg;
+    s = c->base_server;
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+        "SSL Handshake is done, in NSSHandshakeCallback");
+
+    SECStatus rv = SSL_GetNextProto(socket, &alpnState,
+                                    (unsigned char*)chosenAlpn,
+                                    &chosenAlpnLen, sizeof(chosenAlpn));
+    if (rv != SECSuccess) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+            "ALPN error");
+        nss_log_nss_error(APLOG_MARK, APLOG_ERR, NULL);
+        return SECFailure;
+    }
+
+    switch (alpnState) {
+        case SSL_NEXT_PROTO_SELECTED:
+        case SSL_NEXT_PROTO_NEGOTIATED:
+            break; // OK
+        case SSL_NEXT_PROTO_NO_SUPPORT:
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+                "ALPN not negotiated");
+            /* TODO: set some sort of default? */
+            return SECSuccess;
+        case SSL_NEXT_PROTO_NO_OVERLAP:
+            // This only happens if there is a custom NPN/ALPN callback
+            // installed and that callback doesn't properly handle ALPN.
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+                "error in ALPN selection callback");
+            return SECFailure;
+    }
+
+    // The ALPN string isn't null-terminated.
+    protocol = (char *)malloc(chosenAlpnLen + 1);
+    strncpy(protocol, chosenAlpn, chosenAlpnLen);
+    protocol[chosenAlpnLen] = '\0';
+
+    /* FIXME: Needs to happen so we can actually propose a proto */
+    client_protos = apr_array_make(c->pool, 0, sizeof(char *));
+    APR_ARRAY_PUSH(client_protos, char *) =
+            apr_pstrndup(c->pool, (const char *)protocol, chosenAlpnLen);
+
+    proposed = ap_select_protocol(c, NULL, c->base_server, client_protos);
+    if (!proposed) {
+        proposed = ap_get_protocol(c);
+    }
+
+    ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+       "Selected ALPN string: %s", protocol);
+    if (strcmp(protocol, ap_get_protocol(c))) {
+        apr_status_t status;
+        /* FIXME: don't use base_server here, need real server */
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+            "Switching protocol");
+        status = ap_switch_protocol(c, NULL, c->base_server, protocol);
+        if (status != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c,
+                          "protocol switch to '%s' failed", protocol);
+            return SECFailure;
+        }
+    }
+
+    free(protocol);
+
+    /* TODO: ensure the chosen string is in allowed list */
+/*
+    if (alpn_allowed_.find(chosen) == alpn_allowed_.end()) {
+        // Maybe our peer chose a protocol we didn't offer (when we are client), or
+        // something is seriously wrong.
+        std::ostringstream ss;
+        for (auto i = alpn_allowed_.begin(); i != alpn_allowed_.end(); ++i) {
+           ss << (i == alpn_allowed_.begin() ? " '" : ", '") << *i << "'";
+        }
+        MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Bad ALPN string: '" << chosen
+                << "'; permitted:" << ss.str());
+       return false;
+    }
+    alpn_ = chosen;
+*/
+
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+                  "Protocol: %s, Cipher: %s (%s/%s bits)",
+                  nss_var_lookup(NULL, s, c, NULL, "SSL_PROTOCOL"),
+                  nss_var_lookup(NULL, s, c, NULL, "SSL_CIPHER"),
+                  nss_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_USEKEYSIZE"),
+                  nss_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_ALGKEYSIZE"));
+
     return SECSuccess;
 }
 
diff --git a/nss_engine_io.c b/nss_engine_io.c
index de57a20..2a9b158 100644
--- a/nss_engine_io.c
+++ b/nss_engine_io.c
@@ -351,6 +351,10 @@ static apr_status_t nss_io_input_read(nspr_filter_in_ctx_t *inctx,
 
         PR_SetError(0, 0);
         rc = PR_Read(inctx->filter_ctx->pssl, buf + bytes, wanted - bytes);
+#ifdef READ_DEBUG
+        ap_log_error(APLOG_MARK, APLOG_INFO, inctx->rc, c->base_server,
+             "mode: %d read: %s len %d wanted %d", inctx->mode, buf, rc, wanted);
+#endif
 
         if (rc > 0) {
             *len += rc;
@@ -401,6 +405,8 @@ static apr_status_t nss_io_input_read(nspr_filter_in_ctx_t *inctx,
                  * renegotation which is handled implicitly by NSS.)
                  */
                 inctx->rc = APR_EAGAIN;
+                ap_log_error(APLOG_MARK, APLOG_INFO, inctx->rc, c->base_server,
+                    "PR_WOULD_BLOCK_ERROR, len %d", *len);
 
                 if (*len > 0) {
                     inctx->rc = APR_SUCCESS;
@@ -685,7 +691,10 @@ static apr_status_t nss_io_filter_handshake(ap_filter_t *f)
 {
     conn_rec *c         = f->c;
     SSLConnRec *sslconn = myConnConfig(c);
-    SECStatus rv;
+    apr_status_t status;
+    nspr_filter_in_ctx_t *inctx = f->ctx;
+    char buf[1024];
+    apr_size_t len = 0;
 
     /*
      * Enable SNI for proxy backend requests. Make sure we don't do it for
@@ -713,7 +722,7 @@ static apr_status_t nss_io_filter_handshake(ap_filter_t *f)
              apr_ipsubnet_create(&ip, hostname_note, NULL, c->pool)
                 != APR_SUCCESS)
         {
-            if ((rv = SSL_SetURL(sslconn->ssl, hostname_note)) != SECSuccess) {
+            if (SSL_SetURL(sslconn->ssl, hostname_note) != SECSuccess) {
                 ap_log_error(APLOG_MARK, APLOG_INFO, 0, c->base_server,
                     "Error setting SNI extension for SSL Proxy request: %d",
                      PR_GetError());
@@ -728,6 +737,13 @@ static apr_status_t nss_io_filter_handshake(ap_filter_t *f)
         }
     }
 
+    status = nss_io_input_read(inctx, buf, &len);
+
+    /* don't want it being EOF, check first */
+    inctx->rc = APR_SUCCESS;
+
+    /* FIXME: do something with status */
+
     return APR_SUCCESS;
 }
 
@@ -766,9 +782,12 @@ static apr_status_t nss_io_filter_input(ap_filter_t *f,
     inctx->mode = mode;
     inctx->block = block;
 
+    /* FIXME: be sure to check that proxy client still works */
+    if (mode == AP_MODE_INIT) {
     if ((status = nss_io_filter_handshake(f)) != APR_SUCCESS) {
         return nss_io_filter_error(f, bb, status);
     }
+    }
 
     if (is_init) {
         /* protocol module needs to handshake before sending
@@ -891,9 +910,13 @@ static apr_status_t nss_io_filter_output(ap_filter_t *f,
     inctx->mode = AP_MODE_READBYTES;
     inctx->block = APR_BLOCK_READ;
 
+
+    /* FIXME: is this really needed? */
+#if 0
     if ((status = nss_io_filter_handshake(f)) != APR_SUCCESS) {
         return nss_io_filter_error(f, bb, status);
     }
+#endif
 
     while (!APR_BRIGADE_EMPTY(bb)) {
         apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
-- 
2.5.5

_______________________________________________
Mod_nss-list mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/mod_nss-list

Reply via email to