[PATCH] secur32: Add TLS application protocol negotiation support.

Hans Leidekker hans at codeweavers.com
Thu Apr 9 10:25:32 CDT 2020


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/secur32/schannel.c        |  25 ++++-
 dlls/secur32/schannel_gnutls.c | 112 +++++++++++++++++++
 dlls/secur32/secur32_priv.h    |   4 +-
 dlls/secur32/tests/schannel.c  | 198 +++++++++++++++++++++++++++------
 include/sspi.h                 |  26 +++++
 5 files changed, 323 insertions(+), 42 deletions(-)

diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c
index 8063fd4d6b..e76a3e46c3 100644
--- a/dlls/secur32/schannel.c
+++ b/dlls/secur32/schannel.c
@@ -786,6 +786,8 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
     struct schan_credentials *cred;
     SIZE_T expected_size = ~0UL;
     SECURITY_STATUS ret;
+    SecBuffer *buffer;
+    int idx;
 
     TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext,
      debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
@@ -842,6 +844,13 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
                 heap_free( target );
             }
         }
+
+        if (pInput && (idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_APPLICATION_PROTOCOLS)) != -1)
+        {
+            buffer = &pInput->pBuffers[idx];
+            schan_imp_set_application_protocols(ctx->session, buffer->pvBuffer, buffer->cbBuffer);
+        }
+
         phNewContext->dwLower = handle;
         phNewContext->dwUpper = 0;
     }
@@ -849,8 +858,6 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
     {
         SIZE_T record_size = 0;
         unsigned char *ptr;
-        SecBuffer *buffer;
-        int idx;
 
         if (!pInput)
             return SEC_E_INCOMPLETE_MESSAGE;
@@ -1003,6 +1010,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
         PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
 {
     struct schan_context *ctx;
+    SECURITY_STATUS status;
 
     TRACE("context_handle %p, attribute %#x, buffer %p\n",
             context_handle, attribute, buffer);
@@ -1015,7 +1023,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
         case SECPKG_ATTR_STREAM_SIZES:
         {
             SecPkgContext_ConnectionInfo info;
-            SECURITY_STATUS status = schan_imp_get_connection_info(ctx->session, &info);
+            status = schan_imp_get_connection_info(ctx->session, &info);
             if (status == SEC_E_OK)
             {
                 SecPkgContext_StreamSizes *stream_sizes = buffer;
@@ -1039,7 +1047,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
         case SECPKG_ATTR_KEY_INFO:
         {
             SecPkgContext_ConnectionInfo conn_info;
-            SECURITY_STATUS status = schan_imp_get_connection_info(ctx->session, &conn_info);
+            status = schan_imp_get_connection_info(ctx->session, &conn_info);
             if (status == SEC_E_OK)
             {
                 SecPkgContext_KeyInfoW *info = buffer;
@@ -1054,7 +1062,6 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
         case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
         {
             PCCERT_CONTEXT *cert = buffer;
-            SECURITY_STATUS status;
 
             status = ensure_remote_cert(ctx);
             if(status != SEC_E_OK)
@@ -1075,7 +1082,6 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
             ALG_ID hash_alg = CALG_SHA_256;
             BYTE hash[1024];
             DWORD hash_size;
-            SECURITY_STATUS status;
             char *p;
             BOOL r;
 
@@ -1109,6 +1115,11 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
             memcpy(p, hash, hash_size);
             return SEC_E_OK;
         }
+        case SECPKG_ATTR_APPLICATION_PROTOCOL:
+        {
+            SecPkgContext_ApplicationProtocol *protocol = buffer;
+            return schan_imp_get_application_protocol(ctx->session, protocol);
+        }
 
         default:
             FIXME("Unhandled attribute %#x\n", attribute);
@@ -1143,6 +1154,8 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesA(
             return schan_QueryContextAttributesW(context_handle, attribute, buffer);
         case SECPKG_ATTR_ENDPOINT_BINDINGS:
             return schan_QueryContextAttributesW(context_handle, attribute, buffer);
+        case SECPKG_ATTR_APPLICATION_PROTOCOL:
+            return schan_QueryContextAttributesW(context_handle, attribute, buffer);
 
         default:
             FIXME("Unhandled attribute %#x\n", attribute);
diff --git a/dlls/secur32/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c
index ecc9f6bcfe..f177d90dfd 100644
--- a/dlls/secur32/schannel_gnutls.c
+++ b/dlls/secur32/schannel_gnutls.c
@@ -50,6 +50,11 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
 /* Not present in gnutls version < 2.9.10. */
 static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t);
 
+/* Not present in gnutls version < 3.2.0. */
+static int (*pgnutls_alpn_get_selected_protocol)(gnutls_session_t, gnutls_datum_t *);
+static int (*pgnutls_alpn_set_protocols)(gnutls_session_t, const gnutls_datum_t *,
+                                         unsigned, unsigned int);
+
 /* Not present in gnutls version < 3.3.0. */
 static int (*pgnutls_privkey_import_rsa_raw)(gnutls_privkey_t, const gnutls_datum_t *,
                                         const gnutls_datum_t *, const gnutls_datum_t *,
@@ -114,6 +119,10 @@ MAKE_FUNCPTR(gnutls_x509_privkey_deinit);
 #define GNUTLS_KX_ECDHE_PSK     14
 #endif
 
+#if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 2)
+#define GNUTLS_ALPN_SERVER_PRECEDENCE (1<<1)
+#endif
+
 static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
 {
     switch(cipher) {
@@ -153,6 +162,19 @@ static int compat_gnutls_privkey_import_rsa_raw(gnutls_privkey_t key, const gnut
     return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
 }
 
+static int compat_gnutls_alpn_get_selected_protocol(gnutls_session_t session, gnutls_datum_t *protocol)
+{
+    FIXME("\n");
+    return GNUTLS_E_INVALID_REQUEST;
+}
+
+static int compat_gnutls_alpn_set_protocols(gnutls_session_t session, const gnutls_datum_t *protocols,
+                                            unsigned size, unsigned int flags)
+{
+    FIXME("\n");
+    return GNUTLS_E_INVALID_REQUEST;
+}
+
 static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
                                       void *buff, size_t buff_len)
 {
@@ -599,6 +621,86 @@ again:
     return SEC_E_OK;
 }
 
+static unsigned int parse_alpn_protocol_list(unsigned char *buffer, unsigned int buflen, gnutls_datum_t *list)
+{
+    unsigned int len, offset = 0, count = 0;
+
+    while (buflen)
+    {
+        len = buffer[offset++];
+        buflen--;
+        if (!len || len > buflen) return 0;
+        if (list)
+        {
+            list[count].data = &buffer[offset];
+            list[count].size = len;
+        }
+        buflen -= len;
+        offset += len;
+        count++;
+    }
+
+    return count;
+}
+
+void schan_imp_set_application_protocols(schan_imp_session session, unsigned char *buffer, unsigned int buflen)
+{
+    gnutls_session_t s = (gnutls_session_t)session;
+    unsigned int extension_len, extension, count = 0, offset = 0;
+    unsigned short list_len;
+    gnutls_datum_t *protocols;
+    int ret;
+
+    if (sizeof(extension_len) > buflen) return;
+    extension_len = *(unsigned int *)&buffer[offset];
+    offset += sizeof(extension_len);
+
+    if (offset + sizeof(extension) > buflen) return;
+    extension = *(unsigned int *)&buffer[offset];
+    if (extension != SecApplicationProtocolNegotiationExt_ALPN)
+    {
+        FIXME("extension %u not supported\n", extension);
+        return;
+    }
+    offset += sizeof(extension);
+
+    if (offset + sizeof(list_len) > buflen) return;
+    list_len = *(unsigned short *)&buffer[offset];
+    offset += sizeof(list_len);
+
+    if (offset + list_len > buflen) return;
+    count = parse_alpn_protocol_list(&buffer[offset], list_len, NULL);
+    if (!count || !(protocols = heap_alloc(count * sizeof(*protocols)))) return;
+
+    parse_alpn_protocol_list(&buffer[offset], list_len, protocols);
+    if ((ret = pgnutls_alpn_set_protocols(s, protocols, count, GNUTLS_ALPN_SERVER_PRECEDENCE) < 0))
+    {
+        pgnutls_perror(ret);
+    }
+
+    heap_free(protocols);
+}
+
+SECURITY_STATUS schan_imp_get_application_protocol(schan_imp_session session,
+                                                   SecPkgContext_ApplicationProtocol *protocol)
+{
+    gnutls_session_t s = (gnutls_session_t)session;
+    gnutls_datum_t selected;
+
+    memset(protocol, 0, sizeof(*protocol));
+    if (pgnutls_alpn_get_selected_protocol(s, &selected) < 0) return SEC_E_OK;
+
+    if (selected.size <= sizeof(protocol->ProtocolId))
+    {
+        protocol->ProtoNegoStatus = SecApplicationProtocolNegotiationStatus_Success;
+        protocol->ProtoNegoExt    = SecApplicationProtocolNegotiationExt_ALPN;
+        protocol->ProtocolIdSize  = selected.size;
+        memcpy(protocol->ProtocolId, selected.data, selected.size);
+        TRACE("returning %s\n", debugstr_an((const char *)selected.data, selected.size));
+    }
+    return SEC_E_OK;
+}
+
 static WCHAR *get_key_container_path(const CERT_CONTEXT *ctx)
 {
     static const WCHAR rsabaseW[] =
@@ -935,6 +1037,16 @@ BOOL schan_imp_init(void)
         WARN("gnutls_cipher_get_block_size not found\n");
         pgnutls_cipher_get_block_size = compat_cipher_get_block_size;
     }
+    if (!(pgnutls_alpn_set_protocols = dlsym(libgnutls_handle, "gnutls_alpn_set_protocols")))
+    {
+        WARN("gnutls_alpn_set_protocols not found\n");
+        pgnutls_alpn_set_protocols = compat_gnutls_alpn_set_protocols;
+    }
+    if (!(pgnutls_alpn_get_selected_protocol = dlsym(libgnutls_handle, "gnutls_alpn_get_selected_protocol")))
+    {
+        WARN("gnutls_alpn_get_selected_protocol not found\n");
+        pgnutls_alpn_get_selected_protocol = compat_gnutls_alpn_get_selected_protocol;
+    }
     if (!(pgnutls_privkey_export_x509 = dlsym(libgnutls_handle, "gnutls_privkey_export_x509")))
     {
         WARN("gnutls_privkey_export_x509 not found\n");
diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h
index 1bff1372b0..c34d1d3256 100644
--- a/dlls/secur32/secur32_priv.h
+++ b/dlls/secur32/secur32_priv.h
@@ -248,6 +248,8 @@ extern void schan_imp_free_certificate_credentials(schan_credentials*) DECLSPEC_
 extern DWORD schan_imp_enabled_protocols(void) DECLSPEC_HIDDEN;
 extern BOOL schan_imp_init(void) DECLSPEC_HIDDEN;
 extern void schan_imp_deinit(void) DECLSPEC_HIDDEN;
-
+extern void schan_imp_set_application_protocols(schan_imp_session, unsigned char *, unsigned int) DECLSPEC_HIDDEN;
+extern SECURITY_STATUS schan_imp_get_application_protocol(schan_imp_session,
+                                                          SecPkgContext_ApplicationProtocol *) DECLSPEC_HIDDEN;
 
 #endif /* ndef __SECUR32_PRIV_H__ */
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c
index 217dbe8dfa..d51583d665 100644
--- a/dlls/secur32/tests/schannel.c
+++ b/dlls/secur32/tests/schannel.c
@@ -670,14 +670,41 @@ static void test_InitializeSecurityContext(void)
     FreeCredentialsHandle(&cred_handle);
 }
 
+static SOCKET create_ssl_socket( const char *hostname )
+{
+    struct hostent *host;
+    struct sockaddr_in addr;
+    SOCKET sock;
+
+    if (!(host = gethostbyname(hostname)))
+    {
+        skip("Can't resolve \"%s\"\n", hostname);
+        return -1;
+    }
+
+    addr.sin_family = host->h_addrtype;
+    addr.sin_addr = *(struct in_addr *)host->h_addr_list[0];
+    addr.sin_port = htons(443);
+    if ((sock = socket(host->h_addrtype, SOCK_STREAM, 0)) == -1)
+    {
+        skip("Can't create socket\n");
+        return 1;
+    }
+
+    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+    {
+        skip("Can't connect to \"%s\"\n", hostname);
+        closesocket(sock);
+        return -1;
+    }
+
+    return sock;
+}
+
 static void test_communication(void)
 {
     int ret;
-
-    WSADATA wsa_data;
     SOCKET sock;
-    struct hostent *host;
-    struct sockaddr_in addr;
 
     SECURITY_STATUS status;
     ULONG attrs;
@@ -705,36 +732,7 @@ static void test_communication(void)
     }
 
     /* Create a socket and connect to test.winehq.org */
-    ret = WSAStartup(0x0202, &wsa_data);
-    if (ret)
-    {
-        skip("Can't init winsock 2.2\n");
-        return;
-    }
-
-    host = gethostbyname("test.winehq.org");
-    if (!host)
-    {
-        skip("Can't resolve test.winehq.org\n");
-        return;
-    }
-
-    addr.sin_family = host->h_addrtype;
-    addr.sin_addr = *(struct in_addr *)host->h_addr_list[0];
-    addr.sin_port = htons(443);
-    sock = socket(host->h_addrtype, SOCK_STREAM, 0);
-    if (sock == SOCKET_ERROR)
-    {
-        skip("Can't create socket\n");
-        return;
-    }
-
-    ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
-    if (ret == SOCKET_ERROR)
-    {
-        skip("Can't connect to test.winehq.org\n");
-        return;
-    }
+    if ((sock = create_ssl_socket( "test.winehq.org" )) == -1) return;
 
     /* Create client credentials */
     init_cred(&cred);
@@ -952,7 +950,7 @@ todo_wine
     status = pQueryContextAttributesA(&context, SECPKG_ATTR_STREAM_SIZES, &sizes);
     ok(status == SEC_E_OK, "QueryContextAttributesW(SECPKG_ATTR_STREAM_SIZES) failed: %08x\n", status);
 
-    status = QueryContextAttributesA(&context, SECPKG_ATTR_NEGOTIATION_INFO, &info);
+    status = pQueryContextAttributesA(&context, SECPKG_ATTR_NEGOTIATION_INFO, &info);
     ok(status == SEC_E_UNSUPPORTED_FUNCTION, "QueryContextAttributesA returned %08x\n", status);
 
     reset_buffers(&buffers[0]);
@@ -1037,12 +1035,142 @@ todo_wine
     closesocket(sock);
 }
 
+static void test_application_protocol_negotiation(void)
+{
+    int ret;
+    SOCKET sock;
+    SECURITY_STATUS status;
+    ULONG attrs;
+    SCHANNEL_CRED cred;
+    CredHandle cred_handle;
+    CtxtHandle context;
+    SecPkgContext_ApplicationProtocol protocol;
+    SecBufferDesc buffers[3];
+    SecBuffer *buf;
+    unsigned buf_size = 8192;
+    unsigned char *alpn_buffer;
+    unsigned int *extension_len;
+    unsigned short *list_len;
+    int list_start_index, offset = 0;
+
+    if (!pQueryContextAttributesA)
+    {
+        win_skip("Required secur32 functions not available\n");
+        return;
+    }
+
+    if ((sock = create_ssl_socket( "test.winehq.org" )) == -1) return;
+
+    init_cred(&cred);
+    cred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
+    cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS|SCH_CRED_MANUAL_CRED_VALIDATION;
+
+    status = AcquireCredentialsHandleA(NULL, (SEC_CHAR *)UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL,
+        &cred, NULL, NULL, &cred_handle, NULL);
+    ok(status == SEC_E_OK, "got %08x\n", status);
+    if (status != SEC_E_OK) return;
+
+    init_buffers(&buffers[0], 4, buf_size);
+    init_buffers(&buffers[1], 4, buf_size);
+    init_buffers(&buffers[2], 1, 128);
+
+    alpn_buffer = buffers[2].pBuffers[0].pvBuffer;
+    extension_len = (unsigned int *)&alpn_buffer[offset];
+    offset += sizeof(*extension_len);
+    *(unsigned int *)&alpn_buffer[offset] = SecApplicationProtocolNegotiationExt_ALPN;
+    offset += sizeof(unsigned int);
+    list_len = (unsigned short *)&alpn_buffer[offset];
+    offset += sizeof(*list_len);
+    list_start_index = offset;
+
+    alpn_buffer[offset++] = sizeof("http/1.1") - 1;
+    memcpy(&alpn_buffer[offset], "http/1.1", sizeof("http/1.1") - 1);
+    offset += sizeof("http/1.1") - 1;
+    alpn_buffer[offset++] = sizeof("h2") - 1;
+    memcpy(&alpn_buffer[offset], "h2", sizeof("h2") - 1);
+    offset += sizeof("h2") - 1;
+
+    *list_len = offset - list_start_index;
+    *extension_len = *list_len + sizeof(*extension_len) + sizeof(*list_len);
+
+    buffers[2].pBuffers[0].BufferType = SECBUFFER_APPLICATION_PROTOCOLS;
+    buffers[2].pBuffers[0].cbBuffer = offset;
+
+    buffers[0].pBuffers[0].BufferType = SECBUFFER_TOKEN;
+    status = InitializeSecurityContextA(&cred_handle, NULL, (SEC_CHAR *)"localhost",
+        ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM, 0, 0, &buffers[2], 0, &context, &buffers[0], &attrs, NULL);
+    ok(status == SEC_I_CONTINUE_NEEDED, "got %08x\n", status);
+
+    buf = &buffers[0].pBuffers[0];
+    send(sock, buf->pvBuffer, buf->cbBuffer, 0);
+    buf->cbBuffer = buf_size;
+
+    buf = &buffers[1].pBuffers[0];
+    buf->cbBuffer = buf_size;
+    ret = receive_data(sock, buf);
+    if (ret == -1)
+        return;
+
+    buffers[1].pBuffers[0].BufferType = SECBUFFER_TOKEN;
+    status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost",
+        ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM|ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, NULL,
+        &buffers[0], &attrs, NULL);
+    buffers[1].pBuffers[0].cbBuffer = buf_size;
+    while (status == SEC_I_CONTINUE_NEEDED)
+    {
+        buf = &buffers[0].pBuffers[0];
+        send(sock, buf->pvBuffer, buf->cbBuffer, 0);
+        buf->cbBuffer = buf_size;
+
+        buf = &buffers[1].pBuffers[0];
+        ret = receive_data(sock, buf);
+        if (ret == -1)
+            return;
+
+        buf->BufferType = SECBUFFER_TOKEN;
+        status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost",
+            ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, NULL, &buffers[0], &attrs, NULL);
+        buffers[1].pBuffers[0].cbBuffer = buf_size;
+    }
+
+    ok (status == SEC_E_OK || broken(status == SEC_E_ILLEGAL_MESSAGE) /* winxp */, "got %08x\n", status);
+    if (status != SEC_E_OK)
+    {
+        skip("Handshake failed\n");
+        return;
+    }
+
+    memset(&protocol, 0, sizeof(protocol));
+    status = pQueryContextAttributesA(&context, SECPKG_ATTR_APPLICATION_PROTOCOL, &protocol);
+    ok(status == SEC_E_OK || broken(status == SEC_E_UNSUPPORTED_FUNCTION) /* win2k8 */, "got %08x\n", status);
+    if (status == SEC_E_OK)
+    {
+        ok(protocol.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success, "got %u\n", protocol.ProtoNegoStatus);
+        ok(protocol.ProtoNegoExt == SecApplicationProtocolNegotiationExt_ALPN, "got %u\n", protocol.ProtoNegoExt);
+        ok(protocol.ProtocolIdSize == 8, "got %u\n", protocol.ProtocolIdSize);
+        ok(!memcmp(protocol.ProtocolId, "http/1.1", 8), "wrong protocol id\n");
+    }
+
+    DeleteSecurityContext(&context);
+    FreeCredentialsHandle(&cred_handle);
+
+    free_buffers(&buffers[0]);
+    free_buffers(&buffers[1]);
+    free_buffers(&buffers[2]);
+
+    closesocket(sock);
+}
+
 START_TEST(schannel)
 {
+    WSADATA wsa_data;
     pQueryContextAttributesA = (void*)GetProcAddress(GetModuleHandleA("secur32.dll"), "QueryContextAttributesA");
 
+    WSAStartup(0x0202, &wsa_data);
+
     test_cread_attrs();
     testAcquireSecurityContext();
     test_InitializeSecurityContext();
     test_communication();
+    test_application_protocol_negotiation();
 }
diff --git a/include/sspi.h b/include/sspi.h
index 036d2cf2f8..e7371f129d 100644
--- a/include/sspi.h
+++ b/include/sspi.h
@@ -201,6 +201,7 @@ typedef struct _SecBuffer
 #define SECBUFFER_MECHLIST_SIGNATURE 12
 #define SECBUFFER_TARGET             13
 #define SECBUFFER_CHANNEL_BINDINGS   14
+#define SECBUFFER_APPLICATION_PROTOCOLS 18
 
 #define SECBUFFER_ATTRMASK               0xf0000000
 #define SECBUFFER_READONLY               0x80000000
@@ -497,6 +498,7 @@ typedef SECURITY_STATUS (SEC_ENTRY *QUERY_CONTEXT_ATTRIBUTES_FN_W)(PCtxtHandle,
 #define SECPKG_ATTR_NEGO_PKG_INFO      31
 #define SECPKG_ATTR_NEGO_STATUS        32
 #define SECPKG_ATTR_CONTEXT_DELETED    33
+#define SECPKG_ATTR_APPLICATION_PROTOCOL 35
 
 #define SECPKG_ATTR_SUBJECT_SECURITY_ATTRIBUTES 128
 #define SECPKG_ATTR_NEGO_INFO_FLAG_NO_KERBEROS 0x1
@@ -712,6 +714,30 @@ typedef struct _SecPkgContext_Bindings
     SEC_CHANNEL_BINDINGS *Bindings;
 } SecPkgContext_Bindings, *PSecPkgContext_Bindings;
 
+typedef enum _SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS
+{
+    SecApplicationProtocolNegotiationStatus_None,
+    SecApplicationProtocolNegotiationStatus_Success,
+    SecApplicationProtocolNegotiationStatus_SelectedClientOnly
+} SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS, *PSEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS;
+
+typedef enum _SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT
+{
+    SecApplicationProtocolNegotiationExt_None,
+    SecApplicationProtocolNegotiationExt_NPN,
+    SecApplicationProtocolNegotiationExt_ALPN
+} SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT, *PSEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT;
+
+#define MAX_PROTOCOL_ID_SIZE    0xff
+
+typedef struct _SecPkgContext_ApplicationProtocol
+{
+    SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS ProtoNegoStatus;
+    SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT ProtoNegoExt;
+    unsigned char ProtocolIdSize;
+    unsigned char ProtocolId[MAX_PROTOCOL_ID_SIZE];
+} SecPkgContext_ApplicationProtocol, *PSecPkgContext_ApplicationProtocol;
+
 SECURITY_STATUS SEC_ENTRY ImpersonateSecurityContext(PCtxtHandle phContext);
 
 typedef SECURITY_STATUS (SEC_ENTRY *IMPERSONATE_SECURITY_CONTEXT_FN)
-- 
2.20.1




More information about the wine-devel mailing list