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