mirror of
https://github.com/moparisthebest/curl
synced 2024-08-13 17:03:50 -04:00
schannel: Add ALPN support
Add ALPN support for schannel. This allows cURL to negotiate HTTP/2.0 connections when built with schannel. Closes #724
This commit is contained in:
parent
96fff9f6b5
commit
f8d88a4913
@ -7,7 +7,7 @@ HTTP/2 with curl
|
|||||||
Build prerequisites
|
Build prerequisites
|
||||||
-------------------
|
-------------------
|
||||||
- nghttp2
|
- 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/)
|
[nghttp2](https://nghttp2.org/)
|
||||||
-------------------------------
|
-------------------------------
|
||||||
@ -58,6 +58,7 @@ provide the necessary TLS features. Right now we support:
|
|||||||
- NSS: ALPN and NPN
|
- NSS: ALPN and NPN
|
||||||
- GnuTLS: ALPN
|
- GnuTLS: ALPN
|
||||||
- PolarSSL: ALPN
|
- PolarSSL: ALPN
|
||||||
|
- SChannel: ALPN
|
||||||
|
|
||||||
Multiplexing
|
Multiplexing
|
||||||
------------
|
------------
|
||||||
|
@ -61,6 +61,12 @@
|
|||||||
/* The last #include file should be: */
|
/* The last #include file should be: */
|
||||||
#include "memdebug.h"
|
#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
|
/* Uncomment to force verbose output
|
||||||
* #define infof(x, y, ...) printf(y, __VA_ARGS__)
|
* #define infof(x, y, ...) printf(y, __VA_ARGS__)
|
||||||
* #define failf(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];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
SecBuffer outbuf;
|
SecBuffer outbuf;
|
||||||
SecBufferDesc outbuf_desc;
|
SecBufferDesc outbuf_desc;
|
||||||
|
SecBuffer inbuf;
|
||||||
|
SecBufferDesc inbuf_desc;
|
||||||
|
#ifdef HAS_ALPN
|
||||||
|
unsigned char alpn_buffer[128];
|
||||||
|
#endif
|
||||||
SCHANNEL_CRED schannel_cred;
|
SCHANNEL_CRED schannel_cred;
|
||||||
SECURITY_STATUS sspi_status = SEC_E_OK;
|
SECURITY_STATUS sspi_status = SEC_E_OK;
|
||||||
struct curl_schannel_cred *old_cred = NULL;
|
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");
|
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 */
|
/* setup output buffer */
|
||||||
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
|
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
|
||||||
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
|
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
|
||||||
@ -245,7 +310,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
|
|||||||
|
|
||||||
sspi_status = s_pSecFn->InitializeSecurityContext(
|
sspi_status = s_pSecFn->InitializeSecurityContext(
|
||||||
&connssl->cred->cred_handle, NULL, host_name,
|
&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);
|
&outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
|
||||||
|
|
||||||
Curl_unicodefree(host_name);
|
Curl_unicodefree(host_name);
|
||||||
@ -535,6 +600,10 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
|
|||||||
struct SessionHandle *data = conn->data;
|
struct SessionHandle *data = conn->data;
|
||||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
struct curl_schannel_cred *old_cred = NULL;
|
struct curl_schannel_cred *old_cred = NULL;
|
||||||
|
#ifdef HAS_ALPN
|
||||||
|
SECURITY_STATUS sspi_status = SEC_E_OK;
|
||||||
|
SecPkgContext_ApplicationProtocol alpn_result;
|
||||||
|
#endif
|
||||||
bool incache;
|
bool incache;
|
||||||
|
|
||||||
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
|
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
|
||||||
@ -560,6 +629,41 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
|
|||||||
return CURLE_SSL_CONNECT_ERROR;
|
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 */
|
/* increment the reference counter of the credential/session handle */
|
||||||
if(connssl->cred && connssl->ctxt) {
|
if(connssl->cred && connssl->ctxt) {
|
||||||
connssl->cred->refcount++;
|
connssl->cred->refcount++;
|
||||||
|
Loading…
Reference in New Issue
Block a user