aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJDepooter <joel.depooter@gmail.com>2016-03-20 23:59:57 -0700
committerDaniel Stenberg <daniel@haxx.se>2016-03-24 09:56:12 +0100
commitf8d88a491397a13f0e676bd0b2d0f275a728b860 (patch)
treee05063b3c2bd2485305442fb264ca79c852d66c6
parent96fff9f6b5ac6ec38c970a2b5acc6e76be0bdbdd (diff)
schannel: Add ALPN support
Add ALPN support for schannel. This allows cURL to negotiate HTTP/2.0 connections when built with schannel. Closes #724
-rw-r--r--docs/HTTP2.md3
-rw-r--r--lib/vtls/schannel.c106
2 files changed, 107 insertions, 2 deletions
diff --git a/docs/HTTP2.md b/docs/HTTP2.md
index bf5382070..6f7ece431 100644
--- a/docs/HTTP2.md
+++ b/docs/HTTP2.md
@@ -7,7 +7,7 @@ HTTP/2 with curl
Build prerequisites
-------------------
- nghttp2
- - OpenSSL, NSS, GnutTLS or PolarSSL with a new enough version
+ - OpenSSL, NSS, GnutTLS, PolarSSL or SChannel with a new enough version
[nghttp2](https://nghttp2.org/)
-------------------------------
@@ -58,6 +58,7 @@ provide the necessary TLS features. Right now we support:
- NSS: ALPN and NPN
- GnuTLS: ALPN
- PolarSSL: ALPN
+ - SChannel: ALPN
Multiplexing
------------
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index 7f7bd357d..82dff6d53 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -61,6 +61,12 @@
/* The last #include file should be: */
#include "memdebug.h"
+/* ALPN requires version 8.1 of the Windows SDK, which was
+ shipped with Visual Studio 2013, aka _MSC_VER 1800*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+# define HAS_ALPN 1
+#endif
+
/* Uncomment to force verbose output
* #define infof(x, y, ...) printf(y, __VA_ARGS__)
* #define failf(x, y, ...) printf(y, __VA_ARGS__)
@@ -97,6 +103,11 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
SecBuffer outbuf;
SecBufferDesc outbuf_desc;
+ SecBuffer inbuf;
+ SecBufferDesc inbuf_desc;
+#ifdef HAS_ALPN
+ unsigned char alpn_buffer[128];
+#endif
SCHANNEL_CRED schannel_cred;
SECURITY_STATUS sspi_status = SEC_E_OK;
struct curl_schannel_cred *old_cred = NULL;
@@ -219,6 +230,60 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
}
+#ifdef HAS_ALPN
+ if(data->set.ssl_enable_alpn) {
+ int cur = 0;
+ int list_start_index = 0;
+ unsigned int* extension_len = NULL;
+ unsigned short* list_len = NULL;
+
+ /* The first four bytes will be an unsigned int indicating number
+ of bytes of data in the rest of the the buffer. */
+ extension_len = (unsigned int*)(&alpn_buffer[cur]);
+ cur += sizeof(unsigned int);
+
+ /* The next four bytes are an indicator that this buffer will contain
+ ALPN data, as opposed to NPN, for example. */
+ *(unsigned int*)&alpn_buffer[cur] =
+ SecApplicationProtocolNegotiationExt_ALPN;
+ cur += sizeof(unsigned int);
+
+ /* The next two bytes will be an unsigned short indicating the number
+ of bytes used to list the preferred protocols. */
+ list_len = (unsigned short*)(&alpn_buffer[cur]);
+ cur += sizeof(unsigned short);
+
+ list_start_index = cur;
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
+ memcpy(&alpn_buffer[cur], NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN);
+ cur += NGHTTP2_PROTO_ALPN_LEN;
+ infof(data, "schannel: ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
+ }
+#endif
+
+ alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
+ cur += ALPN_HTTP_1_1_LENGTH;
+ infof(data, "schannel: ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ *list_len = cur - list_start_index;
+ *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
+
+ InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
+ InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+ }
+ else
+ {
+ InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+ }
+#else /* HAS_ALPN */
+ InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+#endif
+
/* setup output buffer */
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
@@ -245,7 +310,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
sspi_status = s_pSecFn->InitializeSecurityContext(
&connssl->cred->cred_handle, NULL, host_name,
- connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
+ connssl->req_flags, 0, 0, &inbuf_desc, 0, &connssl->ctxt->ctxt_handle,
&outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
Curl_unicodefree(host_name);
@@ -535,6 +600,10 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct curl_schannel_cred *old_cred = NULL;
+#ifdef HAS_ALPN
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ SecPkgContext_ApplicationProtocol alpn_result;
+#endif
bool incache;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -560,6 +629,41 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
return CURLE_SSL_CONNECT_ERROR;
}
+#ifdef HAS_ALPN
+ if(data->set.ssl_enable_alpn) {
+ sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
+ SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result);
+
+ if(sspi_status != SEC_E_OK) {
+ failf(data, "schannel: failed to retrieve ALPN result");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(alpn_result.ProtoNegoStatus ==
+ SecApplicationProtocolNegotiationStatus_Success) {
+
+ infof(data, "schannel: ALPN, server accepted to use %.*s\n",
+ alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
+
+#ifdef USE_NGHTTP2
+ if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN &&
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId,
+ NGHTTP2_PROTO_VERSION_ID_LEN)) {
+ conn->negnpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
+ ALPN_HTTP_1_1_LENGTH)) {
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+ }
+ }
+ else
+ infof(data, "ALPN, server did not agree to a protocol\n");
+ }
+#endif
+
/* increment the reference counter of the credential/session handle */
if(connssl->cred && connssl->ctxt) {
connssl->cred->refcount++;