static int netconn_secure_verify( int preverify_ok, X509_STORE_CTX *ctx ) { SSL *ssl; WCHAR *server; BOOL ret = FALSE; netconn_t *conn; ssl = pX509_STORE_CTX_get_ex_data( ctx, pSSL_get_ex_data_X509_STORE_CTX_idx() ); server = pSSL_get_ex_data( ssl, hostname_idx ); conn = pSSL_get_ex_data( ssl, conn_idx ); if (preverify_ok) { HCERTSTORE store = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL ); if (store) { X509 *cert; int i; PCCERT_CONTEXT endCert = NULL; ret = TRUE; for (i = 0; ret && i < psk_num((struct stack_st *)ctx->chain); i++) { PCCERT_CONTEXT context; cert = (X509 *)psk_value((struct stack_st *)ctx->chain, i); if ((context = X509_to_cert_context( cert ))) { if (i == 0) ret = CertAddCertificateContextToStore( store, context, CERT_STORE_ADD_ALWAYS, &endCert ); else ret = CertAddCertificateContextToStore( store, context, CERT_STORE_ADD_ALWAYS, NULL ); CertFreeCertificateContext( context ); } } if (!endCert) ret = FALSE; if (ret) { DWORD_PTR err = netconn_verify_cert( endCert, store, server, conn->security_flags ); if (err) { pSSL_set_ex_data( ssl, error_idx, (void *)err ); ret = FALSE; } } CertFreeCertificateContext( endCert ); CertCloseStore( store, 0 ); } } return ret; }
static DWORD netcon_secure_connect_setup(netconn_t *connection, BOOL compat_mode) { SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}}; SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs}; SecHandle *cred = &cred_handle; BYTE *read_buf; SIZE_T read_buf_size = 2048; ULONG attrs = 0; CtxtHandle ctx; SSIZE_T size; int bits; const CERT_CONTEXT *cert; SECURITY_STATUS status; DWORD res = ERROR_SUCCESS; const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION; if(!ensure_cred_handle()) return ERROR_INTERNET_SECURITY_CHANNEL_ERROR; if(compat_mode) { if(!have_compat_cred_handle) return ERROR_INTERNET_SECURITY_CHANNEL_ERROR; cred = &compat_cred_handle; } read_buf = heap_alloc(read_buf_size); if(!read_buf) return ERROR_OUTOFMEMORY; status = InitializeSecurityContextW(cred, NULL, connection->server->name, isc_req_flags, 0, 0, NULL, 0, &ctx, &out_desc, &attrs, NULL); assert(status != SEC_E_OK); while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) { if(out_buf.cbBuffer) { assert(status == SEC_I_CONTINUE_NEEDED); TRACE("sending %lu bytes\n", out_buf.cbBuffer); size = sock_send(connection->socket, out_buf.pvBuffer, out_buf.cbBuffer, 0); if(size != out_buf.cbBuffer) { ERR("send failed\n"); status = ERROR_INTERNET_SECURITY_CHANNEL_ERROR; break; } FreeContextBuffer(out_buf.pvBuffer); out_buf.pvBuffer = NULL; out_buf.cbBuffer = 0; } if(status == SEC_I_CONTINUE_NEEDED) { assert(in_bufs[1].cbBuffer < read_buf_size); memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer); in_bufs[0].cbBuffer = in_bufs[1].cbBuffer; in_bufs[1].BufferType = SECBUFFER_EMPTY; in_bufs[1].cbBuffer = 0; in_bufs[1].pvBuffer = NULL; } assert(in_bufs[0].BufferType == SECBUFFER_TOKEN); assert(in_bufs[1].BufferType == SECBUFFER_EMPTY); if(in_bufs[0].cbBuffer + 1024 > read_buf_size) { BYTE *new_read_buf; new_read_buf = heap_realloc(read_buf, read_buf_size + 1024); if(!new_read_buf) { status = E_OUTOFMEMORY; break; } in_bufs[0].pvBuffer = read_buf = new_read_buf; read_buf_size += 1024; } size = sock_recv(connection->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0); if(size < 1) { WARN("recv error\n"); res = ERROR_INTERNET_SECURITY_CHANNEL_ERROR; break; } TRACE("recv %tu bytes\n", size); in_bufs[0].cbBuffer += size; in_bufs[0].pvBuffer = read_buf; status = InitializeSecurityContextW(cred, &ctx, connection->server->name, isc_req_flags, 0, 0, &in_desc, 0, NULL, &out_desc, &attrs, NULL); TRACE("InitializeSecurityContext ret %08x\n", status); if(status == SEC_E_OK) { if(SecIsValidHandle(&connection->ssl_ctx)) DeleteSecurityContext(&connection->ssl_ctx); connection->ssl_ctx = ctx; if(in_bufs[1].BufferType == SECBUFFER_EXTRA) FIXME("SECBUFFER_EXTRA not supported\n"); status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &connection->ssl_sizes); if(status != SEC_E_OK) { WARN("Could not get sizes\n"); break; } status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert); if(status == SEC_E_OK) { res = netconn_verify_cert(connection, cert, cert->hCertStore); CertFreeCertificateContext(cert); if(res != ERROR_SUCCESS) { WARN("cert verify failed: %u\n", res); break; } }else { WARN("Could not get cert\n"); break; } connection->ssl_buf = heap_alloc(connection->ssl_sizes.cbHeader + connection->ssl_sizes.cbMaximumMessage + connection->ssl_sizes.cbTrailer); if(!connection->ssl_buf) { res = GetLastError(); break; } } } heap_free(read_buf); if(status != SEC_E_OK || res != ERROR_SUCCESS) { WARN("Failed to establish SSL connection: %08x (%u)\n", status, res); heap_free(connection->ssl_buf); connection->ssl_buf = NULL; return res ? res : ERROR_INTERNET_SECURITY_CHANNEL_ERROR; } TRACE("established SSL connection\n"); connection->secure = TRUE; connection->security_flags |= SECURITY_FLAG_SECURE; bits = NETCON_GetCipherStrength(connection); if (bits >= 128) connection->security_flags |= SECURITY_FLAG_STRENGTH_STRONG; else if (bits >= 56) connection->security_flags |= SECURITY_FLAG_STRENGTH_MEDIUM; else connection->security_flags |= SECURITY_FLAG_STRENGTH_WEAK; if(connection->mask_errors) connection->server->security_flags = connection->security_flags; return ERROR_SUCCESS; }
BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname ) { SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}}; SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs}; BYTE *read_buf; SIZE_T read_buf_size = 2048; ULONG attrs = 0; CtxtHandle ctx; SSIZE_T size; const CERT_CONTEXT *cert; SECURITY_STATUS status; DWORD res = ERROR_SUCCESS; const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION; if(!ensure_cred_handle()) return FALSE; read_buf = heap_alloc(read_buf_size); if(!read_buf) return FALSE; status = InitializeSecurityContextW(&cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0, &ctx, &out_desc, &attrs, NULL); assert(status != SEC_E_OK); while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) { if(out_buf.cbBuffer) { assert(status == SEC_I_CONTINUE_NEEDED); TRACE("sending %u bytes\n", out_buf.cbBuffer); size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, 0); if(size != out_buf.cbBuffer) { ERR("send failed\n"); res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR; break; } FreeContextBuffer(out_buf.pvBuffer); out_buf.pvBuffer = NULL; out_buf.cbBuffer = 0; } if(status == SEC_I_CONTINUE_NEEDED) { assert(in_bufs[1].cbBuffer < read_buf_size); memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer); in_bufs[0].cbBuffer = in_bufs[1].cbBuffer; in_bufs[1].BufferType = SECBUFFER_EMPTY; in_bufs[1].cbBuffer = 0; in_bufs[1].pvBuffer = NULL; } assert(in_bufs[0].BufferType == SECBUFFER_TOKEN); assert(in_bufs[1].BufferType == SECBUFFER_EMPTY); if(in_bufs[0].cbBuffer + 1024 > read_buf_size) { BYTE *new_read_buf; new_read_buf = heap_realloc(read_buf, read_buf_size + 1024); if(!new_read_buf) { status = E_OUTOFMEMORY; break; } in_bufs[0].pvBuffer = read_buf = new_read_buf; read_buf_size += 1024; } size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0); if(size < 1) { WARN("recv error\n"); status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR; break; } TRACE("recv %lu bytes\n", size); in_bufs[0].cbBuffer += size; in_bufs[0].pvBuffer = read_buf; status = InitializeSecurityContextW(&cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc, 0, NULL, &out_desc, &attrs, NULL); TRACE("InitializeSecurityContext ret %08x\n", status); if(status == SEC_E_OK) { if(in_bufs[1].BufferType == SECBUFFER_EXTRA) FIXME("SECBUFFER_EXTRA not supported\n"); status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes); if(status != SEC_E_OK) { WARN("Could not get sizes\n"); break; } status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert); if(status == SEC_E_OK) { res = netconn_verify_cert(cert, hostname, conn->security_flags); CertFreeCertificateContext(cert); if(res != ERROR_SUCCESS) { WARN("cert verify failed: %u\n", res); break; } } else { WARN("Could not get cert\n"); break; } conn->ssl_buf = heap_alloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer); if(!conn->ssl_buf) { res = GetLastError(); break; } } } heap_free(read_buf); if(status != SEC_E_OK || res != ERROR_SUCCESS) { WARN("Failed to initialize security context failed: %08x\n", status); heap_free(conn->ssl_buf); conn->ssl_buf = NULL; DeleteSecurityContext(&ctx); set_last_error(res ? res : ERROR_WINHTTP_SECURE_CHANNEL_ERROR); return FALSE; } TRACE("established SSL connection\n"); conn->secure = TRUE; conn->ssl_ctx = ctx; return TRUE; }