diff options
Diffstat (limited to 'lib/vtls')
| -rw-r--r-- | lib/vtls/schannel.c | 106 | 
1 files changed, 105 insertions, 1 deletions
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++;  | 
