2 * TLS support for CUPS on Windows using the Security Support Provider
5 * Copyright 2010-2018 by Apple Inc.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
10 /**** This file is included from tls.c ****/
13 * Include necessary headers...
16 #include "debug-private.h"
20 * Include necessary libraries...
23 #pragma comment(lib, "Crypt32.lib")
24 #pragma comment(lib, "Secur32.lib")
25 #pragma comment(lib, "Ws2_32.lib")
32 #ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA
33 # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
34 #endif /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */
36 #ifndef SECURITY_FLAG_IGNORE_CERT_CN_INVALID
37 # define SECURITY_FLAG_IGNORE_CERT_CN_INVALID 0x00001000 /* Common name does not match */
38 #endif /* !SECURITY_FLAG_IGNORE_CERT_CN_INVALID */
40 #ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
41 # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
42 #endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */
49 static int tls_options = -1,/* Options for TLS connections */
50 tls_min_version = _HTTP_TLS_1_0,
51 tls_max_version = _HTTP_TLS_MAX;
58 static _http_sspi_t *http_sspi_alloc(void);
59 static int http_sspi_client(http_t *http, const char *hostname);
60 static PCCERT_CONTEXT http_sspi_create_credential(http_credential_t *cred);
61 static BOOL http_sspi_find_credentials(http_t *http, const LPWSTR containerName, const char *common_name);
62 static void http_sspi_free(_http_sspi_t *sspi);
63 static BOOL http_sspi_make_credentials(_http_sspi_t *sspi, const LPWSTR containerName, const char *common_name, _http_mode_t mode, int years);
64 static int http_sspi_server(http_t *http, const char *hostname);
65 static void http_sspi_set_allows_any_root(_http_sspi_t *sspi, BOOL allow);
66 static void http_sspi_set_allows_expired_certs(_http_sspi_t *sspi, BOOL allow);
67 static const char *http_sspi_strerror(char *buffer, size_t bufsize, DWORD code);
68 static DWORD http_sspi_verify(PCCERT_CONTEXT cert, const char *common_name, DWORD dwCertFlags);
72 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
74 * @since CUPS 2.0/OS 10.10@
77 int /* O - 1 on success, 0 on failure */
78 cupsMakeServerCredentials(
79 const char *path, /* I - Keychain path or @code NULL@ for default */
80 const char *common_name, /* I - Common name */
81 int num_alt_names, /* I - Number of subject alternate names */
82 const char **alt_names, /* I - Subject Alternate Names */
83 time_t expiration_date) /* I - Expiration date */
85 _http_sspi_t *sspi; /* SSPI data */
86 int ret; /* Return value */
89 DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
95 sspi = http_sspi_alloc();
96 ret = http_sspi_make_credentials(sspi, L"ServerContainer", common_name, _HTTP_MODE_SERVER, (int)((expiration_date - time(NULL) + 86399) / 86400 / 365));
105 * 'cupsSetServerCredentials()' - Set the default server credentials.
107 * Note: The server credentials are used by all threads in the running process.
108 * This function is threadsafe.
110 * @since CUPS 2.0/OS 10.10@
113 int /* O - 1 on success, 0 on failure */
114 cupsSetServerCredentials(
115 const char *path, /* I - Keychain path or @code NULL@ for default */
116 const char *common_name, /* I - Default common name for server */
117 int auto_create) /* I - 1 = automatically create self-signed certificates */
119 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
130 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
131 * an encrypted connection.
133 * @since CUPS 1.5/macOS 10.7@
136 int /* O - Status of call (0 = success) */
138 http_t *http, /* I - Connection to server */
139 cups_array_t **credentials) /* O - Array of credentials */
141 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
143 if (!http || !http->tls || !http->tls->remoteCert || !credentials)
151 *credentials = cupsArrayNew(NULL, NULL);
152 httpAddCredential(*credentials, http->tls->remoteCert->pbCertEncoded, http->tls->remoteCert->cbCertEncoded);
159 * '_httpCreateCredentials()' - Create credentials in the internal format.
162 http_tls_credentials_t /* O - Internal credentials */
163 _httpCreateCredentials(
164 cups_array_t *credentials) /* I - Array of credentials */
166 return (http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)));
171 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
173 * @since CUPS 2.0/OS 10.10@
176 int /* O - 1 if valid, 0 otherwise */
177 httpCredentialsAreValidForName(
178 cups_array_t *credentials, /* I - Credentials */
179 const char *common_name) /* I - Name to check */
181 int valid = 1; /* Valid name? */
182 PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
184 char cert_name[1024]; /* Name from certificate */
189 if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
192 * Extract common name at end...
195 char *ptr = strrchr(cert_name, ',');
197 _cups_strcpy(cert_name, ptr + 2);
200 strlcpy(cert_name, "unknown", sizeof(cert_name));
202 CertFreeCertificateContext(cert);
205 strlcpy(cert_name, "unknown", sizeof(cert_name));
208 * Compare the common names...
211 if (_cups_strcasecmp(common_name, cert_name))
214 * Not an exact match for the common name, check for wildcard certs...
217 const char *domain = strchr(common_name, '.');
218 /* Domain in common name */
220 if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
223 * Not a wildcard match.
226 /* TODO: Check subject alternate names */
236 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
238 * @since CUPS 2.0/OS 10.10@
241 http_trust_t /* O - Level of trust */
242 httpCredentialsGetTrust(
243 cups_array_t *credentials, /* I - Credentials */
244 const char *common_name) /* I - Common name for trust lookup */
246 http_trust_t trust = HTTP_TRUST_OK; /* Level of trust */
247 PCCERT_CONTEXT cert = NULL; /* Certificate to validate */
248 DWORD certFlags = 0; /* Cert verification flags */
249 _cups_globals_t *cg = _cupsGlobals(); /* Per-thread global data */
253 return (HTTP_TRUST_UNKNOWN);
255 cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
257 return (HTTP_TRUST_UNKNOWN);
259 if (cg->any_root < 0)
263 certFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
265 if (cg->expired_certs)
266 certFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
268 if (!cg->validate_certs)
269 certFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
271 if (http_sspi_verify(cert, common_name, certFlags) != SEC_E_OK)
272 trust = HTTP_TRUST_INVALID;
274 CertFreeCertificateContext(cert);
281 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
283 * @since CUPS 2.0/OS 10.10@
286 time_t /* O - Expiration date of credentials */
287 httpCredentialsGetExpiration(
288 cups_array_t *credentials) /* I - Credentials */
290 time_t expiration_date = 0; /* Expiration data of credentials */
291 PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
296 SYSTEMTIME systime; /* System time */
297 struct tm tm; /* UNIX date/time */
299 FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
301 tm.tm_year = systime.wYear - 1900;
302 tm.tm_mon = systime.wMonth - 1;
303 tm.tm_mday = systime.wDay;
304 tm.tm_hour = systime.wHour;
305 tm.tm_min = systime.wMinute;
306 tm.tm_sec = systime.wSecond;
308 expiration_date = mktime(&tm);
310 CertFreeCertificateContext(cert);
313 return (expiration_date);
318 * 'httpCredentialsString()' - Return a string representing the credentials.
320 * @since CUPS 2.0/OS 10.10@
323 size_t /* O - Total size of credentials string */
324 httpCredentialsString(
325 cups_array_t *credentials, /* I - Credentials */
326 char *buffer, /* I - Buffer or @code NULL@ */
327 size_t bufsize) /* I - Size of buffer */
329 http_credential_t *first = (http_credential_t *)cupsArrayFirst(credentials);
330 /* First certificate */
331 PCCERT_CONTEXT cert; /* Certificate */
334 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
339 if (buffer && bufsize > 0)
342 cert = http_sspi_create_credential(first);
346 char cert_name[256]; /* Common name */
347 SYSTEMTIME systime; /* System time */
348 struct tm tm; /* UNIX date/time */
349 time_t expiration; /* Expiration date of cert */
350 unsigned char md5_digest[16]; /* MD5 result */
352 FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
354 tm.tm_year = systime.wYear - 1900;
355 tm.tm_mon = systime.wMonth - 1;
356 tm.tm_mday = systime.wDay;
357 tm.tm_hour = systime.wHour;
358 tm.tm_min = systime.wMinute;
359 tm.tm_sec = systime.wSecond;
361 expiration = mktime(&tm);
363 if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
366 * Extract common name at end...
369 char *ptr = strrchr(cert_name, ',');
371 _cups_strcpy(cert_name, ptr + 2);
374 strlcpy(cert_name, "unknown", sizeof(cert_name));
376 cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
378 snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", cert_name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
380 CertFreeCertificateContext(cert);
383 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
385 return (strlen(buffer));
390 * '_httpFreeCredentials()' - Free internal credentials.
394 _httpFreeCredentials(
395 http_tls_credentials_t credentials) /* I - Internal credentials */
400 CertFreeCertificateContext(credentials);
405 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
407 * @since CUPS 2.0/OS 10.10@
410 int /* O - 0 on success, -1 on error */
412 const char *path, /* I - Keychain path or @code NULL@ for default */
413 cups_array_t **credentials, /* IO - Credentials */
414 const char *common_name) /* I - Common name for credentials */
416 HCERTSTORE store = NULL; /* Certificate store */
417 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
418 DWORD dwSize = 0; /* 32 bit size */
419 PBYTE p = NULL; /* Temporary storage */
420 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
421 /* Handle to a CSP */
422 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
424 char error[1024]; /* Error message buffer */
428 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
438 DEBUG_puts("1httpLoadCredentials: NULL credentials pointer, returning -1.");
444 DEBUG_puts("1httpLoadCredentials: Bad common name, returning -1.");
448 if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
450 if (GetLastError() == NTE_EXISTS)
452 if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
454 DEBUG_printf(("1httpLoadCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
460 store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
464 DEBUG_printf(("1httpLoadCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
470 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
472 DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
476 p = (PBYTE)malloc(dwSize);
480 DEBUG_printf(("1httpLoadCredentials: malloc failed for %d bytes.", dwSize));
484 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
486 DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
493 storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
497 DEBUG_printf(("1httpLoadCredentials: Unable to find credentials for \"%s\".", common_name));
501 *credentials = cupsArrayNew(NULL, NULL);
502 httpAddCredential(*credentials, storedContext->pbCertEncoded, storedContext->cbCertEncoded);
511 CertFreeCertificateContext(storedContext);
517 CertCloseStore(store, 0);
520 CryptReleaseContext(hProv, 0);
522 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
524 return (*credentials ? 0 : -1);
529 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
531 * @since CUPS 2.0/OS 10.10@
534 int /* O - -1 on error, 0 on success */
536 const char *path, /* I - Keychain path or @code NULL@ for default */
537 cups_array_t *credentials, /* I - Credentials */
538 const char *common_name) /* I - Common name for credentials */
540 HCERTSTORE store = NULL; /* Certificate store */
541 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
542 PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
543 DWORD dwSize = 0; /* 32 bit size */
544 PBYTE p = NULL; /* Temporary storage */
545 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
546 /* Handle to a CSP */
547 CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
548 int ret = -1; /* Return value */
550 char error[1024]; /* Error message buffer */
554 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name));
560 DEBUG_puts("1httpSaveCredentials: Bad common name, returning -1.");
564 createdContext = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
567 DEBUG_puts("1httpSaveCredentials: Bad credentials, returning -1.");
571 if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
573 if (GetLastError() == NTE_EXISTS)
575 if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
577 DEBUG_printf(("1httpSaveCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
583 store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
587 DEBUG_printf(("1httpSaveCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
593 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
595 DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
599 p = (PBYTE)malloc(dwSize);
603 DEBUG_printf(("1httpSaveCredentials: malloc failed for %d bytes.", dwSize));
607 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
609 DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
614 * Add the created context to the named store, and associate it with the named
618 if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
620 DEBUG_printf(("1httpSaveCredentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
624 ZeroMemory(&ckp, sizeof(ckp));
625 ckp.pwszContainerName = L"RememberedContainer";
626 ckp.pwszProvName = MS_DEF_PROV_W;
627 ckp.dwProvType = PROV_RSA_FULL;
628 ckp.dwFlags = CRYPT_MACHINE_KEYSET;
629 ckp.dwKeySpec = AT_KEYEXCHANGE;
631 if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
633 DEBUG_printf(("1httpSaveCredentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError())));
646 CertFreeCertificateContext(createdContext);
649 CertFreeCertificateContext(storedContext);
655 CertCloseStore(store, 0);
658 CryptReleaseContext(hProv, 0);
660 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
666 * '_httpTLSInitialize()' - Initialize the TLS stack.
670 _httpTLSInitialize(void)
679 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
682 size_t /* O - Bytes available */
683 _httpTLSPending(http_t *http) /* I - HTTP connection */
686 return (http->tls->readBufferUsed);
693 * '_httpTLSRead()' - Read from a SSL/TLS connection.
696 int /* O - Bytes read */
697 _httpTLSRead(http_t *http, /* I - HTTP connection */
698 char *buf, /* I - Buffer to store data */
699 int len) /* I - Length of buffer */
701 int i; /* Looping var */
702 _http_sspi_t *sspi = http->tls; /* SSPI data */
703 SecBufferDesc message; /* Array of SecBuffer struct */
704 SecBuffer buffers[4] = { 0 }; /* Security package buffer */
705 int num = 0; /* Return value */
706 PSecBuffer pDataBuffer; /* Data buffer */
707 PSecBuffer pExtraBuffer; /* Excess data buffer */
708 SECURITY_STATUS scRet; /* SSPI status */
711 DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http, buf, len));
714 * If there are bytes that have already been decrypted and have not yet been
715 * read, return those...
718 if (sspi->readBufferUsed > 0)
720 int bytesToCopy = min(sspi->readBufferUsed, len);
721 /* Number of bytes to copy */
723 memcpy(buf, sspi->readBuffer, bytesToCopy);
724 sspi->readBufferUsed -= bytesToCopy;
726 if (sspi->readBufferUsed > 0)
727 memmove(sspi->readBuffer, sspi->readBuffer + bytesToCopy, sspi->readBufferUsed);
729 DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy));
731 return (bytesToCopy);
735 * Initialize security buffer structs
738 message.ulVersion = SECBUFFER_VERSION;
739 message.cBuffers = 4;
740 message.pBuffers = buffers;
745 * If there is not enough space in the buffer, then increase its size...
748 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
750 BYTE *temp; /* New buffer */
752 if (sspi->decryptBufferLength >= 262144)
754 WSASetLastError(E_OUTOFMEMORY);
755 DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)");
759 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
761 DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi->decryptBufferLength + 4096));
762 WSASetLastError(E_OUTOFMEMORY);
766 sspi->decryptBufferLength += 4096;
767 sspi->decryptBuffer = temp;
769 DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi->decryptBufferLength));
772 buffers[0].pvBuffer = sspi->decryptBuffer;
773 buffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
774 buffers[0].BufferType = SECBUFFER_DATA;
775 buffers[1].BufferType = SECBUFFER_EMPTY;
776 buffers[2].BufferType = SECBUFFER_EMPTY;
777 buffers[3].BufferType = SECBUFFER_EMPTY;
779 DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi->decryptBufferUsed));
781 scRet = DecryptMessage(&sspi->context, &message, 0, NULL);
783 if (scRet == SEC_E_INCOMPLETE_MESSAGE)
785 num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
788 DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError()));
793 DEBUG_puts("5_httpTLSRead: Server disconnected.");
797 DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num));
799 sspi->decryptBufferUsed += num;
802 while (scRet == SEC_E_INCOMPLETE_MESSAGE);
804 if (scRet == SEC_I_CONTEXT_EXPIRED)
806 DEBUG_puts("5_httpTLSRead: Context expired.");
807 WSASetLastError(WSAECONNRESET);
810 else if (scRet != SEC_E_OK)
812 DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
813 WSASetLastError(WSASYSCALLFAILURE);
818 * The decryption worked. Now, locate data buffer.
824 for (i = 1; i < 4; i++)
826 if (buffers[i].BufferType == SECBUFFER_DATA)
827 pDataBuffer = &buffers[i];
828 else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA))
829 pExtraBuffer = &buffers[i];
833 * If a data buffer is found, then copy the decrypted bytes to the passed-in
839 int bytesToCopy = min((int)pDataBuffer->cbBuffer, len);
840 /* Number of bytes to copy into buf */
841 int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
842 /* Number of bytes to save in our read buffer */
845 memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
848 * If there are more decrypted bytes than can be copied to the passed in
849 * buffer, then save them...
854 if ((sspi->readBufferLength - sspi->readBufferUsed) < bytesToSave)
856 BYTE *temp; /* New buffer pointer */
858 if ((temp = realloc(sspi->readBuffer, sspi->readBufferUsed + bytesToSave)) == NULL)
860 DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi->readBufferUsed + bytesToSave));
861 WSASetLastError(E_OUTOFMEMORY);
865 sspi->readBufferLength = sspi->readBufferUsed + bytesToSave;
866 sspi->readBuffer = temp;
869 memcpy(((BYTE *)sspi->readBuffer) + sspi->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave);
871 sspi->readBufferUsed += bytesToSave;
878 DEBUG_puts("_httpTLSRead: Unable to find data buffer.");
879 WSASetLastError(WSASYSCALLFAILURE);
884 * If the decryption process left extra bytes, then save those back in
885 * decryptBuffer. They will be processed the next time through the loop.
890 memmove(sspi->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
891 sspi->decryptBufferUsed = pExtraBuffer->cbBuffer;
895 sspi->decryptBufferUsed = 0;
903 * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
907 _httpTLSSetOptions(int options, /* I - Options */
908 int min_version, /* I - Minimum TLS version */
909 int max_version) /* I - Maximum TLS version */
911 if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
913 tls_options = options;
914 tls_min_version = min_version;
915 tls_max_version = max_version;
921 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
924 int /* O - 0 on success, -1 on failure */
925 _httpTLSStart(http_t *http) /* I - HTTP connection */
927 char hostname[256], /* Hostname */
928 *hostptr; /* Pointer into hostname */
931 DEBUG_printf(("3_httpTLSStart(http=%p)", http));
935 DEBUG_puts("4_httpTLSStart: Setting defaults.");
937 DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
940 if ((http->tls = http_sspi_alloc()) == NULL)
943 if (http->mode == _HTTP_MODE_CLIENT)
946 * Client: determine hostname...
949 if (httpAddrLocalhost(http->hostaddr))
951 strlcpy(hostname, "localhost", sizeof(hostname));
956 * Otherwise make sure the hostname we have does not end in a trailing dot.
959 strlcpy(hostname, http->hostname, sizeof(hostname));
960 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
965 return (http_sspi_client(http, hostname));
970 * Server: determine hostname to use...
973 if (http->fields[HTTP_FIELD_HOST])
976 * Use hostname for TLS upgrade...
979 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
984 * Resolve hostname from connection address...
987 http_addr_t addr; /* Connection address */
988 socklen_t addrlen; /* Length of address */
990 addrlen = sizeof(addr);
991 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
993 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
996 else if (httpAddrLocalhost(&addr))
1000 httpAddrLookup(&addr, hostname, sizeof(hostname));
1001 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1005 return (http_sspi_server(http, hostname));
1011 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
1015 _httpTLSStop(http_t *http) /* I - HTTP connection */
1017 _http_sspi_t *sspi = http->tls; /* SSPI data */
1020 if (sspi->contextInitialized && http->fd >= 0)
1022 SecBufferDesc message; /* Array of SecBuffer struct */
1023 SecBuffer buffers[1] = { 0 };
1024 /* Security package buffer */
1025 DWORD dwType; /* Type */
1026 DWORD status; /* Status */
1029 * Notify schannel that we are about to close the connection.
1032 dwType = SCHANNEL_SHUTDOWN;
1034 buffers[0].pvBuffer = &dwType;
1035 buffers[0].BufferType = SECBUFFER_TOKEN;
1036 buffers[0].cbBuffer = sizeof(dwType);
1038 message.cBuffers = 1;
1039 message.pBuffers = buffers;
1040 message.ulVersion = SECBUFFER_VERSION;
1042 status = ApplyControlToken(&sspi->context, &message);
1044 if (SUCCEEDED(status))
1046 PBYTE pbMessage; /* Message buffer */
1047 DWORD cbMessage; /* Message buffer count */
1048 DWORD cbData; /* Data count */
1049 DWORD dwSSPIFlags; /* SSL attributes we requested */
1050 DWORD dwSSPIOutFlags; /* SSL attributes we received */
1051 TimeStamp tsExpiry; /* Time stamp */
1053 dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
1054 ASC_REQ_REPLAY_DETECT |
1055 ASC_REQ_CONFIDENTIALITY |
1056 ASC_REQ_EXTENDED_ERROR |
1057 ASC_REQ_ALLOCATE_MEMORY |
1060 buffers[0].pvBuffer = NULL;
1061 buffers[0].BufferType = SECBUFFER_TOKEN;
1062 buffers[0].cbBuffer = 0;
1064 message.cBuffers = 1;
1065 message.pBuffers = buffers;
1066 message.ulVersion = SECBUFFER_VERSION;
1068 status = AcceptSecurityContext(&sspi->creds, &sspi->context, NULL,
1069 dwSSPIFlags, SECURITY_NATIVE_DREP, NULL,
1070 &message, &dwSSPIOutFlags, &tsExpiry);
1072 if (SUCCEEDED(status))
1074 pbMessage = buffers[0].pvBuffer;
1075 cbMessage = buffers[0].cbBuffer;
1078 * Send the close notify message to the client.
1081 if (pbMessage && cbMessage)
1083 cbData = send(http->fd, pbMessage, cbMessage, 0);
1084 if ((cbData == SOCKET_ERROR) || (cbData == 0))
1086 status = WSAGetLastError();
1087 DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status));
1091 FreeContextBuffer(pbMessage);
1097 DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
1102 DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status)));
1106 http_sspi_free(sspi);
1113 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1116 int /* O - Bytes written */
1117 _httpTLSWrite(http_t *http, /* I - HTTP connection */
1118 const char *buf, /* I - Buffer holding data */
1119 int len) /* I - Length of buffer */
1121 _http_sspi_t *sspi = http->tls; /* SSPI data */
1122 SecBufferDesc message; /* Array of SecBuffer struct */
1123 SecBuffer buffers[4] = { 0 }; /* Security package buffer */
1124 int bufferLen; /* Buffer length */
1125 int bytesLeft; /* Bytes left to write */
1126 const char *bufptr; /* Pointer into buffer */
1127 int num = 0; /* Return value */
1130 bufferLen = sspi->streamSizes.cbMaximumMessage + sspi->streamSizes.cbHeader + sspi->streamSizes.cbTrailer;
1132 if (bufferLen > sspi->writeBufferLength)
1134 BYTE *temp; /* New buffer pointer */
1136 if ((temp = (BYTE *)realloc(sspi->writeBuffer, bufferLen)) == NULL)
1138 DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen));
1139 WSASetLastError(E_OUTOFMEMORY);
1143 sspi->writeBuffer = temp;
1144 sspi->writeBufferLength = bufferLen;
1152 int chunk = min((int)sspi->streamSizes.cbMaximumMessage, bytesLeft);
1153 /* Size of data to write */
1154 SECURITY_STATUS scRet; /* SSPI status */
1157 * Copy user data into the buffer, starting just past the header...
1160 memcpy(sspi->writeBuffer + sspi->streamSizes.cbHeader, bufptr, chunk);
1163 * Setup the SSPI buffers
1166 message.ulVersion = SECBUFFER_VERSION;
1167 message.cBuffers = 4;
1168 message.pBuffers = buffers;
1170 buffers[0].pvBuffer = sspi->writeBuffer;
1171 buffers[0].cbBuffer = sspi->streamSizes.cbHeader;
1172 buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
1173 buffers[1].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader;
1174 buffers[1].cbBuffer = (unsigned long) chunk;
1175 buffers[1].BufferType = SECBUFFER_DATA;
1176 buffers[2].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader + chunk;
1177 buffers[2].cbBuffer = sspi->streamSizes.cbTrailer;
1178 buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
1179 buffers[3].BufferType = SECBUFFER_EMPTY;
1185 scRet = EncryptMessage(&sspi->context, 0, &message, 0);
1189 DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1190 WSASetLastError(WSASYSCALLFAILURE);
1195 * Send the data. Remember the size of the total data to send is the size
1196 * of the header, the size of the data the caller passed in and the size
1200 num = send(http->fd, sspi->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0);
1204 DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError()));
1218 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
1221 static int /* O - 0 on success, -1 on failure */
1222 http_setup_ssl(http_t *http) /* I - Connection to server */
1224 char hostname[256], /* Hostname */
1225 *hostptr; /* Pointer into hostname */
1227 TCHAR username[256]; /* Username returned from GetUserName() */
1228 TCHAR commonName[256];/* Common name for certificate */
1229 DWORD dwSize; /* 32 bit size */
1232 DEBUG_printf(("7http_setup_ssl(http=%p)", http));
1235 * Get the hostname to use for SSL...
1238 if (httpAddrLocalhost(http->hostaddr))
1240 strlcpy(hostname, "localhost", sizeof(hostname));
1245 * Otherwise make sure the hostname we have does not end in a trailing dot.
1248 strlcpy(hostname, http->hostname, sizeof(hostname));
1249 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1254 http->tls = http_sspi_alloc();
1258 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1262 dwSize = sizeof(username) / sizeof(TCHAR);
1263 GetUserName(username, &dwSize);
1264 _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR),
1265 sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username);
1267 if (!_sspiGetCredentials(http->tls, L"ClientContainer",
1270 _sspiFree(http->tls);
1274 http->status = HTTP_STATUS_ERROR;
1276 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
1277 _("Unable to establish a secure connection to host."), 1);
1282 _sspiSetAllowsAnyRoot(http->tls, TRUE);
1283 _sspiSetAllowsExpiredCerts(http->tls, TRUE);
1285 if (!_sspiConnect(http->tls, hostname))
1287 _sspiFree(http->tls);
1291 http->status = HTTP_STATUS_ERROR;
1293 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI,
1294 _("Unable to establish a secure connection to host."), 1);
1305 * 'http_sspi_alloc()' - Allocate SSPI object.
1308 static _http_sspi_t * /* O - New SSPI/SSL object */
1309 http_sspi_alloc(void)
1311 return ((_http_sspi_t *)calloc(sizeof(_http_sspi_t), 1));
1316 * 'http_sspi_client()' - Negotiate a TLS connection as a client.
1319 static int /* O - 0 on success, -1 on failure */
1320 http_sspi_client(http_t *http, /* I - Client connection */
1321 const char *hostname) /* I - Server hostname */
1323 _http_sspi_t *sspi = http->tls; /* SSPI data */
1324 DWORD dwSize; /* Size for buffer */
1325 DWORD dwSSPIFlags; /* SSL connection attributes we want */
1326 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
1327 TimeStamp tsExpiry; /* Time stamp */
1328 SECURITY_STATUS scRet; /* Status */
1329 int cbData; /* Data count */
1330 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
1331 SecBuffer inBuffers[2]; /* Security package buffer */
1332 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
1333 SecBuffer outBuffers[1]; /* Security package buffer */
1334 int ret = 0; /* Return value */
1335 char username[1024], /* Current username */
1336 common_name[1024]; /* CN=username */
1339 DEBUG_printf(("4http_sspi_client(http=%p, hostname=\"%s\")", http, hostname));
1341 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
1342 ISC_REQ_REPLAY_DETECT |
1343 ISC_REQ_CONFIDENTIALITY |
1344 ISC_RET_EXTENDED_ERROR |
1345 ISC_REQ_ALLOCATE_MEMORY |
1349 * Lookup the client certificate...
1352 dwSize = sizeof(username);
1353 GetUserNameA(username, &dwSize);
1354 snprintf(common_name, sizeof(common_name), "CN=%s", username);
1356 if (!http_sspi_find_credentials(http, L"ClientContainer", common_name))
1357 if (!http_sspi_make_credentials(http->tls, L"ClientContainer", common_name, _HTTP_MODE_CLIENT, 10))
1359 DEBUG_puts("5http_sspi_client: Unable to get client credentials.");
1364 * Initiate a ClientHello message and generate a token.
1367 outBuffers[0].pvBuffer = NULL;
1368 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1369 outBuffers[0].cbBuffer = 0;
1371 outBuffer.cBuffers = 1;
1372 outBuffer.pBuffers = outBuffers;
1373 outBuffer.ulVersion = SECBUFFER_VERSION;
1375 scRet = InitializeSecurityContext(&sspi->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &sspi->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1377 if (scRet != SEC_I_CONTINUE_NEEDED)
1379 DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1384 * Send response to server if there is one.
1387 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1389 if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0)
1391 DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
1392 FreeContextBuffer(outBuffers[0].pvBuffer);
1393 DeleteSecurityContext(&sspi->context);
1397 DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
1399 FreeContextBuffer(outBuffers[0].pvBuffer);
1400 outBuffers[0].pvBuffer = NULL;
1403 dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION |
1404 ISC_REQ_SEQUENCE_DETECT |
1405 ISC_REQ_REPLAY_DETECT |
1406 ISC_REQ_CONFIDENTIALITY |
1407 ISC_RET_EXTENDED_ERROR |
1408 ISC_REQ_ALLOCATE_MEMORY |
1411 sspi->decryptBufferUsed = 0;
1414 * Loop until the handshake is finished or an error occurs.
1417 scRet = SEC_I_CONTINUE_NEEDED;
1419 while(scRet == SEC_I_CONTINUE_NEEDED ||
1420 scRet == SEC_E_INCOMPLETE_MESSAGE ||
1421 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1423 if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
1425 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
1427 BYTE *temp; /* New buffer */
1429 if (sspi->decryptBufferLength >= 262144)
1431 WSASetLastError(E_OUTOFMEMORY);
1432 DEBUG_puts("5http_sspi_client: Decryption buffer too large (>256k)");
1436 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
1438 DEBUG_printf(("5http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
1439 WSASetLastError(E_OUTOFMEMORY);
1443 sspi->decryptBufferLength += 4096;
1444 sspi->decryptBuffer = temp;
1447 cbData = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
1451 DEBUG_printf(("5http_sspi_client: recv failed: %d", WSAGetLastError()));
1454 else if (cbData == 0)
1456 DEBUG_printf(("5http_sspi_client: Server unexpectedly disconnected."));
1460 DEBUG_printf(("5http_sspi_client: %d bytes of handshake data received", cbData));
1462 sspi->decryptBufferUsed += cbData;
1466 * Set up the input buffers. Buffer 0 is used to pass in data received from
1467 * the server. Schannel will consume some or all of this. Leftover data
1468 * (if any) will be placed in buffer 1 and given a buffer type of
1472 inBuffers[0].pvBuffer = sspi->decryptBuffer;
1473 inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
1474 inBuffers[0].BufferType = SECBUFFER_TOKEN;
1476 inBuffers[1].pvBuffer = NULL;
1477 inBuffers[1].cbBuffer = 0;
1478 inBuffers[1].BufferType = SECBUFFER_EMPTY;
1480 inBuffer.cBuffers = 2;
1481 inBuffer.pBuffers = inBuffers;
1482 inBuffer.ulVersion = SECBUFFER_VERSION;
1485 * Set up the output buffers. These are initialized to NULL so as to make it
1486 * less likely we'll attempt to free random garbage later.
1489 outBuffers[0].pvBuffer = NULL;
1490 outBuffers[0].BufferType = SECBUFFER_TOKEN;
1491 outBuffers[0].cbBuffer = 0;
1493 outBuffer.cBuffers = 1;
1494 outBuffer.pBuffers = outBuffers;
1495 outBuffer.ulVersion = SECBUFFER_VERSION;
1498 * Call InitializeSecurityContext.
1501 scRet = InitializeSecurityContext(&sspi->creds, &sspi->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
1504 * If InitializeSecurityContext was successful (or if the error was one of
1505 * the special extended ones), send the contents of the output buffer to the
1509 if (scRet == SEC_E_OK ||
1510 scRet == SEC_I_CONTINUE_NEEDED ||
1511 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
1513 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
1515 cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
1519 DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError()));
1520 FreeContextBuffer(outBuffers[0].pvBuffer);
1521 DeleteSecurityContext(&sspi->context);
1525 DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData));
1528 * Free output buffer.
1531 FreeContextBuffer(outBuffers[0].pvBuffer);
1532 outBuffers[0].pvBuffer = NULL;
1537 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we
1538 * need to read more data from the server and try again.
1541 if (scRet == SEC_E_INCOMPLETE_MESSAGE)
1545 * If InitializeSecurityContext returned SEC_E_OK, then the handshake
1546 * completed successfully.
1549 if (scRet == SEC_E_OK)
1552 * If the "extra" buffer contains data, this is encrypted application
1553 * protocol layer stuff. It needs to be saved. The application layer will
1554 * later decrypt it with DecryptMessage.
1557 DEBUG_puts("5http_sspi_client: Handshake was successful.");
1559 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1561 memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1563 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1565 DEBUG_printf(("5http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed));
1568 sspi->decryptBufferUsed = 0;
1578 * Check for fatal error.
1583 DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1589 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1590 * then the server just requested client authentication.
1593 if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1599 DEBUG_printf(("5http_sspi_client: server requested client credentials."));
1605 * Copy any leftover data from the "extra" buffer, and go around again.
1608 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
1610 memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer);
1612 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
1616 sspi->decryptBufferUsed = 0;
1623 * Success! Get the server cert
1626 sspi->contextInitialized = TRUE;
1628 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(sspi->remoteCert));
1630 if (scRet != SEC_E_OK)
1632 DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1637 * Find out how big the header/trailer will be:
1640 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
1642 if (scRet != SEC_E_OK)
1644 DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
1654 * 'http_sspi_create_credential()' - Create an SSPI certificate context.
1657 static PCCERT_CONTEXT /* O - Certificate context */
1658 http_sspi_create_credential(
1659 http_credential_t *cred) /* I - Credential */
1662 return (CertCreateCertificateContext(X509_ASN_ENCODING, cred->data, cred->datalen));
1669 * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store.
1672 static BOOL /* O - 1 on success, 0 on failure */
1673 http_sspi_find_credentials(
1674 http_t *http, /* I - HTTP connection */
1675 const LPWSTR container, /* I - Cert container name */
1676 const char *common_name) /* I - Common name of certificate */
1678 _http_sspi_t *sspi = http->tls; /* SSPI data */
1679 HCERTSTORE store = NULL; /* Certificate store */
1680 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
1681 DWORD dwSize = 0; /* 32 bit size */
1682 PBYTE p = NULL; /* Temporary storage */
1683 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
1684 /* Handle to a CSP */
1685 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
1686 SCHANNEL_CRED SchannelCred; /* Schannel credential data */
1687 TimeStamp tsExpiry; /* Time stamp */
1688 SECURITY_STATUS Status; /* Status */
1689 BOOL ok = TRUE; /* Return value */
1692 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
1694 if (GetLastError() == NTE_EXISTS)
1696 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
1698 DEBUG_printf(("5http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1705 store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
1709 DEBUG_printf(("5http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1716 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
1718 DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1723 p = (PBYTE)malloc(dwSize);
1727 DEBUG_printf(("5http_sspi_find_credentials: malloc failed for %d bytes.", dwSize));
1732 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
1734 DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1739 sib.cbData = dwSize;
1742 storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL);
1746 DEBUG_printf(("5http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name));
1751 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
1753 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
1754 SchannelCred.cCreds = 1;
1755 SchannelCred.paCred = &storedContext;
1758 * Set supported protocols (can also be overriden in the registry...)
1761 #ifdef SP_PROT_TLS1_2_SERVER
1762 if (http->mode == _HTTP_MODE_SERVER)
1764 if (tls_min_version > _HTTP_TLS_1_1)
1765 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER;
1766 else if (tls_min_version > _HTTP_TLS_1_0)
1767 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER;
1768 else if (tls_min_version == _HTTP_TLS_SSL3)
1769 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER | SP_PROT_SSL3_SERVER;
1771 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER;
1775 if (tls_min_version > _HTTP_TLS_1_1)
1776 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
1777 else if (tls_min_version > _HTTP_TLS_1_0)
1778 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT;
1779 else if (tls_min_version == _HTTP_TLS_SSL3)
1780 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_SSL3_CLIENT;
1782 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT;
1786 if (http->mode == _HTTP_MODE_SERVER)
1788 if (tls_min_version == _HTTP_TLS_SSL3)
1789 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER;
1791 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER;
1795 if (tls_min_version == _HTTP_TLS_SSL3)
1796 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT;
1798 SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
1800 #endif /* SP_PROT_TLS1_2_SERVER */
1802 /* TODO: Support _HTTP_TLS_ALLOW_RC4, _HTTP_TLS_ALLOW_DH, and _HTTP_TLS_DENY_CBC options; right now we'll rely on Windows registry to enable/disable RC4/DH/CBC... */
1805 * Create an SSPI credential.
1808 Status = AcquireCredentialsHandle(NULL, UNISP_NAME, http->mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
1809 if (Status != SEC_E_OK)
1811 DEBUG_printf(("5http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
1823 CertFreeCertificateContext(storedContext);
1829 CertCloseStore(store, 0);
1832 CryptReleaseContext(hProv, 0);
1839 * 'http_sspi_free()' - Close a connection and free resources.
1843 http_sspi_free(_http_sspi_t *sspi) /* I - SSPI data */
1848 if (sspi->contextInitialized)
1849 DeleteSecurityContext(&sspi->context);
1851 if (sspi->decryptBuffer)
1852 free(sspi->decryptBuffer);
1854 if (sspi->readBuffer)
1855 free(sspi->readBuffer);
1857 if (sspi->writeBuffer)
1858 free(sspi->writeBuffer);
1860 if (sspi->localCert)
1861 CertFreeCertificateContext(sspi->localCert);
1863 if (sspi->remoteCert)
1864 CertFreeCertificateContext(sspi->remoteCert);
1871 * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store.
1874 static BOOL /* O - 1 on success, 0 on failure */
1875 http_sspi_make_credentials(
1876 _http_sspi_t *sspi, /* I - SSPI data */
1877 const LPWSTR container, /* I - Cert container name */
1878 const char *common_name, /* I - Common name of certificate */
1879 _http_mode_t mode, /* I - Client or server? */
1880 int years) /* I - Years until expiration */
1882 HCERTSTORE store = NULL; /* Certificate store */
1883 PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
1884 PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
1885 DWORD dwSize = 0; /* 32 bit size */
1886 PBYTE p = NULL; /* Temporary storage */
1887 HCRYPTPROV hProv = (HCRYPTPROV)NULL;
1888 /* Handle to a CSP */
1889 CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
1890 SCHANNEL_CRED SchannelCred; /* Schannel credential data */
1891 TimeStamp tsExpiry; /* Time stamp */
1892 SECURITY_STATUS Status; /* Status */
1893 HCRYPTKEY hKey = (HCRYPTKEY)NULL; /* Handle to crypto key */
1894 CRYPT_KEY_PROV_INFO kpi; /* Key container info */
1895 SYSTEMTIME et; /* System time */
1896 CERT_EXTENSIONS exts; /* Array of cert extensions */
1897 CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
1898 BOOL ok = TRUE; /* Return value */
1901 DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi, container, common_name, mode, years));
1903 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
1905 if (GetLastError() == NTE_EXISTS)
1907 if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
1909 DEBUG_printf(("5http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1916 store = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"MY");
1920 DEBUG_printf(("5http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1927 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
1929 DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1934 p = (PBYTE)malloc(dwSize);
1938 DEBUG_printf(("5http_sspi_make_credentials: malloc failed for %d bytes", dwSize));
1943 if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
1945 DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1951 * Create a private key and self-signed certificate...
1954 if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey))
1956 DEBUG_printf(("5http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1961 ZeroMemory(&kpi, sizeof(kpi));
1962 kpi.pwszContainerName = (LPWSTR)container;
1963 kpi.pwszProvName = MS_DEF_PROV_W;
1964 kpi.dwProvType = PROV_RSA_FULL;
1965 kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
1966 kpi.dwKeySpec = AT_KEYEXCHANGE;
1970 if (et.wMonth == 2 && et.wDay == 29)
1971 et.wDay = 28; /* Avoid Feb 29th due to leap years */
1973 ZeroMemory(&exts, sizeof(exts));
1975 createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, &et, &exts);
1977 if (!createdContext)
1979 DEBUG_printf(("5http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1985 * Add the created context to the named store, and associate it with the named
1989 if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext))
1991 DEBUG_printf(("5http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
1996 ZeroMemory(&ckp, sizeof(ckp));
1997 ckp.pwszContainerName = (LPWSTR) container;
1998 ckp.pwszProvName = MS_DEF_PROV_W;
1999 ckp.dwProvType = PROV_RSA_FULL;
2000 ckp.dwFlags = CRYPT_MACHINE_KEYSET;
2001 ckp.dwKeySpec = AT_KEYEXCHANGE;
2003 if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp))
2005 DEBUG_printf(("5http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError())));
2011 * Get a handle to use the certificate...
2014 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
2016 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
2017 SchannelCred.cCreds = 1;
2018 SchannelCred.paCred = &storedContext;
2021 * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
2024 if (mode == _HTTP_MODE_SERVER)
2025 SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
2028 * Create an SSPI credential.
2031 Status = AcquireCredentialsHandle(NULL, UNISP_NAME, mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry);
2032 if (Status != SEC_E_OK)
2034 DEBUG_printf(("5http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status)));
2046 CryptDestroyKey(hKey);
2049 CertFreeCertificateContext(createdContext);
2052 CertFreeCertificateContext(storedContext);
2058 CertCloseStore(store, 0);
2061 CryptReleaseContext(hProv, 0);
2068 * 'http_sspi_server()' - Negotiate a TLS connection as a server.
2071 static int /* O - 0 on success, -1 on failure */
2072 http_sspi_server(http_t *http, /* I - HTTP connection */
2073 const char *hostname) /* I - Hostname of server */
2075 _http_sspi_t *sspi = http->tls; /* I - SSPI data */
2076 char common_name[512]; /* Common name for cert */
2077 DWORD dwSSPIFlags; /* SSL connection attributes we want */
2078 DWORD dwSSPIOutFlags; /* SSL connection attributes we got */
2079 TimeStamp tsExpiry; /* Time stamp */
2080 SECURITY_STATUS scRet; /* SSPI Status */
2081 SecBufferDesc inBuffer; /* Array of SecBuffer structs */
2082 SecBuffer inBuffers[2]; /* Security package buffer */
2083 SecBufferDesc outBuffer; /* Array of SecBuffer structs */
2084 SecBuffer outBuffers[1]; /* Security package buffer */
2085 int num = 0; /* 32 bit status value */
2086 BOOL fInitContext = TRUE; /* Has the context been init'd? */
2087 int ret = 0; /* Return value */
2090 DEBUG_printf(("4http_sspi_server(http=%p, hostname=\"%s\")", http, hostname));
2092 dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT |
2093 ASC_REQ_REPLAY_DETECT |
2094 ASC_REQ_CONFIDENTIALITY |
2095 ASC_REQ_EXTENDED_ERROR |
2096 ASC_REQ_ALLOCATE_MEMORY |
2099 sspi->decryptBufferUsed = 0;
2102 * Lookup the server certificate...
2105 snprintf(common_name, sizeof(common_name), "CN=%s", hostname);
2107 if (!http_sspi_find_credentials(http, L"ServerContainer", common_name))
2108 if (!http_sspi_make_credentials(http->tls, L"ServerContainer", common_name, _HTTP_MODE_SERVER, 10))
2110 DEBUG_puts("5http_sspi_server: Unable to get server credentials.");
2115 * Set OutBuffer for AcceptSecurityContext call
2118 outBuffer.cBuffers = 1;
2119 outBuffer.pBuffers = outBuffers;
2120 outBuffer.ulVersion = SECBUFFER_VERSION;
2122 scRet = SEC_I_CONTINUE_NEEDED;
2124 while (scRet == SEC_I_CONTINUE_NEEDED ||
2125 scRet == SEC_E_INCOMPLETE_MESSAGE ||
2126 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
2128 if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE)
2130 if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
2132 BYTE *temp; /* New buffer */
2134 if (sspi->decryptBufferLength >= 262144)
2136 WSASetLastError(E_OUTOFMEMORY);
2137 DEBUG_puts("5http_sspi_server: Decryption buffer too large (>256k)");
2141 if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL)
2143 DEBUG_printf(("5http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096));
2144 WSASetLastError(E_OUTOFMEMORY);
2148 sspi->decryptBufferLength += 4096;
2149 sspi->decryptBuffer = temp;
2154 num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0);
2156 if (num == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
2164 DEBUG_printf(("5http_sspi_server: recv failed: %d", WSAGetLastError()));
2169 DEBUG_puts("5http_sspi_server: client disconnected");
2173 DEBUG_printf(("5http_sspi_server: received %d (handshake) bytes from client.", num));
2174 sspi->decryptBufferUsed += num;
2178 * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process
2179 * on this run around the loop.
2182 inBuffers[0].pvBuffer = sspi->decryptBuffer;
2183 inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed;
2184 inBuffers[0].BufferType = SECBUFFER_TOKEN;
2186 inBuffers[1].pvBuffer = NULL;
2187 inBuffers[1].cbBuffer = 0;
2188 inBuffers[1].BufferType = SECBUFFER_EMPTY;
2190 inBuffer.cBuffers = 2;
2191 inBuffer.pBuffers = inBuffers;
2192 inBuffer.ulVersion = SECBUFFER_VERSION;
2195 * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to
2196 * free random garbage at the quit.
2199 outBuffers[0].pvBuffer = NULL;
2200 outBuffers[0].BufferType = SECBUFFER_TOKEN;
2201 outBuffers[0].cbBuffer = 0;
2203 scRet = AcceptSecurityContext(&sspi->creds, (fInitContext?NULL:&sspi->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&sspi->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry);
2205 fInitContext = FALSE;
2207 if (scRet == SEC_E_OK ||
2208 scRet == SEC_I_CONTINUE_NEEDED ||
2209 (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0)))
2211 if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer)
2214 * Send response to server if there is one.
2217 num = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
2221 DEBUG_printf(("5http_sspi_server: handshake send failed: %d", WSAGetLastError()));
2225 DEBUG_printf(("5http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer));
2227 FreeContextBuffer(outBuffers[0].pvBuffer);
2228 outBuffers[0].pvBuffer = NULL;
2232 if (scRet == SEC_E_OK)
2235 * If there's extra data then save it for next time we go to decrypt.
2238 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
2240 memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
2241 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
2245 sspi->decryptBufferUsed = 0;
2249 else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE)
2251 DEBUG_printf(("5http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
2256 if (scRet != SEC_E_INCOMPLETE_MESSAGE &&
2257 scRet != SEC_I_INCOMPLETE_CREDENTIALS)
2259 if (inBuffers[1].BufferType == SECBUFFER_EXTRA)
2261 memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer);
2262 sspi->decryptBufferUsed = inBuffers[1].cbBuffer;
2266 sspi->decryptBufferUsed = 0;
2273 sspi->contextInitialized = TRUE;
2276 * Find out how big the header will be:
2279 scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes);
2281 if (scRet != SEC_E_OK)
2283 DEBUG_printf(("5http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet)));
2293 * 'http_sspi_strerror()' - Return a string for the specified error code.
2296 static const char * /* O - String for error */
2297 http_sspi_strerror(char *buffer, /* I - Error message buffer */
2298 size_t bufsize, /* I - Size of buffer */
2299 DWORD code) /* I - Error code */
2301 if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
2304 * Strip trailing CR + LF...
2307 char *ptr; /* Pointer into error message */
2309 for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr --)
2310 if (*ptr == '\n' || *ptr == '\r')
2316 snprintf(buffer, bufsize, "Unknown error %x", code);
2323 * 'http_sspi_verify()' - Verify a certificate.
2326 static DWORD /* O - Error code (0 == No error) */
2328 PCCERT_CONTEXT cert, /* I - Server certificate */
2329 const char *common_name, /* I - Common name */
2330 DWORD dwCertFlags) /* I - Verification flags */
2332 HTTPSPolicyCallbackData httpsPolicy; /* HTTPS Policy Struct */
2333 CERT_CHAIN_POLICY_PARA policyPara; /* Cert chain policy parameters */
2334 CERT_CHAIN_POLICY_STATUS policyStatus;/* Cert chain policy status */
2335 CERT_CHAIN_PARA chainPara; /* Used for searching and matching criteria */
2336 PCCERT_CHAIN_CONTEXT chainContext = NULL;
2337 /* Certificate chain */
2338 PWSTR commonNameUnicode = NULL;
2339 /* Unicode common name */
2340 LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
2341 szOID_SERVER_GATED_CRYPTO,
2342 szOID_SGC_NETSCAPE };
2343 /* How are we using this certificate? */
2344 DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
2345 /* Number of ites in rgszUsages */
2346 DWORD count; /* 32 bit count variable */
2347 DWORD status; /* Return value */
2349 char error[1024]; /* Error message string */
2354 return (SEC_E_WRONG_PRINCIPAL);
2357 * Convert common name to Unicode.
2360 if (!common_name || !*common_name)
2361 return (SEC_E_WRONG_PRINCIPAL);
2363 count = MultiByteToWideChar(CP_ACP, 0, common_name, -1, NULL, 0);
2364 commonNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR));
2365 if (!commonNameUnicode)
2366 return (SEC_E_INSUFFICIENT_MEMORY);
2368 if (!MultiByteToWideChar(CP_ACP, 0, common_name, -1, commonNameUnicode, count))
2370 LocalFree(commonNameUnicode);
2371 return (SEC_E_WRONG_PRINCIPAL);
2375 * Build certificate chain.
2378 ZeroMemory(&chainPara, sizeof(chainPara));
2380 chainPara.cbSize = sizeof(chainPara);
2381 chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
2382 chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
2383 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
2385 if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext))
2387 status = GetLastError();
2389 DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(error, sizeof(error), status)));
2391 LocalFree(commonNameUnicode);
2396 * Validate certificate chain.
2399 ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData));
2400 httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData);
2401 httpsPolicy.dwAuthType = AUTHTYPE_SERVER;
2402 httpsPolicy.fdwChecks = dwCertFlags;
2403 httpsPolicy.pwszServerName = commonNameUnicode;
2405 memset(&policyPara, 0, sizeof(policyPara));
2406 policyPara.cbSize = sizeof(policyPara);
2407 policyPara.pvExtraPolicyPara = &httpsPolicy;
2409 memset(&policyStatus, 0, sizeof(policyStatus));
2410 policyStatus.cbSize = sizeof(policyStatus);
2412 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus))
2414 status = GetLastError();
2416 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %s", http_sspi_strerror(error, sizeof(error), status)));
2418 else if (policyStatus.dwError)
2419 status = policyStatus.dwError;
2424 CertFreeCertificateChain(chainContext);
2426 if (commonNameUnicode)
2427 LocalFree(commonNameUnicode);