BOOL MSCAPI_Manager::BuildCertificateChain(HCRYPTPROV provider, OpString &label, OpString &shortname, SSL_ASN1Cert_list &cert) { CERT_PUBLIC_KEY_INFO *pubkey = (CERT_PUBLIC_KEY_INFO *) g_memory_manager->GetTempBuf2k(); DWORD len; label.Empty(); shortname.Empty(); cert.Resize(0); if(!hMYSystemStore) return FALSE; len = g_memory_manager->GetTempBuf2kLen(); if(!CryptExportPublicKeyInfo(provider, /*AT_SIGNATURE*/ AT_KEYEXCHANGE, (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), pubkey, &len)) { int err0 = GetLastError(); op_memset(&pubkey, 0, sizeof(pubkey)); len = g_memory_manager->GetTempBufLen(); if(!CryptExportPublicKeyInfo(provider, /* AT_KEYEXCHANGE */ AT_SIGNATURE, (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), pubkey, &len)) { int err = GetLastError(); return FALSE; } } PCCERT_CONTEXT cert_item = NULL; cert_item = CertFindCertificateInStore(hMYSystemStore, (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), 0, CERT_FIND_PUBLIC_KEY, pubkey, cert_item); if(!cert_item && hUserDSSystemStore) cert_item = CertFindCertificateInStore(hUserDSSystemStore, (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), 0, CERT_FIND_PUBLIC_KEY, pubkey, cert_item); if(!cert_item) return FALSE; len = CertNameToStr((X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), &cert_item->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0); if(len) { if(shortname.Reserve(len+1) == NULL) return OpStatus::ERR_NO_MEMORY; len = CertNameToStr((X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), &cert_item->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, shortname.DataPtr(), shortname.Capacity()); } cert.Resize(1); if(cert.Error()) return FALSE(); cert[0].Set(cert_item->pbCertEncoded, cert_item->cbCertEncoded); if(cert.Error() || cert[0].GetLength() == 0) return FALSE; return TRUE; }
HRESULT CLocalCertStoreImp::GetDecodedCertName(PCERT_NAME_BLOB pCertName, DWORD dwStrType, LPTSTR szCertName, ULONG nBufferMax) { DWORD dwDataLength = 0; PWCHAR pString = NULL; HRESULT hr = S_OK; if (pCertName == NULL) { return E_INVALIDARG; } // find the length first dwDataLength = ::CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertName, dwStrType, NULL, 0); if (dwDataLength == 0) { return E_FAIL; } pString = new WCHAR[dwDataLength]; if (pString == NULL) { return E_OUTOFMEMORY; } CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertName, dwStrType, pString, dwDataLength); StringCbCopy(szCertName, nBufferMax, pString); // perform cleanup delete[] pString; pString = NULL; return hr; }
tstring Win32Certificate::getIssuerName() { DWORD bufferSize = CertNameToStr( X509_ASN_ENCODING, &mpCert->pCertInfo->Issuer, CERT_X500_NAME_STR, NULL, 0); std::vector<TCHAR> buffer(bufferSize); bufferSize = CertNameToStr( X509_ASN_ENCODING, &mpCert->pCertInfo->Issuer, CERT_X500_NAME_STR, &buffer[0], bufferSize); tstring strName(&buffer[0]); return strName; }
int ma_tls_verify_server_cert(MARIADB_TLS *ctls) { SC_CTX *sctx= (SC_CTX *)ctls->ssl; MARIADB_PVIO *pvio= ctls->pvio; int rc= 1; char *szName= NULL; char *pszServerName= pvio->mysql->host; /* check server name */ if (pszServerName && (sctx->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) { PCCERT_CONTEXT pServerCert; DWORD NameSize= 0; char *p1, *p2; SECURITY_STATUS sRet; if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK) { ma_schannel_set_sec_error(pvio, sRet); return 1; } if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType, &pServerCert->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, NULL, 0))) { pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate"); return 1; } if (!(szName= (char *)LocalAlloc(0, NameSize + 1))) { pvio->set_error(sctx->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL); goto end; } if (!CertNameToStr(pServerCert->dwCertEncodingType, &pServerCert->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, szName, NameSize)) { pvio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate"); goto end; } if ((p1 = strstr(szName, "CN="))) { p1+= 3; if ((p2= strstr(p1, ", "))) *p2= 0; if (!strcmp(pszServerName, p1)) { rc= 0; goto end; } pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Name of server certificate didn't match"); } } end: if (szName) LocalFree(szName); return rc; }
static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags) { SECURITY_STATUS e; ULONG a; TimeStamp t; SecBuffer ibuf[2],obuf[1]; SecBufferDesc ibufs,obufs; SCHANNEL_CRED tlscred; CERT_CONTEXT *cert; CERT_CHAIN_PARA chparam; CERT_CHAIN_CONTEXT *chain; SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy; CERT_CHAIN_POLICY_PARA polparam; CERT_CHAIN_POLICY_STATUS status; char tmp[MAILTMPLEN],certname[256]; char *reason = NIL; ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | ISC_REQ_MANUAL_CRED_VALIDATION; LPSTR usage[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; PWSTR whost = NIL; char *buf = (char *) fs_get (ssltsz); unsigned long size = 0; sslcertificatequery_t scq = (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL); SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); stream->tcpstream = tstream; /* bind TCP stream */ /* initialize TLS credential */ memset (&tlscred,0,sizeof (SCHANNEL_CRED)); tlscred.dwVersion = SCHANNEL_CRED_VERSION; tlscred.grbitEnabledProtocols = SP_PROT_TLS1; /* acquire credentials */ if (AcquireCredentialsHandle (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ? &tlscred : NIL,NIL,NIL,&stream->cred,&t) != SEC_E_OK) reason = "Acquire credentials handle failed"; else while (!reason) { /* negotiate security context */ /* initialize buffers */ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf; ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL; obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL; ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; ibuf[1].BufferType = SECBUFFER_EMPTY; /* initialize buffer descriptors */ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; ibufs.cBuffers = 2; obufs.cBuffers = 1; ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; /* negotiate security */ e = InitializeSecurityContext (&stream->cred,size ? &stream->context : NIL,host,req,0, SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t); /* have an output buffer we need to send? */ if (obuf[0].pvBuffer && obuf[0].cbBuffer) { if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer)) reason = "Unexpected TCP output disconnect"; /* free the buffer */ FreeContextBuffer (obuf[0].pvBuffer); } if (!reason) switch (e) { /* negotiation state */ case SEC_I_INCOMPLETE_CREDENTIALS: break; /* server wants client auth */ case SEC_I_CONTINUE_NEEDED: if (size) { /* continue, read any data? */ /* yes, anything regurgiated back to us? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); size = ibuf[1].cbBuffer; break; } size = 0; /* otherwise, read more stuff from server */ } case SEC_E_INCOMPLETE_MESSAGE: /* need to read more data from server */ if (!tcp_getdata (stream->tcpstream)) reason = "Unexpected TCP input disconnect"; else { memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr); size += stream->tcpstream->ictr; /* empty it from TCP's buffers */ stream->tcpstream->iptr += stream->tcpstream->ictr; stream->tcpstream->ictr = 0; } break; case SEC_E_OK: /* success, any data to be regurgitated? */ if (ibuf[1].BufferType == SECBUFFER_EXTRA) { /* yes, set this as the new data */ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf, buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); stream->tcpstream->ictr = ibuf[1].cbBuffer; } if (!(flags & NET_NOVALIDATECERT)) { /* need validation, make wchar of host */ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) && (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) && MultiByteToWideChar (CP_ACP,0,host,-1,whost,size))) fatal ("Can't make wchar of host name!"); /* get certificate */ if ((QueryContextAttributes (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) != SEC_E_OK) || !cert) { reason = "*Unable to get certificate"; strcpy (certname,"<no certificate>"); } else { /* get certificate subject name */ CertNameToStr (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &cert->pCertInfo->Subject,CERT_X500_NAME_STR, certname,255); /* build certificate chain */ memset (&chparam,0,sizeof (chparam)); chparam.cbSize = sizeof (chparam); chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage; chparam.RequestedUsage.Usage.cUsageIdentifier = sizeof (usage) / sizeof (LPSTR); if (!CertGetCertificateChain (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain)) reason = ssl_analyze_status (GetLastError (),tmp); else { /* validate certificate chain */ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA)); policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA); policy.dwAuthType = AUTHTYPE_SERVER; policy.fdwChecks = NIL; policy.pwszServerName = whost; memset (&polparam,0,sizeof (polparam)); polparam.cbSize = sizeof (polparam); polparam.pvExtraPolicyPara = &policy; memset (&status,0,sizeof (status)); status.cbSize = sizeof (status); if (!CertVerifyCertificateChainPolicy (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status)) reason = ssl_analyze_status (GetLastError (),tmp); else if (status.dwError) reason = ssl_analyze_status (status.dwError,tmp); CertFreeCertificateChain (chain); } } if (whost) fs_give ((void **) &whost); if (reason) { /* got an error? */ /* application callback */ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason, host,certname) ? NIL : ""; else if (*certname) { /* error message to return via mm_log() */ sprintf (buf,"*%.128s: %.255s", (*reason == '*') ? reason + 1 : reason,certname); reason = buf; } } } if (reason || (reason = ssl_analyze_status (QueryContextAttributes (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf))) break; /* error in certificate or getting sizes */ fs_give ((void **) &buf); /* flush temporary buffer */ /* make maximum-sized buffers */ stream->bufsize = stream->sizes.cbHeader + stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer; if (stream->sizes.cbMaximumMessage < SSLBUFLEN) fatal ("cbMaximumMessage is less than SSLBUFLEN!"); else if (stream->sizes.cbMaximumMessage < 16384) { sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384", (long) stream->sizes.cbMaximumMessage); mm_log (tmp,NIL); } stream->ibuf = (char *) fs_get (stream->bufsize); stream->obuf = (char *) fs_get (stream->bufsize); return stream; default: reason = ssl_analyze_status (e,buf); } } ssl_close (stream); /* failed to do SSL */ stream = NIL; /* no stream returned */ switch (*reason) { /* analyze reason */ case '*': /* certificate failure */ ++reason; /* skip over certificate failure indication */ /* pass to error callback */ if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } case '\0': /* user answered no to certificate callback */ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, sizeof (SSLSTREAM)); break; default: /* non-certificate failure */ if (flags & NET_TRYSSL); /* no error output if tryssl */ /* pass to error callback */ else if (sf) (*sf) (host,reason,flags); else { /* no error callback, build error message */ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason); mm_log (tmp,ERROR); } break; } fs_give ((void **) &buf); /* flush temporary buffer */ return stream; }
void SchannelCertificate::parse() { // // Subject name // DWORD requiredSize = CertNameToStr(X509_ASN_ENCODING, &m_cert->pCertInfo->Subject, CERT_OID_NAME_STR, NULL, 0); if (requiredSize > 1) { vector<char> rawSubjectName(requiredSize); CertNameToStr(X509_ASN_ENCODING, &m_cert->pCertInfo->Subject, CERT_OID_NAME_STR, &rawSubjectName[0], rawSubjectName.size()); m_subjectName = std::string(&rawSubjectName[0]); } // // Common name // // Note: We only pull out one common name from the cert. requiredSize = CertGetNameString(m_cert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, NULL, 0); if (requiredSize > 1) { vector<char> rawCommonName(requiredSize); requiredSize = CertGetNameString(m_cert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, &rawCommonName[0], rawCommonName.size()); m_commonNames.push_back( std::string(&rawCommonName[0]) ); } // // Subject alternative names // PCERT_EXTENSION pExtensions = CertFindExtension(szOID_SUBJECT_ALT_NAME2, m_cert->pCertInfo->cExtension, m_cert->pCertInfo->rgExtension); if (pExtensions) { CRYPT_DECODE_PARA decodePara = {0}; decodePara.cbSize = sizeof(decodePara); CERT_ALT_NAME_INFO* pAltNameInfo = NULL; DWORD altNameInfoSize = 0; BOOL status = CryptDecodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_SUBJECT_ALT_NAME2, pExtensions->Value.pbData, pExtensions->Value.cbData, CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, &decodePara, &pAltNameInfo, &altNameInfoSize); if (status && pAltNameInfo) { for (int i = 0; i < pAltNameInfo->cAltEntry; i++) { if (pAltNameInfo->rgAltEntry[i].dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) addDNSName( wstrToStr( pAltNameInfo->rgAltEntry[i].pwszDNSName ) ); } } } // if (pExtensions) // { // vector<wchar_t> subjectAlt // CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_SUBJECT_ALT_NAME, pExtensions->Value->pbData, pExtensions->Value->cbData, ) // } // // // subjectAltNames // int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1); // if(subjectAltNameLoc != -1) { // X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc); // std::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free); // std::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free); // std::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free); // for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) { // GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i); // if (generalName->type == GEN_OTHERNAME) { // OTHERNAME* otherName = generalName->d.otherName; // if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) { // // XmppAddr // if (otherName->value->type != V_ASN1_UTF8STRING) { // continue; // } // ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string; // addXMPPAddress(ByteArray(ASN1_STRING_data(xmppAddrValue), ASN1_STRING_length(xmppAddrValue)).toString()); // } // else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) { // // SRVName // if (otherName->value->type != V_ASN1_IA5STRING) { // continue; // } // ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string; // addSRVName(ByteArray(ASN1_STRING_data(srvNameValue), ASN1_STRING_length(srvNameValue)).toString()); // } // } // else if (generalName->type == GEN_DNS) { // // DNSName // addDNSName(ByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName)).toString()); // } // } // } }
/// /// Disabled all enhanced key usages for the specified certificate. /// void RemoveAllEnhancedKeyUsages(LPTSTR pszStoreName, LPTSTR szSubjectName) { HANDLE hStoreHandle = NULL; PCCERT_CONTEXT pCertContext = NULL; // Open Store. if (hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, pszStoreName)) { _tprintf(TEXT("[i] Successfully opened certificate store '%s'\n"), pszStoreName); } else { ErrorHandler("Failed to open certificate store"); exit(1); } // // Iterate over certificates in this store. while (pCertContext = CertEnumCertificatesInStore(hStoreHandle, pCertContext)) { DWORD cbSize; LPTSTR pszName; // Get the Certificate Subject Name. cbSize = CertNameToStr(pCertContext->dwCertEncodingType, &(pCertContext->pCertInfo->Subject), CERT_OID_NAME_STR, NULL, 0); if (1 == cbSize) { ErrorHandler("Failed to return the subject name of the certificate."); } if (!(pszName = (LPTSTR)malloc(cbSize * sizeof(TCHAR)))) { ErrorHandler("Failed to allocate memory"); } if (CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pszName, cbSize)) { // _tprintf(TEXT("%s\n"), pszName); } else { ErrorHandler("CertGetNameString failed"); } // // Is this the certificate we're looking for? if (_tcscmp(pszName, szSubjectName) == 0) { _tprintf(TEXT("[i] Located certificate %s\n"), pszName); PCERT_ENHKEY_USAGE pEKeyUsage = NULL; if (!(pEKeyUsage = (PCERT_ENHKEY_USAGE)CryptMemAlloc(sizeof(CERT_ENHKEY_USAGE)))) { ErrorHandler("Failed to allocate memory"); } if (CertSetEnhancedKeyUsage(pCertContext, pEKeyUsage)) { _tprintf(TEXT("[i] Removed EKU\n")); } else { ErrorHandler("CertSetEnhancedKeyUsage Failed"); } CryptMemFree(pEKeyUsage); } free(pszName); } // Clean Up if (pCertContext) CertFreeCertificateContext(pCertContext); if (hStoreHandle) { if (!CertCloseStore(hStoreHandle, 0)) ErrorHandler("Failed CertCloseStorn"); } }
// Main entry point int __cdecl wmain() { HRESULT hr = S_OK; WS_ERROR* error = NULL; WS_LISTENER* listener = NULL; WS_CHANNEL* channel = NULL; WS_MESSAGE* requestMessage = NULL; WS_MESSAGE* replyMessage = NULL; WS_HEAP* heap = NULL; WCHAR* certSubjectName = NULL; PCCERT_CONTEXT certContext = NULL; BOOL requireClientCert = TRUE; WS_SECURITY_BINDING_PROPERTY requireClientCertProperty = {}; requireClientCertProperty.id = WS_SECURITY_BINDING_PROPERTY_REQUIRE_SSL_CLIENT_CERT; requireClientCertProperty.value = &requireClientCert; requireClientCertProperty.valueSize = sizeof(requireClientCert); // declare and initialize an SSL transport security binding WS_SSL_TRANSPORT_SECURITY_BINDING sslBinding = {}; // zero out the struct sslBinding.binding.bindingType = WS_SSL_TRANSPORT_SECURITY_BINDING_TYPE; // set the binding type sslBinding.binding.properties = &requireClientCertProperty; sslBinding.binding.propertyCount = 1; // NOTE: At the server, the SSL certificate for the listen URI must be // registered with http.sys using a tool such as httpcfg.exe. // declare and initialize the array of all security bindings WS_SECURITY_BINDING* securityBindings[1] = { &sslBinding.binding }; // declare and initialize the security description WS_SECURITY_DESCRIPTION securityDescription = {}; // zero out the struct securityDescription.securityBindings = securityBindings; securityDescription.securityBindingCount = WsCountOf(securityBindings); static const WS_STRING uri = WS_STRING_VALUE(L"https://localhost:8443/example"); // Create an error object for storing rich error information hr = WsCreateError( NULL, 0, &error); if (FAILED(hr)) { goto Exit; } // Create a heap to store deserialized data hr = WsCreateHeap( /*maxSize*/ 2048, /*trimSize*/ 512, NULL, 0, &heap, error); if (FAILED(hr)) { goto Exit; } // Create a listener hr = WsCreateListener( WS_CHANNEL_TYPE_REPLY, WS_HTTP_CHANNEL_BINDING, NULL, 0, &securityDescription, &listener, error); if (FAILED(hr)) { goto Exit; } // Open listener hr = WsOpenListener(listener, &uri, NULL, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateChannelForListener( listener, NULL, 0, &channel, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( channel, NULL, 0, &requestMessage, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( channel, NULL, 0, &replyMessage, error); if (FAILED(hr)) { goto Exit; } // Receive requests / send replies for (int i = 0; i < 300; i++) { // Accept a request from the client hr = WsAcceptChannel(listener, channel, NULL, error); if (FAILED(hr)) { goto Exit; } // Set up the descriptions of the expected messages. We expect either a purchase order // or a request for order status. const WS_MESSAGE_DESCRIPTION* requestMessageDescriptions[] = { &PurchaseOrder_wsdl.messages.PurchaseOrder, // contains a _PurchaseOrderType in the body &PurchaseOrder_wsdl.messages.GetOrderStatus, // contains a GetOrderStatus in the body }; // Receive the message and deserialize the element of the body into the appropriate // structure, based on the message descriptions. The value of the body will be // allocated in the specified WS_HEAP, and are valid until WsResetHeap is called. void* requestBodyPointer; ULONG indexOfMatchedMessageDescription; hr = WsReceiveMessage(channel, requestMessage, requestMessageDescriptions, WsCountOf(requestMessageDescriptions), WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_POINTER, heap, &requestBodyPointer, sizeof(requestBodyPointer), &indexOfMatchedMessageDescription, NULL, error); // Validate the client certificate. if (SUCCEEDED(hr)) { // Another way to do client cert validation is to use // WS_MESSAGE_PROPERTY_TRANSPORT_SECURITY_WINDOWS_TOKEN here, assuming http.sys // is configured to map the cert into a windows token and was able to map the // cert to a user account. WS_BYTES encodedCert = {}; hr = WsGetMessageProperty( requestMessage, WS_MESSAGE_PROPERTY_ENCODED_CERT, &encodedCert, sizeof(encodedCert), error); if (FAILED(hr)) { goto Exit; } _Analysis_assume_(encodedCert.bytes != NULL); certContext = CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, encodedCert.bytes, encodedCert.length); if (certContext == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } ULONG subjectNameLength = CertNameToStr( certContext->dwCertEncodingType, &certContext->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0); if (subjectNameLength == 0) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } certSubjectName = new WCHAR[subjectNameLength]; if (certSubjectName == NULL) { hr = E_OUTOFMEMORY; goto Exit; } subjectNameLength = CertNameToStr( certContext->dwCertEncodingType, &certContext->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, certSubjectName, subjectNameLength); if (subjectNameLength == 0) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } static const WCHAR expectedCertName[] = L"client.com"; wprintf(L"Expected client cert name: %s\n", expectedCertName); wprintf(L"Received client cert name: %s\n", certSubjectName); if (CompareString( LOCALE_INVARIANT, NORM_IGNORECASE, expectedCertName, (int)::wcslen(expectedCertName), certSubjectName, (int)::wcslen(certSubjectName)) != CSTR_EQUAL) { hr = WS_E_SECURITY_VERIFICATION_FAILURE; goto Exit; } } // Process the request, and generate the reply const WS_MESSAGE_DESCRIPTION* replyMessageDescription = NULL; const void* replyBodyPointer = NULL; ULONG replyBodySize = 0; _OrderConfirmationType orderConfirmation; _GetOrderStatusResponseType getOrderStatusResponse; if (SUCCEEDED(hr)) { // Get the message description that matched const WS_MESSAGE_DESCRIPTION* requestMessageDescription = requestMessageDescriptions[indexOfMatchedMessageDescription]; if (requestMessageDescription == &PurchaseOrder_wsdl.messages.PurchaseOrder) { // The message was a purchase order. Get the pointer to the deserialized value. _PurchaseOrderType* purchaseOrder = (_PurchaseOrderType*)requestBodyPointer; // Print out purchase order contents wprintf(L"%d, %s\n", purchaseOrder->quantity, purchaseOrder->productName); // Initialize order confirmation data orderConfirmation.expectedShipDate = L"1/1/2006"; orderConfirmation.orderID = 123; // Setup up reply message replyMessageDescription = &PurchaseOrder_wsdl.messages.OrderConfirmation; replyBodyPointer = &orderConfirmation; replyBodySize = sizeof(orderConfirmation); } else if (requestMessageDescription == &PurchaseOrder_wsdl.messages.GetOrderStatus) { // The message was a order status request. Get the pointer to the deserialized value. _GetOrderStatusType* getOrderStatus = (_GetOrderStatusType*)requestBodyPointer; // Generate a fault if we don't recognize the order ID if (getOrderStatus->orderID != 123) { // Fill out details about the fault _OrderNotFoundFaultType orderNotFound; orderNotFound.orderID = getOrderStatus->orderID; static const WS_XML_STRING _faultDetailName = WS_XML_STRING_VALUE("OrderNotFound"); static const WS_XML_STRING _faultDetailNs = WS_XML_STRING_VALUE("http://example.com"); static const WS_XML_STRING _faultAction = WS_XML_STRING_VALUE("http://example.com/fault"); static const WS_ELEMENT_DESCRIPTION _faultElementDescription = { (WS_XML_STRING*)&_faultDetailName, (WS_XML_STRING*)&_faultDetailNs, WS_UINT32_TYPE, NULL }; static const WS_FAULT_DETAIL_DESCRIPTION orderNotFoundFaultTypeDescription = { (WS_XML_STRING*)&_faultAction, (WS_ELEMENT_DESCRIPTION*)&_faultElementDescription }; // Set fault detail information in the error object hr = WsSetFaultErrorDetail( error, &orderNotFoundFaultTypeDescription, WS_WRITE_REQUIRED_VALUE, &orderNotFound, sizeof(orderNotFound)); if (FAILED(hr)) { goto Exit; } // Add an error string to the error object. This string will // be included in the fault that is sent. static const WS_STRING errorMessage = WS_STRING_VALUE(L"Invalid order ID"); hr = WsAddErrorString(error, &errorMessage); if (FAILED(hr)) { goto Exit; } // Use a failure code to indicate that a fault should be sent hr = E_FAIL; } else { // Initialize the order status response getOrderStatusResponse.orderID = getOrderStatus->orderID; getOrderStatusResponse.status = L"Pending"; // Specify which message description to use for reply replyMessageDescription = &PurchaseOrder_wsdl.messages.GetOrderStatusResponse; replyBodyPointer = &getOrderStatusResponse; replyBodySize = sizeof(getOrderStatusResponse); } } } // If there was an error receiving the message if (FAILED(hr)) { // Send a fault in the body of the reply message. The information // accumulated in the error object is used to populate the fault. // The error code is not transmitted but instead is used to // generate an error string if no error strings are present in the // error object. hr = WsSendFaultMessageForError( channel, replyMessage, error, hr, WS_FULL_FAULT_DISCLOSURE, requestMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Reset the error so it can be used again hr = WsResetError(error); if (FAILED(hr)) { goto Exit; } // Reset the reply message so it can be used again hr = WsResetMessage(replyMessage, error); if (FAILED(hr)) { goto Exit; } } else { // Send a reply message hr = WsSendReplyMessage( channel, replyMessage, replyMessageDescription, WS_WRITE_REQUIRED_VALUE, replyBodyPointer, replyBodySize, requestMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Reset the reply message so it can be used again hr = WsResetMessage(replyMessage, error); if (FAILED(hr)) { goto Exit; } } // Reset the request message so it can be used again hr = WsResetMessage(requestMessage, error); if (FAILED(hr)) { goto Exit; } // Reset the heap, which will free any allocations made on it hr = WsResetHeap(heap, error); if (FAILED(hr)) { goto Exit; } // Close the request hr = WsCloseChannel(channel, NULL, error); if (FAILED(hr)) { goto Exit; } // Prepare channel for reuse hr = WsResetChannel(channel, error); if (FAILED(hr)) { goto Exit; } // Delete the certificate name string if needed. if (certSubjectName != NULL) { delete[] certSubjectName; certSubjectName = NULL; } // Delete cert context if needed if (certContext != NULL) { CertFreeCertificateContext(certContext); certContext = NULL; } } Exit: if (FAILED(hr)) { // Print out the error PrintError(hr, error); } if (channel != NULL) { // Close the channel WsCloseChannel(channel, NULL, error); } if (channel != NULL) { WsFreeChannel(channel); } if (requestMessage != NULL) { WsFreeMessage(requestMessage); } if (replyMessage != NULL) { WsFreeMessage(replyMessage); } if (listener != NULL) { // Close the listener if it was opened WsCloseListener(listener, NULL, error); } if (listener != NULL) { WsFreeListener(listener); } if (error != NULL) { WsFreeError(error); } if (heap != NULL) { WsFreeHeap(heap); } if (certSubjectName != NULL) { delete[] certSubjectName; } if (certContext != NULL) { CertFreeCertificateContext(certContext); } fflush(stdout); return SUCCEEDED(hr) ? 0 : -1; }