secur32: Add TLS application protocol negotiation support.
Signed-off-by: Hans Leidekker <hans@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
86d20a47f4
commit
0527cf89fb
|
@ -786,6 +786,8 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
|
||||||
struct schan_credentials *cred;
|
struct schan_credentials *cred;
|
||||||
SIZE_T expected_size = ~0UL;
|
SIZE_T expected_size = ~0UL;
|
||||||
SECURITY_STATUS ret;
|
SECURITY_STATUS ret;
|
||||||
|
SecBuffer *buffer;
|
||||||
|
int idx;
|
||||||
|
|
||||||
TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext,
|
TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext,
|
||||||
debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
|
debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
|
||||||
|
@ -842,6 +844,13 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
|
||||||
heap_free( target );
|
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->dwLower = handle;
|
||||||
phNewContext->dwUpper = 0;
|
phNewContext->dwUpper = 0;
|
||||||
}
|
}
|
||||||
|
@ -849,8 +858,6 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
|
||||||
{
|
{
|
||||||
SIZE_T record_size = 0;
|
SIZE_T record_size = 0;
|
||||||
unsigned char *ptr;
|
unsigned char *ptr;
|
||||||
SecBuffer *buffer;
|
|
||||||
int idx;
|
|
||||||
|
|
||||||
if (!pInput)
|
if (!pInput)
|
||||||
return SEC_E_INCOMPLETE_MESSAGE;
|
return SEC_E_INCOMPLETE_MESSAGE;
|
||||||
|
@ -1003,6 +1010,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
|
||||||
PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
|
PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
|
||||||
{
|
{
|
||||||
struct schan_context *ctx;
|
struct schan_context *ctx;
|
||||||
|
SECURITY_STATUS status;
|
||||||
|
|
||||||
TRACE("context_handle %p, attribute %#x, buffer %p\n",
|
TRACE("context_handle %p, attribute %#x, buffer %p\n",
|
||||||
context_handle, attribute, buffer);
|
context_handle, attribute, buffer);
|
||||||
|
@ -1015,7 +1023,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
|
||||||
case SECPKG_ATTR_STREAM_SIZES:
|
case SECPKG_ATTR_STREAM_SIZES:
|
||||||
{
|
{
|
||||||
SecPkgContext_ConnectionInfo info;
|
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)
|
if (status == SEC_E_OK)
|
||||||
{
|
{
|
||||||
SecPkgContext_StreamSizes *stream_sizes = buffer;
|
SecPkgContext_StreamSizes *stream_sizes = buffer;
|
||||||
|
@ -1039,7 +1047,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
|
||||||
case SECPKG_ATTR_KEY_INFO:
|
case SECPKG_ATTR_KEY_INFO:
|
||||||
{
|
{
|
||||||
SecPkgContext_ConnectionInfo conn_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)
|
if (status == SEC_E_OK)
|
||||||
{
|
{
|
||||||
SecPkgContext_KeyInfoW *info = buffer;
|
SecPkgContext_KeyInfoW *info = buffer;
|
||||||
|
@ -1054,7 +1062,6 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
|
||||||
case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
|
case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
|
||||||
{
|
{
|
||||||
PCCERT_CONTEXT *cert = buffer;
|
PCCERT_CONTEXT *cert = buffer;
|
||||||
SECURITY_STATUS status;
|
|
||||||
|
|
||||||
status = ensure_remote_cert(ctx);
|
status = ensure_remote_cert(ctx);
|
||||||
if(status != SEC_E_OK)
|
if(status != SEC_E_OK)
|
||||||
|
@ -1075,7 +1082,6 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
|
||||||
ALG_ID hash_alg = CALG_SHA_256;
|
ALG_ID hash_alg = CALG_SHA_256;
|
||||||
BYTE hash[1024];
|
BYTE hash[1024];
|
||||||
DWORD hash_size;
|
DWORD hash_size;
|
||||||
SECURITY_STATUS status;
|
|
||||||
char *p;
|
char *p;
|
||||||
BOOL r;
|
BOOL r;
|
||||||
|
|
||||||
|
@ -1109,6 +1115,11 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
|
||||||
memcpy(p, hash, hash_size);
|
memcpy(p, hash, hash_size);
|
||||||
return SEC_E_OK;
|
return SEC_E_OK;
|
||||||
}
|
}
|
||||||
|
case SECPKG_ATTR_APPLICATION_PROTOCOL:
|
||||||
|
{
|
||||||
|
SecPkgContext_ApplicationProtocol *protocol = buffer;
|
||||||
|
return schan_imp_get_application_protocol(ctx->session, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
FIXME("Unhandled attribute %#x\n", attribute);
|
FIXME("Unhandled attribute %#x\n", attribute);
|
||||||
|
@ -1143,6 +1154,8 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesA(
|
||||||
return schan_QueryContextAttributesW(context_handle, attribute, buffer);
|
return schan_QueryContextAttributesW(context_handle, attribute, buffer);
|
||||||
case SECPKG_ATTR_ENDPOINT_BINDINGS:
|
case SECPKG_ATTR_ENDPOINT_BINDINGS:
|
||||||
return schan_QueryContextAttributesW(context_handle, attribute, buffer);
|
return schan_QueryContextAttributesW(context_handle, attribute, buffer);
|
||||||
|
case SECPKG_ATTR_APPLICATION_PROTOCOL:
|
||||||
|
return schan_QueryContextAttributesW(context_handle, attribute, buffer);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
FIXME("Unhandled attribute %#x\n", attribute);
|
FIXME("Unhandled attribute %#x\n", attribute);
|
||||||
|
|
|
@ -50,6 +50,11 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
|
||||||
/* Not present in gnutls version < 2.9.10. */
|
/* Not present in gnutls version < 2.9.10. */
|
||||||
static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t);
|
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. */
|
/* Not present in gnutls version < 3.3.0. */
|
||||||
static int (*pgnutls_privkey_import_rsa_raw)(gnutls_privkey_t, const gnutls_datum_t *,
|
static int (*pgnutls_privkey_import_rsa_raw)(gnutls_privkey_t, const gnutls_datum_t *,
|
||||||
const gnutls_datum_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
|
#define GNUTLS_KX_ECDHE_PSK 14
|
||||||
#endif
|
#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)
|
static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
|
||||||
{
|
{
|
||||||
switch(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;
|
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,
|
static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
|
||||||
void *buff, size_t buff_len)
|
void *buff, size_t buff_len)
|
||||||
{
|
{
|
||||||
|
@ -599,6 +621,86 @@ again:
|
||||||
return SEC_E_OK;
|
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 WCHAR *get_key_container_path(const CERT_CONTEXT *ctx)
|
||||||
{
|
{
|
||||||
static const WCHAR rsabaseW[] =
|
static const WCHAR rsabaseW[] =
|
||||||
|
@ -935,6 +1037,16 @@ BOOL schan_imp_init(void)
|
||||||
WARN("gnutls_cipher_get_block_size not found\n");
|
WARN("gnutls_cipher_get_block_size not found\n");
|
||||||
pgnutls_cipher_get_block_size = compat_cipher_get_block_size;
|
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")))
|
if (!(pgnutls_privkey_export_x509 = dlsym(libgnutls_handle, "gnutls_privkey_export_x509")))
|
||||||
{
|
{
|
||||||
WARN("gnutls_privkey_export_x509 not found\n");
|
WARN("gnutls_privkey_export_x509 not found\n");
|
||||||
|
|
|
@ -248,6 +248,8 @@ extern void schan_imp_free_certificate_credentials(schan_credentials*) DECLSPEC_
|
||||||
extern DWORD schan_imp_enabled_protocols(void) DECLSPEC_HIDDEN;
|
extern DWORD schan_imp_enabled_protocols(void) DECLSPEC_HIDDEN;
|
||||||
extern BOOL schan_imp_init(void) DECLSPEC_HIDDEN;
|
extern BOOL schan_imp_init(void) DECLSPEC_HIDDEN;
|
||||||
extern void schan_imp_deinit(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__ */
|
#endif /* ndef __SECUR32_PRIV_H__ */
|
||||||
|
|
|
@ -670,14 +670,41 @@ static void test_InitializeSecurityContext(void)
|
||||||
FreeCredentialsHandle(&cred_handle);
|
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)
|
static void test_communication(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
WSADATA wsa_data;
|
|
||||||
SOCKET sock;
|
SOCKET sock;
|
||||||
struct hostent *host;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
|
|
||||||
SECURITY_STATUS status;
|
SECURITY_STATUS status;
|
||||||
ULONG attrs;
|
ULONG attrs;
|
||||||
|
@ -705,36 +732,7 @@ static void test_communication(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a socket and connect to test.winehq.org */
|
/* Create a socket and connect to test.winehq.org */
|
||||||
ret = WSAStartup(0x0202, &wsa_data);
|
if ((sock = create_ssl_socket( "test.winehq.org" )) == -1) return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create client credentials */
|
/* Create client credentials */
|
||||||
init_cred(&cred);
|
init_cred(&cred);
|
||||||
|
@ -952,7 +950,7 @@ todo_wine
|
||||||
status = pQueryContextAttributesA(&context, SECPKG_ATTR_STREAM_SIZES, &sizes);
|
status = pQueryContextAttributesA(&context, SECPKG_ATTR_STREAM_SIZES, &sizes);
|
||||||
ok(status == SEC_E_OK, "QueryContextAttributesW(SECPKG_ATTR_STREAM_SIZES) failed: %08x\n", status);
|
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);
|
ok(status == SEC_E_UNSUPPORTED_FUNCTION, "QueryContextAttributesA returned %08x\n", status);
|
||||||
|
|
||||||
reset_buffers(&buffers[0]);
|
reset_buffers(&buffers[0]);
|
||||||
|
@ -1037,12 +1035,142 @@ todo_wine
|
||||||
closesocket(sock);
|
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)
|
START_TEST(schannel)
|
||||||
{
|
{
|
||||||
|
WSADATA wsa_data;
|
||||||
pQueryContextAttributesA = (void*)GetProcAddress(GetModuleHandleA("secur32.dll"), "QueryContextAttributesA");
|
pQueryContextAttributesA = (void*)GetProcAddress(GetModuleHandleA("secur32.dll"), "QueryContextAttributesA");
|
||||||
|
|
||||||
|
WSAStartup(0x0202, &wsa_data);
|
||||||
|
|
||||||
test_cread_attrs();
|
test_cread_attrs();
|
||||||
testAcquireSecurityContext();
|
testAcquireSecurityContext();
|
||||||
test_InitializeSecurityContext();
|
test_InitializeSecurityContext();
|
||||||
test_communication();
|
test_communication();
|
||||||
|
test_application_protocol_negotiation();
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,7 @@ typedef struct _SecBuffer
|
||||||
#define SECBUFFER_MECHLIST_SIGNATURE 12
|
#define SECBUFFER_MECHLIST_SIGNATURE 12
|
||||||
#define SECBUFFER_TARGET 13
|
#define SECBUFFER_TARGET 13
|
||||||
#define SECBUFFER_CHANNEL_BINDINGS 14
|
#define SECBUFFER_CHANNEL_BINDINGS 14
|
||||||
|
#define SECBUFFER_APPLICATION_PROTOCOLS 18
|
||||||
|
|
||||||
#define SECBUFFER_ATTRMASK 0xf0000000
|
#define SECBUFFER_ATTRMASK 0xf0000000
|
||||||
#define SECBUFFER_READONLY 0x80000000
|
#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_PKG_INFO 31
|
||||||
#define SECPKG_ATTR_NEGO_STATUS 32
|
#define SECPKG_ATTR_NEGO_STATUS 32
|
||||||
#define SECPKG_ATTR_CONTEXT_DELETED 33
|
#define SECPKG_ATTR_CONTEXT_DELETED 33
|
||||||
|
#define SECPKG_ATTR_APPLICATION_PROTOCOL 35
|
||||||
|
|
||||||
#define SECPKG_ATTR_SUBJECT_SECURITY_ATTRIBUTES 128
|
#define SECPKG_ATTR_SUBJECT_SECURITY_ATTRIBUTES 128
|
||||||
#define SECPKG_ATTR_NEGO_INFO_FLAG_NO_KERBEROS 0x1
|
#define SECPKG_ATTR_NEGO_INFO_FLAG_NO_KERBEROS 0x1
|
||||||
|
@ -712,6 +714,30 @@ typedef struct _SecPkgContext_Bindings
|
||||||
SEC_CHANNEL_BINDINGS *Bindings;
|
SEC_CHANNEL_BINDINGS *Bindings;
|
||||||
} SecPkgContext_Bindings, *PSecPkgContext_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);
|
SECURITY_STATUS SEC_ENTRY ImpersonateSecurityContext(PCtxtHandle phContext);
|
||||||
|
|
||||||
typedef SECURITY_STATUS (SEC_ENTRY *IMPERSONATE_SECURITY_CONTEXT_FN)
|
typedef SECURITY_STATUS (SEC_ENTRY *IMPERSONATE_SECURITY_CONTEXT_FN)
|
||||||
|
|
Loading…
Reference in New Issue