Browse Source

[mod_gnutls] fix acme-tls/1 challenge bootstrap

parse ALPN in GNUTLS_HOOK_PRE via gnutls_ext_raw_parse()

(does not appear to work when checking in GNUTLS_HOOK_POST)
master
Glenn Strauss 10 months ago
parent
commit
0936fe6905
  1. 157
      src/mod_gnutls.c

157
src/mod_gnutls.c

@ -1230,7 +1230,7 @@ mod_gnutls_acme_tls_1 (handler_ctx *hctx)
/* check if acme-tls/1 protocol is enabled (path to dir of cert(s) is set)*/
if (buffer_string_is_empty(hctx->conf.ssl_acme_tls_1))
return 0; /*(should not happen)*/
return 0;
/* check if SNI set server name (required for acme-tls/1 protocol)
* and perform simple path checks for no '/'
@ -1313,6 +1313,7 @@ mod_gnutls_acme_tls_1 (handler_ctx *hctx)
hctx->acme_tls_1_cred = ssl_cred; /*(save ptr and free later)*/
gnutls_credentials_clear(hctx->ssl);
rc = gnutls_credentials_set(hctx->ssl, GNUTLS_CRD_CERTIFICATE, ssl_cred);
if (rc < 0) {
elogf(hctx->r->conf.errh, __FILE__, __LINE__, rc,
@ -1321,6 +1322,9 @@ mod_gnutls_acme_tls_1 (handler_ctx *hctx)
return rc;
}
/*(acme-tls/1 is separate from certificate auth access to website)*/
gnutls_certificate_server_set_request(hctx->ssl, GNUTLS_CERT_IGNORE);
return GNUTLS_E_SUCCESS; /* 0 */
}
@ -1333,67 +1337,61 @@ enum {
};
/* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids */
static int
mod_gnutls_alpn_select_cb (gnutls_session_t ssl, handler_ctx *hctx)
{
const int i = 0;
uint8_t proto;
gnutls_datum_t d = { NULL, 0 };
if (gnutls_alpn_get_selected_protocol(ssl, &d) < 0)
return 0; /* ignore GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE */
const char *in = (const char *)d.data;
const int n = (int)d.size;
switch (n) {
case 2: /* "h2" */
if (in[i] == 'h' && in[i+1] == '2') {
proto = MOD_GNUTLS_ALPN_H2;
hctx->r->http_version = HTTP_VERSION_2;
break;
}
return 0;
case 8: /* "http/1.1" "http/1.0" */
if (0 == memcmp(in+i, "http/1.", 7)) {
if (in[i+7] == '1') {
proto = MOD_GNUTLS_ALPN_HTTP11;
break;
mod_gnutls_ALPN (handler_ctx * const hctx, const unsigned char * const in, const unsigned int inlen)
{
/*(skip first two bytes which should match inlen-2)*/
for (unsigned int i = 2, n; i < inlen; i += n) {
n = in[i++];
if (i+n > inlen || 0 == n) break;
switch (n) {
case 2: /* "h2" */
if (in[i] == 'h' && in[i+1] == '2') {
if (!hctx->r->conf.h2proto) continue;
hctx->alpn = MOD_GNUTLS_ALPN_H2;
hctx->r->http_version = HTTP_VERSION_2;
return GNUTLS_E_SUCCESS;
}
if (in[i+7] == '0') {
proto = MOD_GNUTLS_ALPN_HTTP10;
break;
continue;
case 8: /* "http/1.1" "http/1.0" */
if (0 == memcmp(in+i, "http/1.", 7)) {
if (in[i+7] == '1') {
hctx->alpn = MOD_GNUTLS_ALPN_HTTP11;
return GNUTLS_E_SUCCESS;
}
if (in[i+7] == '0') {
hctx->alpn = MOD_GNUTLS_ALPN_HTTP10;
return GNUTLS_E_SUCCESS;
}
}
}
return 0;
case 10: /* "acme-tls/1" */
if (0 == memcmp(in+i, "acme-tls/1", 10)) {
int rc = mod_gnutls_acme_tls_1(hctx);
if (0 == rc) {
proto = MOD_GNUTLS_ALPN_ACME_TLS_1;
break;
continue;
case 10: /* "acme-tls/1" */
if (0 == memcmp(in+i, "acme-tls/1", 10)) {
int rc = mod_gnutls_acme_tls_1(hctx);
if (0 == rc) {
hctx->alpn = MOD_GNUTLS_ALPN_ACME_TLS_1;
return GNUTLS_E_SUCCESS;
}
return rc;
}
return rc;
continue;
default:
continue;
}
return 0;
default:
return 0;
}
hctx->alpn = proto;
return 0;
return GNUTLS_E_SUCCESS;
}
#endif /* GNUTLS_VERSION_NUMBER >= 0x030200 */
static int
mod_gnutls_SNI(void *ctx, unsigned int tls_id,
mod_gnutls_SNI(handler_ctx * const hctx,
const unsigned char *servername, unsigned int len)
{
if (tls_id != 0) return 0;
/* server name */
/* https://www.gnutls.org/manual/gnutls.html#Virtual-hosts-and-credentials
* figure the advertized name - the following hack relies on the fact that
* this extension only supports DNS names, and due to a protocol bug cannot
@ -1401,7 +1399,6 @@ mod_gnutls_SNI(void *ctx, unsigned int tls_id,
if (len < 5) return 0;
len -= 5;
servername += 5;
handler_ctx * const hctx = (handler_ctx *) ctx;
request_st * const r = hctx->r;
buffer_copy_string(&r->uri.scheme, "https");
@ -1436,21 +1433,20 @@ mod_gnutls_SNI(void *ctx, unsigned int tls_id,
static int
mod_gnutls_client_hello_hook_post(gnutls_session_t ssl)
{
#if GNUTLS_VERSION_NUMBER >= 0x030200
handler_ctx * const hctx = gnutls_session_get_ptr(ssl);
int rc = mod_gnutls_alpn_select_cb(ssl, hctx);
if (rc < 0)
return rc;
/* check if acme-tls/1 protocol is enabled (path to dir of cert(s) is set)*/
if (hctx->alpn == MOD_GNUTLS_ALPN_ACME_TLS_1) {
/*(acme-tls/1 is separate from certificate auth access to website)*/
gnutls_certificate_server_set_request(ssl, GNUTLS_CERT_IGNORE);
return GNUTLS_E_SUCCESS; /* 0 */ /*(skip further session config below)*/
mod_gnutls_client_hello_ext_cb(void *ctx, unsigned int tls_id,
const unsigned char *data, unsigned int dlen)
{
switch (tls_id) {
case 0: /* Server Name */
return mod_gnutls_SNI((handler_ctx *)ctx, data, dlen);
#if GNUTLS_VERSION_NUMBER >= 0x030200
case 16: /* ALPN */
return mod_gnutls_ALPN((handler_ctx *)ctx, data, dlen);
#endif
/*case 35:*/ /* Session Ticket */
default:
break;
}
#endif
return GNUTLS_E_SUCCESS; /* 0 */
}
@ -1462,17 +1458,25 @@ mod_gnutls_client_hello_hook(gnutls_session_t ssl, unsigned int htype,
const gnutls_datum_t *msg)
{
/*assert(htype == GNUTLS_HANDSHAKE_CLIENT_HELLO);*/
/*assert(when == GNUTLS_HOOK_PRE);*/
UNUSED(htype);
UNUSED(when);
UNUSED(incoming);
if (when == GNUTLS_HOOK_POST)
return mod_gnutls_client_hello_hook_post(ssl);
handler_ctx * const hctx = gnutls_session_get_ptr(ssl);
#if GNUTLS_VERSION_NUMBER < 0x030600
gnutls_dh_params_t dh_params = hctx->conf.dh_params;
#if GNUTLS_VERSION_NUMBER >= 0x030200
/*(do not repeat if acme-tls/1 creds have been set
* and still in handshake (hctx->alpn not unset yet))*/
if (hctx->alpn == MOD_GNUTLS_ALPN_ACME_TLS_1)
return GNUTLS_E_SUCCESS; /* 0 */
#endif
int rc = gnutls_ext_raw_parse(hctx, mod_gnutls_SNI, msg,
/* ??? why might this be called more than once ??? renegotiation? */
void *existing_cred = NULL;
if (0 == gnutls_credentials_get(ssl, GNUTLS_CRD_CERTIFICATE, &existing_cred)
&& existing_cred)
return GNUTLS_E_SUCCESS; /* 0 */
int rc = gnutls_ext_raw_parse(hctx, mod_gnutls_client_hello_ext_cb, msg,
GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO);
if (rc < 0) {
log_error_st *errh = hctx->r->conf.errh;
@ -1501,17 +1505,16 @@ mod_gnutls_client_hello_hook(gnutls_session_t ssl, unsigned int htype,
elog(errh, __FILE__, __LINE__, rc, "gnutls_alpn_set_protocols()");
return rc;
}
#if 0
/* XXX: might have to manually process client hello for ALPN "acme-tls/1"
* and handle here (GNUTLS_HOOK_PRE) before setting certificate */
if (!buffer_string_is_empty(hctx->conf.ssl_acme_tls_1)) {
}
#endif
/*(skip below if creds already set for acme-tls/1
* via mod_gnutls_client_hello_ext_cb())*/
if (hctx->alpn == MOD_GNUTLS_ALPN_ACME_TLS_1)
return GNUTLS_E_SUCCESS; /* 0 */
#endif
#if 0 /* must enable before GnuTLS client hello hook */
/* GnuTLS returns an error here if TLSv1.3 (? key already set ?) */
/* see mod_gnutls_handle_con_accept() */
/* future: ? handle in mod_gnutls_client_hello_ext_cb() */
if (hctx->ssl_session_ticket && session_ticket_key.size) {
/* XXX: NOT done: parse client hello for session ticket extension
* and choose from among multiple keys */
@ -1550,8 +1553,8 @@ mod_gnutls_client_hello_hook(gnutls_session_t ssl, unsigned int htype,
gnutls_certificate_server_set_request(ssl, req);
#if GNUTLS_VERSION_NUMBER < 0x030600
if (dh_params)
gnutls_certificate_set_dh_params(ssl_cred, dh_params);
if (hctx->conf.dh_params)
gnutls_certificate_set_dh_params(ssl_cred, hctx->conf.dh_params);
#if GNUTLS_VERSION_NUMBER >= 0x030506
else
gnutls_certificate_set_known_dh_params(ssl_cred, GNUTLS_SEC_PARAM_HIGH);
@ -2537,7 +2540,7 @@ CONNECTION_FUNC(mod_gnutls_handle_con_accept)
/* generic func replaces gnutls_handshake_set_post_client_hello_function()*/
gnutls_handshake_set_hook_function(hctx->ssl, GNUTLS_HANDSHAKE_CLIENT_HELLO,
GNUTLS_HOOK_BOTH,
GNUTLS_HOOK_PRE,
mod_gnutls_client_hello_hook);
gnutls_session_set_ptr(hctx->ssl, hctx);

Loading…
Cancel
Save