From 909a68c1216b6ea5dbeceaedecec16a0599793d1 Mon Sep 17 00:00:00 2001 From: Fabian Frank Date: Sun, 9 Feb 2014 23:38:55 -0800 Subject: [PATCH] NPN/ALPN: allow disabling via command line when using --http2 one can now selectively disable NPN or ALPN with --no-alpn and --no-npn. for now honored with NSS only. TODO: honor this option with GnuTLS and OpenSSL --- docs/curl.1 | 12 ++++++++ docs/libcurl/symbols-in-versions | 2 ++ include/curl/curl.h | 6 ++++ lib/url.c | 8 +++++ lib/urldata.h | 3 ++ lib/vtls/nss.c | 53 ++++++++++++++++++++------------ src/tool_cfgable.h | 2 ++ src/tool_getparam.c | 8 +++++ src/tool_help.c | 2 ++ src/tool_operate.c | 8 +++++ src/tool_setopt.c | 2 ++ 11 files changed, 87 insertions(+), 19 deletions(-) diff --git a/docs/curl.1 b/docs/curl.1 index 94f4cd845..722d1190b 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -133,6 +133,18 @@ version. (Added in 7.33.0) .IP "--http2" (HTTP) Tells curl to issue its requests using HTTP 2. This requires that the underlying libcurl was built to support it. (Added in 7.33.0) +.IP "--no-npn" +Disable the NPN TLS extension. NPN is enabled by default if libcurl was built +with an SSL library that supports NPN. NPN is used by a libcurl that supports +HTTP 2 to negoatiate HTTP 2 support with the server during https sessions. + +(Added in 7.36.0) +.IP "--no-alpn" +Disable the ALPN TLS extension. ALPN is enabled by default if libcurl was built +with an SSL library that supports ALPN. ALPN is used by a libcurl that supports +HTTP 2 to negoatiate HTTP 2 support with the server during https sessions. + +(Added in 7.36.0) .IP "-1, --tlsv1" (SSL) Forces curl to use TLS version 1 when negotiating with a remote TLS server. diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 432f180ec..a3bc7a507 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -495,6 +495,8 @@ CURLOPT_SSLKEY 7.9.3 CURLOPT_SSLKEYPASSWD 7.9.3 7.17.0 CURLOPT_SSLKEYTYPE 7.9.3 CURLOPT_SSLVERSION 7.1 +CURLOPT_SSL_ENABLE_ALPN 7.36.0 +CURLOPT_SSL_ENABLE_NPN 7.36.0 CURLOPT_SSL_CIPHER_LIST 7.9 CURLOPT_SSL_CTX_DATA 7.10.6 CURLOPT_SSL_CTX_FUNCTION 7.10.6 diff --git a/include/curl/curl.h b/include/curl/curl.h index aafaeed2d..b2c9ee091 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1571,6 +1571,12 @@ typedef enum { /* Set authentication options directly */ CINIT(LOGIN_OPTIONS, OBJECTPOINT, 224), + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_NPN, LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_ALPN, LONG, 226), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/url.c b/lib/url.c index 3e621bdee..3f2112d8a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -563,6 +563,8 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) set->tcp_keepintvl = 60; set->tcp_keepidle = 60; + set->ssl_enable_npn = TRUE; + set->ssl_enable_alpn = TRUE; return res; } @@ -2478,6 +2480,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, case CURLOPT_TCP_KEEPINTVL: data->set.tcp_keepintvl = va_arg(param, long); break; + case CURLOPT_SSL_ENABLE_NPN: + data->set.ssl_enable_npn = (0 != va_arg(param, long))?TRUE:FALSE; + break; + case CURLOPT_SSL_ENABLE_ALPN: + data->set.ssl_enable_alpn = (0 != va_arg(param, long))?TRUE:FALSE; + break; default: /* unknown tag and its companion, just ignore: */ diff --git a/lib/urldata.h b/lib/urldata.h index 1e1ef5d6b..188e766ea 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1593,6 +1593,9 @@ struct UserDefined { long tcp_keepintvl; /* seconds between TCP keepalive probes */ size_t maxconnects; /* Max idle connections in the connection cache */ + + bool ssl_enable_npn; /* TLS NPN extension? */ + bool ssl_enable_alpn; /* TLS ALPN extension? */ }; struct Names { diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index b679c12bb..c784109f7 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -616,15 +616,16 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) { struct connectdata *conn = (struct connectdata*) arg; -#ifndef USE_NGHTTP2 - (void)sock; - (void)conn; -#else +#ifdef USE_NGHTTP2 unsigned int buflenmax = 50; unsigned char buf[50]; unsigned int buflen; SSLNextProtoState state; + if(!conn->data->set.ssl_enable_npn && !conn->data->set.ssl_enable_alpn) { + return; + } + if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { switch(state) { @@ -1311,6 +1312,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) #endif #endif + if(connssl->state == ssl_connection_complete) return CURLE_OK; @@ -1485,32 +1487,45 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } #ifdef USE_NGHTTP2 + if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { #ifdef SSL_ENABLE_NPN - if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, PR_TRUE) != SECSuccess) - goto error; + if(data->set.ssl_enable_npn) { + if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, PR_TRUE) != SECSuccess) + goto error; + } #endif #ifdef SSL_ENABLE_ALPN - if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, PR_TRUE) != SECSuccess) - goto error; + if(data->set.ssl_enable_alpn) { + if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, PR_TRUE) + != SECSuccess) + goto error; + } #endif #if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN) - alpn_protos[cur] = NGHTTP2_PROTO_VERSION_ID_LEN; - cur++; - memcpy(&alpn_protos[cur], NGHTTP2_PROTO_VERSION_ID, - NGHTTP2_PROTO_VERSION_ID_LEN); - cur += NGHTTP2_PROTO_VERSION_ID_LEN; - alpn_protos[cur] = ALPN_HTTP_1_1_LENGTH; - cur++; - memcpy(&alpn_protos[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); + if(data->set.ssl_enable_npn || data->set.ssl_enable_alpn) { + alpn_protos[cur] = NGHTTP2_PROTO_VERSION_ID_LEN; + cur++; + memcpy(&alpn_protos[cur], NGHTTP2_PROTO_VERSION_ID, + NGHTTP2_PROTO_VERSION_ID_LEN); + cur += NGHTTP2_PROTO_VERSION_ID_LEN; + alpn_protos[cur] = ALPN_HTTP_1_1_LENGTH; + cur++; + memcpy(&alpn_protos[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - if(SSL_SetNextProtoNego(connssl->handle, alpn_protos, alpn_protos_len) - != SECSuccess) - goto error; + if(SSL_SetNextProtoNego(connssl->handle, alpn_protos, alpn_protos_len) + != SECSuccess) + goto error; + } + else { + infof(data, "SSL, can't negotiate HTTP/2.0 with neither NPN nor ALPN\n"); + } #endif + } #endif + /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 6696dfb90..2462ac7cd 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -214,6 +214,8 @@ struct Configurable { bool test_event_based; #endif char *xoauth2_bearer; /* XOAUTH2 bearer token */ + bool nonpn; /* enable/disable TLS NPN extension */ + bool noalpn; /* enable/disable TLS ALPN extension */ struct Configurable* prev; struct Configurable* next; /* Always last in the struct */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index e9ae7923a..d54df22d6 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -90,7 +90,9 @@ static const struct LongShort aliases[]= { #endif {"*F", "dns-servers", TRUE}, {"*g", "trace", TRUE}, + {"*G", "npn", FALSE}, {"*h", "trace-ascii", TRUE}, + {"*H", "alpn", FALSE}, {"*i", "limit-rate", TRUE}, {"*j", "compressed", FALSE}, {"*J", "tr-encoding", FALSE}, @@ -554,6 +556,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */ warnf(config, "--trace overrides an earlier trace/verbose option\n"); config->tracetype = TRACE_BIN; break; + case 'G': /* --npn */ + config->nonpn = (!toggle)?TRUE:FALSE; + break; case 'h': /* --trace-ascii */ GetStr(&config->trace_dump, nextarg); if(config->tracetype && (config->tracetype != TRACE_ASCII)) @@ -561,6 +566,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */ "--trace-ascii overrides an earlier trace/verbose option\n"); config->tracetype = TRACE_ASCII; break; + case 'H': /* --alpn */ + config->noalpn = (!toggle)?TRUE:FALSE; + break; case 'i': /* --limit-rate */ { /* We support G, M, K too */ diff --git a/src/tool_help.c b/src/tool_help.c index e129ebb4d..fbd08e8af 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -105,6 +105,8 @@ static const char *const helptext[] = { " -0, --http1.0 Use HTTP 1.0 (H)", " --http1.1 Use HTTP 1.1 (H)", " --http2 Use HTTP 2 (H)", + " --no-npn Disable the NPN TLS extension", + " --no-alpn Disable the ALPN TLS extension", " --ignore-content-length Ignore the HTTP Content-Length header", " -i, --include Include protocol headers in the output (H/F)", " -k, --insecure Allow connections to SSL sites without certs (H)", diff --git a/src/tool_operate.c b/src/tool_operate.c index 559b24cc6..b28321043 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1350,6 +1350,14 @@ static int operate_do(struct Configurable *config) if(config->sasl_ir) my_setopt(curl, CURLOPT_SASL_IR, 1L); + if(config->nonpn) { + my_setopt(curl, CURLOPT_SSL_ENABLE_NPN, 0L); + } + + if(config->noalpn) { + my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L); + } + /* initialize retry vars for loop below */ retry_sleep_default = (config->retry_delay) ? config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ diff --git a/src/tool_setopt.c b/src/tool_setopt.c index f29bcd619..2d60b255d 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -145,6 +145,8 @@ const NameValue setopt_nv_CURLPROTO[] = { static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = { NV1(CURLOPT_SSL_VERIFYPEER, 1), NV1(CURLOPT_SSL_VERIFYHOST, 1), + NV1(CURLOPT_SSL_ENABLE_NPN, 1), + NV1(CURLOPT_SSL_ENABLE_ALPN, 1), NVEND };