[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