| 2115 | } |
| 2116 | |
| 2117 | static HRESULT verify_peer(pni_ssl_t *ssl, HCERTSTORE root_store, const char *server_name, bool tracing) |
| 2118 | { |
| 2119 | // Free/release the following before return: |
| 2120 | PCCERT_CONTEXT peer_cc = 0; |
| 2121 | PCCERT_CONTEXT trust_anchor = 0; |
| 2122 | PCCERT_CHAIN_CONTEXT chain_context = 0; |
| 2123 | wchar_t *nameUCS2 = 0; |
| 2124 | |
| 2125 | if (server_name && strlen(server_name) > 255) { |
| 2126 | ssl_log_error("invalid server name: %s", server_name); |
| 2127 | return WSAENAMETOOLONG; |
| 2128 | } |
| 2129 | |
| 2130 | // Get peer's certificate. |
| 2131 | SECURITY_STATUS status; |
| 2132 | status = QueryContextAttributes(&ssl->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &peer_cc); |
| 2133 | if (status != SEC_E_OK) { |
| 2134 | ssl_log_error_status(status, "can't obtain remote peer certificate information"); |
| 2135 | return status; |
| 2136 | } |
| 2137 | |
| 2138 | // Build the peer's certificate chain. Multiple chains may be built but we |
| 2139 | // care about rgpChain[0], which is the best. Custom root stores are not |
| 2140 | // allowed until W8/server 2012: see CERT_CHAIN_ENGINE_CONFIG. For now, we |
| 2141 | // manually override to taste. |
| 2142 | |
| 2143 | // Chain verification functions give false reports for CRL if the trust anchor |
| 2144 | // is not in the official root store. We ignore CRL completely if it doesn't |
| 2145 | // apply to any untrusted certs in the chain, and defer to SChannel's veto |
| 2146 | // otherwise. To rely on CRL, the CA must be in both the official system |
| 2147 | // trusted root store and the Proton cred->trust_store. To defeat CRL, the |
| 2148 | // most distal cert with CRL must be placed in the Proton cred->trust_store. |
| 2149 | // Similarly, certificate usage checking is overly strict at times. |
| 2150 | |
| 2151 | CERT_CHAIN_PARA desc; |
| 2152 | memset(&desc, 0, sizeof(desc)); |
| 2153 | desc.cbSize = sizeof(desc); |
| 2154 | |
| 2155 | LPSTR usages[] = { szOID_PKIX_KP_SERVER_AUTH }; |
| 2156 | DWORD n_usages = sizeof(usages) / sizeof(LPSTR); |
| 2157 | desc.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; |
| 2158 | desc.RequestedUsage.Usage.cUsageIdentifier = n_usages; |
| 2159 | desc.RequestedUsage.Usage.rgpszUsageIdentifier = usages; |
| 2160 | |
| 2161 | if(!CertGetCertificateChain(0, peer_cc, 0, peer_cc->hCertStore, &desc, |
| 2162 | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | |
| 2163 | CERT_CHAIN_CACHE_END_CERT, |
| 2164 | 0, &chain_context)){ |
| 2165 | HRESULT st = GetLastError(); |
| 2166 | ssl_log_error_status(st, "Basic certificate chain check failed"); |
| 2167 | CertFreeCertificateContext(peer_cc); |
| 2168 | return st; |
| 2169 | } |
| 2170 | if (chain_context->cChain < 1 || chain_context->rgpChain[0]->cElement < 1) { |
| 2171 | ssl_log_error("empty chain with status %x %x", chain_context->TrustStatus.dwErrorStatus, |
| 2172 | chain_context->TrustStatus.dwInfoStatus); |
| 2173 | return SEC_E_CERT_UNKNOWN; |
| 2174 | } |
no test coverage detected