/* * TLS support for CUPS on Windows using the Security Support Provider * Interface (SSPI). * * Copyright 2010-2018 by Apple Inc. * * Licensed under Apache License v2.0. See the file "LICENSE" for more information. */ /**** This file is included from tls.c ****/ /* * Include necessary headers... */ #include "debug-private.h" /* * Include necessary libraries... */ #pragma comment(lib, "Crypt32.lib") #pragma comment(lib, "Secur32.lib") #pragma comment(lib, "Ws2_32.lib") /* * Constants... */ #ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */ #endif /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */ #ifndef SECURITY_FLAG_IGNORE_CERT_CN_INVALID # define SECURITY_FLAG_IGNORE_CERT_CN_INVALID 0x00001000 /* Common name does not match */ #endif /* !SECURITY_FLAG_IGNORE_CERT_CN_INVALID */ #ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */ #endif /* !SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */ /* * Local globals... */ static int tls_options = -1,/* Options for TLS connections */ tls_min_version = _HTTP_TLS_1_0, tls_max_version = _HTTP_TLS_MAX; /* * Local functions... */ static _http_sspi_t *http_sspi_alloc(void); static int http_sspi_client(http_t *http, const char *hostname); static PCCERT_CONTEXT http_sspi_create_credential(http_credential_t *cred); static BOOL http_sspi_find_credentials(http_t *http, const LPWSTR containerName, const char *common_name); static void http_sspi_free(_http_sspi_t *sspi); static BOOL http_sspi_make_credentials(_http_sspi_t *sspi, const LPWSTR containerName, const char *common_name, _http_mode_t mode, int years); static int http_sspi_server(http_t *http, const char *hostname); static void http_sspi_set_allows_any_root(_http_sspi_t *sspi, BOOL allow); static void http_sspi_set_allows_expired_certs(_http_sspi_t *sspi, BOOL allow); static const char *http_sspi_strerror(char *buffer, size_t bufsize, DWORD code); static DWORD http_sspi_verify(PCCERT_CONTEXT cert, const char *common_name, DWORD dwCertFlags); /* * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair. * * @since CUPS 2.0/OS 10.10@ */ int /* O - 1 on success, 0 on failure */ cupsMakeServerCredentials( const char *path, /* I - Keychain path or @code NULL@ for default */ const char *common_name, /* I - Common name */ int num_alt_names, /* I - Number of subject alternate names */ const char **alt_names, /* I - Subject Alternate Names */ time_t expiration_date) /* I - Expiration date */ { _http_sspi_t *sspi; /* SSPI data */ int ret; /* Return value */ 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)); (void)path; (void)num_alt_names; (void)alt_names; sspi = http_sspi_alloc(); ret = http_sspi_make_credentials(sspi, L"ServerContainer", common_name, _HTTP_MODE_SERVER, (int)((expiration_date - time(NULL) + 86399) / 86400 / 365)); http_sspi_free(sspi); return (ret); } /* * 'cupsSetServerCredentials()' - Set the default server credentials. * * Note: The server credentials are used by all threads in the running process. * This function is threadsafe. * * @since CUPS 2.0/OS 10.10@ */ int /* O - 1 on success, 0 on failure */ cupsSetServerCredentials( const char *path, /* I - Keychain path or @code NULL@ for default */ const char *common_name, /* I - Default common name for server */ int auto_create) /* I - 1 = automatically create self-signed certificates */ { DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create)); (void)path; (void)common_name; (void)auto_create; return (0); } /* * 'httpCopyCredentials()' - Copy the credentials associated with the peer in * an encrypted connection. * * @since CUPS 1.5/macOS 10.7@ */ int /* O - Status of call (0 = success) */ httpCopyCredentials( http_t *http, /* I - Connection to server */ cups_array_t **credentials) /* O - Array of credentials */ { DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials)); if (!http || !http->tls || !http->tls->remoteCert || !credentials) { if (credentials) *credentials = NULL; return (-1); } *credentials = cupsArrayNew(NULL, NULL); httpAddCredential(*credentials, http->tls->remoteCert->pbCertEncoded, http->tls->remoteCert->cbCertEncoded); return (0); } /* * '_httpCreateCredentials()' - Create credentials in the internal format. */ http_tls_credentials_t /* O - Internal credentials */ _httpCreateCredentials( cups_array_t *credentials) /* I - Array of credentials */ { return (http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials))); } /* * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name. * * @since CUPS 2.0/OS 10.10@ */ int /* O - 1 if valid, 0 otherwise */ httpCredentialsAreValidForName( cups_array_t *credentials, /* I - Credentials */ const char *common_name) /* I - Name to check */ { int valid = 1; /* Valid name? */ PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)); /* Certificate */ char cert_name[1024]; /* Name from certificate */ if (cert) { if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name))) { /* * Extract common name at end... */ char *ptr = strrchr(cert_name, ','); if (ptr && ptr[1]) _cups_strcpy(cert_name, ptr + 2); } else strlcpy(cert_name, "unknown", sizeof(cert_name)); CertFreeCertificateContext(cert); } else strlcpy(cert_name, "unknown", sizeof(cert_name)); /* * Compare the common names... */ if (_cups_strcasecmp(common_name, cert_name)) { /* * Not an exact match for the common name, check for wildcard certs... */ const char *domain = strchr(common_name, '.'); /* Domain in common name */ if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1)) { /* * Not a wildcard match. */ /* TODO: Check subject alternate names */ valid = 0; } } return (valid); } /* * 'httpCredentialsGetTrust()' - Return the trust of credentials. * * @since CUPS 2.0/OS 10.10@ */ http_trust_t /* O - Level of trust */ httpCredentialsGetTrust( cups_array_t *credentials, /* I - Credentials */ const char *common_name) /* I - Common name for trust lookup */ { http_trust_t trust = HTTP_TRUST_OK; /* Level of trust */ PCCERT_CONTEXT cert = NULL; /* Certificate to validate */ DWORD certFlags = 0; /* Cert verification flags */ _cups_globals_t *cg = _cupsGlobals(); /* Per-thread global data */ if (!common_name) return (HTTP_TRUST_UNKNOWN); cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)); if (!cert) return (HTTP_TRUST_UNKNOWN); if (cg->any_root < 0) _cupsSetDefaults(); if (cg->any_root) certFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA; if (cg->expired_certs) certFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; if (!cg->validate_certs) certFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID; if (http_sspi_verify(cert, common_name, certFlags) != SEC_E_OK) trust = HTTP_TRUST_INVALID; CertFreeCertificateContext(cert); return (trust); } /* * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials. * * @since CUPS 2.0/OS 10.10@ */ time_t /* O - Expiration date of credentials */ httpCredentialsGetExpiration( cups_array_t *credentials) /* I - Credentials */ { time_t expiration_date = 0; /* Expiration data of credentials */ PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)); /* Certificate */ if (cert) { SYSTEMTIME systime; /* System time */ struct tm tm; /* UNIX date/time */ FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime); tm.tm_year = systime.wYear - 1900; tm.tm_mon = systime.wMonth - 1; tm.tm_mday = systime.wDay; tm.tm_hour = systime.wHour; tm.tm_min = systime.wMinute; tm.tm_sec = systime.wSecond; expiration_date = mktime(&tm); CertFreeCertificateContext(cert); } return (expiration_date); } /* * 'httpCredentialsString()' - Return a string representing the credentials. * * @since CUPS 2.0/OS 10.10@ */ size_t /* O - Total size of credentials string */ httpCredentialsString( cups_array_t *credentials, /* I - Credentials */ char *buffer, /* I - Buffer or @code NULL@ */ size_t bufsize) /* I - Size of buffer */ { http_credential_t *first = (http_credential_t *)cupsArrayFirst(credentials); /* First certificate */ PCCERT_CONTEXT cert; /* Certificate */ DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize)); if (!buffer) return (0); if (buffer && bufsize > 0) *buffer = '\0'; cert = http_sspi_create_credential(first); if (cert) { char cert_name[256]; /* Common name */ SYSTEMTIME systime; /* System time */ struct tm tm; /* UNIX date/time */ time_t expiration; /* Expiration date of cert */ unsigned char md5_digest[16]; /* MD5 result */ FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime); tm.tm_year = systime.wYear - 1900; tm.tm_mon = systime.wMonth - 1; tm.tm_mday = systime.wDay; tm.tm_hour = systime.wHour; tm.tm_min = systime.wMinute; tm.tm_sec = systime.wSecond; expiration = mktime(&tm); if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name))) { /* * Extract common name at end... */ char *ptr = strrchr(cert_name, ','); if (ptr && ptr[1]) _cups_strcpy(cert_name, ptr + 2); } else strlcpy(cert_name, "unknown", sizeof(cert_name)); cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest)); 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]); CertFreeCertificateContext(cert); } DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer)); return (strlen(buffer)); } /* * '_httpFreeCredentials()' - Free internal credentials. */ void _httpFreeCredentials( http_tls_credentials_t credentials) /* I - Internal credentials */ { if (!credentials) return; CertFreeCertificateContext(credentials); } /* * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file. * * @since CUPS 2.0/OS 10.10@ */ int /* O - 0 on success, -1 on error */ httpLoadCredentials( const char *path, /* I - Keychain path or @code NULL@ for default */ cups_array_t **credentials, /* IO - Credentials */ const char *common_name) /* I - Common name for credentials */ { HCERTSTORE store = NULL; /* Certificate store */ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */ DWORD dwSize = 0; /* 32 bit size */ PBYTE p = NULL; /* Temporary storage */ HCRYPTPROV hProv = (HCRYPTPROV)NULL; /* Handle to a CSP */ CERT_NAME_BLOB sib; /* Arbitrary array of bytes */ #ifdef DEBUG char error[1024]; /* Error message buffer */ #endif /* DEBUG */ DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name)); (void)path; if (credentials) { *credentials = NULL; } else { DEBUG_puts("1httpLoadCredentials: NULL credentials pointer, returning -1."); return (-1); } if (!common_name) { DEBUG_puts("1httpLoadCredentials: Bad common name, returning -1."); return (-1); } if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) { if (GetLastError() == NTE_EXISTS) { if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) { DEBUG_printf(("1httpLoadCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } } } 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"); if (!store) { DEBUG_printf(("1httpLoadCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } dwSize = 0; if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL)) { DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } p = (PBYTE)malloc(dwSize); if (!p) { DEBUG_printf(("1httpLoadCredentials: malloc failed for %d bytes.", dwSize)); goto cleanup; } if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL)) { DEBUG_printf(("1httpLoadCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } sib.cbData = dwSize; sib.pbData = p; storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL); if (!storedContext) { DEBUG_printf(("1httpLoadCredentials: Unable to find credentials for \"%s\".", common_name)); goto cleanup; } *credentials = cupsArrayNew(NULL, NULL); httpAddCredential(*credentials, storedContext->pbCertEncoded, storedContext->cbCertEncoded); cleanup: /* * Cleanup */ if (storedContext) CertFreeCertificateContext(storedContext); if (p) free(p); if (store) CertCloseStore(store, 0); if (hProv) CryptReleaseContext(hProv, 0); DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1)); return (*credentials ? 0 : -1); } /* * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file. * * @since CUPS 2.0/OS 10.10@ */ int /* O - -1 on error, 0 on success */ httpSaveCredentials( const char *path, /* I - Keychain path or @code NULL@ for default */ cups_array_t *credentials, /* I - Credentials */ const char *common_name) /* I - Common name for credentials */ { HCERTSTORE store = NULL; /* Certificate store */ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */ PCCERT_CONTEXT createdContext = NULL; /* Context created by us */ DWORD dwSize = 0; /* 32 bit size */ PBYTE p = NULL; /* Temporary storage */ HCRYPTPROV hProv = (HCRYPTPROV)NULL; /* Handle to a CSP */ CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */ int ret = -1; /* Return value */ #ifdef DEBUG char error[1024]; /* Error message buffer */ #endif /* DEBUG */ DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, credentials, common_name)); (void)path; if (!common_name) { DEBUG_puts("1httpSaveCredentials: Bad common name, returning -1."); return (-1); } createdContext = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)); if (!createdContext) { DEBUG_puts("1httpSaveCredentials: Bad credentials, returning -1."); return (-1); } if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) { if (GetLastError() == NTE_EXISTS) { if (!CryptAcquireContextW(&hProv, L"RememberedContainer", MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) { DEBUG_printf(("1httpSaveCredentials: CryptAcquireContext failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } } } 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"); if (!store) { DEBUG_printf(("1httpSaveCredentials: CertOpenSystemStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } dwSize = 0; if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL)) { DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } p = (PBYTE)malloc(dwSize); if (!p) { DEBUG_printf(("1httpSaveCredentials: malloc failed for %d bytes.", dwSize)); goto cleanup; } if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL)) { DEBUG_printf(("1httpSaveCredentials: CertStrToName failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } /* * Add the created context to the named store, and associate it with the named * container... */ if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext)) { DEBUG_printf(("1httpSaveCredentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } ZeroMemory(&ckp, sizeof(ckp)); ckp.pwszContainerName = L"RememberedContainer"; ckp.pwszProvName = MS_DEF_PROV_W; ckp.dwProvType = PROV_RSA_FULL; ckp.dwFlags = CRYPT_MACHINE_KEYSET; ckp.dwKeySpec = AT_KEYEXCHANGE; if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp)) { DEBUG_printf(("1httpSaveCredentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(error, sizeof(error), GetLastError()))); goto cleanup; } ret = 0; cleanup: /* * Cleanup */ if (createdContext) CertFreeCertificateContext(createdContext); if (storedContext) CertFreeCertificateContext(storedContext); if (p) free(p); if (store) CertCloseStore(store, 0); if (hProv) CryptReleaseContext(hProv, 0); DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret)); return (ret); } /* * '_httpTLSInitialize()' - Initialize the TLS stack. */ void _httpTLSInitialize(void) { /* * Nothing to do... */ } /* * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes. */ size_t /* O - Bytes available */ _httpTLSPending(http_t *http) /* I - HTTP connection */ { if (http->tls) return (http->tls->readBufferUsed); else return (0); } /* * '_httpTLSRead()' - Read from a SSL/TLS connection. */ int /* O - Bytes read */ _httpTLSRead(http_t *http, /* I - HTTP connection */ char *buf, /* I - Buffer to store data */ int len) /* I - Length of buffer */ { int i; /* Looping var */ _http_sspi_t *sspi = http->tls; /* SSPI data */ SecBufferDesc message; /* Array of SecBuffer struct */ SecBuffer buffers[4] = { 0 }; /* Security package buffer */ int num = 0; /* Return value */ PSecBuffer pDataBuffer; /* Data buffer */ PSecBuffer pExtraBuffer; /* Excess data buffer */ SECURITY_STATUS scRet; /* SSPI status */ DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http, buf, len)); /* * If there are bytes that have already been decrypted and have not yet been * read, return those... */ if (sspi->readBufferUsed > 0) { int bytesToCopy = min(sspi->readBufferUsed, len); /* Number of bytes to copy */ memcpy(buf, sspi->readBuffer, bytesToCopy); sspi->readBufferUsed -= bytesToCopy; if (sspi->readBufferUsed > 0) memmove(sspi->readBuffer, sspi->readBuffer + bytesToCopy, sspi->readBufferUsed); DEBUG_printf(("5_httpTLSRead: Returning %d bytes previously decrypted.", bytesToCopy)); return (bytesToCopy); } /* * Initialize security buffer structs */ message.ulVersion = SECBUFFER_VERSION; message.cBuffers = 4; message.pBuffers = buffers; do { /* * If there is not enough space in the buffer, then increase its size... */ if (sspi->decryptBufferLength <= sspi->decryptBufferUsed) { BYTE *temp; /* New buffer */ if (sspi->decryptBufferLength >= 262144) { WSASetLastError(E_OUTOFMEMORY); DEBUG_puts("_httpTLSRead: Decryption buffer too large (>256k)"); return (-1); } if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL) { DEBUG_printf(("_httpTLSRead: Unable to allocate %d byte decryption buffer.", sspi->decryptBufferLength + 4096)); WSASetLastError(E_OUTOFMEMORY); return (-1); } sspi->decryptBufferLength += 4096; sspi->decryptBuffer = temp; DEBUG_printf(("_httpTLSRead: Resized decryption buffer to %d bytes.", sspi->decryptBufferLength)); } buffers[0].pvBuffer = sspi->decryptBuffer; buffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed; buffers[0].BufferType = SECBUFFER_DATA; buffers[1].BufferType = SECBUFFER_EMPTY; buffers[2].BufferType = SECBUFFER_EMPTY; buffers[3].BufferType = SECBUFFER_EMPTY; DEBUG_printf(("5_httpTLSRead: decryptBufferUsed=%d", sspi->decryptBufferUsed)); scRet = DecryptMessage(&sspi->context, &message, 0, NULL); if (scRet == SEC_E_INCOMPLETE_MESSAGE) { num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0); if (num < 0) { DEBUG_printf(("5_httpTLSRead: recv failed: %d", WSAGetLastError())); return (-1); } else if (num == 0) { DEBUG_puts("5_httpTLSRead: Server disconnected."); return (0); } DEBUG_printf(("5_httpTLSRead: Read %d bytes into decryption buffer.", num)); sspi->decryptBufferUsed += num; } } while (scRet == SEC_E_INCOMPLETE_MESSAGE); if (scRet == SEC_I_CONTEXT_EXPIRED) { DEBUG_puts("5_httpTLSRead: Context expired."); WSASetLastError(WSAECONNRESET); return (-1); } else if (scRet != SEC_E_OK) { DEBUG_printf(("5_httpTLSRead: DecryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); WSASetLastError(WSASYSCALLFAILURE); return (-1); } /* * The decryption worked. Now, locate data buffer. */ pDataBuffer = NULL; pExtraBuffer = NULL; for (i = 1; i < 4; i++) { if (buffers[i].BufferType == SECBUFFER_DATA) pDataBuffer = &buffers[i]; else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA)) pExtraBuffer = &buffers[i]; } /* * If a data buffer is found, then copy the decrypted bytes to the passed-in * buffer... */ if (pDataBuffer) { int bytesToCopy = min((int)pDataBuffer->cbBuffer, len); /* Number of bytes to copy into buf */ int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy; /* Number of bytes to save in our read buffer */ if (bytesToCopy) memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy); /* * If there are more decrypted bytes than can be copied to the passed in * buffer, then save them... */ if (bytesToSave) { if ((sspi->readBufferLength - sspi->readBufferUsed) < bytesToSave) { BYTE *temp; /* New buffer pointer */ if ((temp = realloc(sspi->readBuffer, sspi->readBufferUsed + bytesToSave)) == NULL) { DEBUG_printf(("_httpTLSRead: Unable to allocate %d bytes.", sspi->readBufferUsed + bytesToSave)); WSASetLastError(E_OUTOFMEMORY); return (-1); } sspi->readBufferLength = sspi->readBufferUsed + bytesToSave; sspi->readBuffer = temp; } memcpy(((BYTE *)sspi->readBuffer) + sspi->readBufferUsed, ((BYTE *)pDataBuffer->pvBuffer) + bytesToCopy, bytesToSave); sspi->readBufferUsed += bytesToSave; } num = bytesToCopy; } else { DEBUG_puts("_httpTLSRead: Unable to find data buffer."); WSASetLastError(WSASYSCALLFAILURE); return (-1); } /* * If the decryption process left extra bytes, then save those back in * decryptBuffer. They will be processed the next time through the loop. */ if (pExtraBuffer) { memmove(sspi->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); sspi->decryptBufferUsed = pExtraBuffer->cbBuffer; } else { sspi->decryptBufferUsed = 0; } return (num); } /* * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options. */ void _httpTLSSetOptions(int options, /* I - Options */ int min_version, /* I - Minimum TLS version */ int max_version) /* I - Maximum TLS version */ { if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0) { tls_options = options; tls_min_version = min_version; tls_max_version = max_version; } } /* * '_httpTLSStart()' - Set up SSL/TLS support on a connection. */ int /* O - 0 on success, -1 on failure */ _httpTLSStart(http_t *http) /* I - HTTP connection */ { char hostname[256], /* Hostname */ *hostptr; /* Pointer into hostname */ DEBUG_printf(("3_httpTLSStart(http=%p)", http)); if (tls_options < 0) { DEBUG_puts("4_httpTLSStart: Setting defaults."); _cupsSetDefaults(); DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options)); } if ((http->tls = http_sspi_alloc()) == NULL) return (-1); if (http->mode == _HTTP_MODE_CLIENT) { /* * Client: determine hostname... */ if (httpAddrLocalhost(http->hostaddr)) { strlcpy(hostname, "localhost", sizeof(hostname)); } else { /* * Otherwise make sure the hostname we have does not end in a trailing dot. */ strlcpy(hostname, http->hostname, sizeof(hostname)); if ((hostptr = hostname + strlen(hostname) - 1) >= hostname && *hostptr == '.') *hostptr = '\0'; } return (http_sspi_client(http, hostname)); } else { /* * Server: determine hostname to use... */ if (http->fields[HTTP_FIELD_HOST]) { /* * Use hostname for TLS upgrade... */ strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname)); } else { /* * Resolve hostname from connection address... */ http_addr_t addr; /* Connection address */ socklen_t addrlen; /* Length of address */ addrlen = sizeof(addr); if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen)) { DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno))); hostname[0] = '\0'; } else if (httpAddrLocalhost(&addr)) hostname[0] = '\0'; else { httpAddrLookup(&addr, hostname, sizeof(hostname)); DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname)); } } return (http_sspi_server(http, hostname)); } } /* * '_httpTLSStop()' - Shut down SSL/TLS on a connection. */ void _httpTLSStop(http_t *http) /* I - HTTP connection */ { _http_sspi_t *sspi = http->tls; /* SSPI data */ if (sspi->contextInitialized && http->fd >= 0) { SecBufferDesc message; /* Array of SecBuffer struct */ SecBuffer buffers[1] = { 0 }; /* Security package buffer */ DWORD dwType; /* Type */ DWORD status; /* Status */ /* * Notify schannel that we are about to close the connection. */ dwType = SCHANNEL_SHUTDOWN; buffers[0].pvBuffer = &dwType; buffers[0].BufferType = SECBUFFER_TOKEN; buffers[0].cbBuffer = sizeof(dwType); message.cBuffers = 1; message.pBuffers = buffers; message.ulVersion = SECBUFFER_VERSION; status = ApplyControlToken(&sspi->context, &message); if (SUCCEEDED(status)) { PBYTE pbMessage; /* Message buffer */ DWORD cbMessage; /* Message buffer count */ DWORD cbData; /* Data count */ DWORD dwSSPIFlags; /* SSL attributes we requested */ DWORD dwSSPIOutFlags; /* SSL attributes we received */ TimeStamp tsExpiry; /* Time stamp */ dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY | ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_STREAM; buffers[0].pvBuffer = NULL; buffers[0].BufferType = SECBUFFER_TOKEN; buffers[0].cbBuffer = 0; message.cBuffers = 1; message.pBuffers = buffers; message.ulVersion = SECBUFFER_VERSION; status = AcceptSecurityContext(&sspi->creds, &sspi->context, NULL, dwSSPIFlags, SECURITY_NATIVE_DREP, NULL, &message, &dwSSPIOutFlags, &tsExpiry); if (SUCCEEDED(status)) { pbMessage = buffers[0].pvBuffer; cbMessage = buffers[0].cbBuffer; /* * Send the close notify message to the client. */ if (pbMessage && cbMessage) { cbData = send(http->fd, pbMessage, cbMessage, 0); if ((cbData == SOCKET_ERROR) || (cbData == 0)) { status = WSAGetLastError(); DEBUG_printf(("_httpTLSStop: sending close notify failed: %d", status)); } else { FreeContextBuffer(pbMessage); } } } else { DEBUG_printf(("_httpTLSStop: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status))); } } else { DEBUG_printf(("_httpTLSStop: ApplyControlToken failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), status))); } } http_sspi_free(sspi); http->tls = NULL; } /* * '_httpTLSWrite()' - Write to a SSL/TLS connection. */ int /* O - Bytes written */ _httpTLSWrite(http_t *http, /* I - HTTP connection */ const char *buf, /* I - Buffer holding data */ int len) /* I - Length of buffer */ { _http_sspi_t *sspi = http->tls; /* SSPI data */ SecBufferDesc message; /* Array of SecBuffer struct */ SecBuffer buffers[4] = { 0 }; /* Security package buffer */ int bufferLen; /* Buffer length */ int bytesLeft; /* Bytes left to write */ const char *bufptr; /* Pointer into buffer */ int num = 0; /* Return value */ bufferLen = sspi->streamSizes.cbMaximumMessage + sspi->streamSizes.cbHeader + sspi->streamSizes.cbTrailer; if (bufferLen > sspi->writeBufferLength) { BYTE *temp; /* New buffer pointer */ if ((temp = (BYTE *)realloc(sspi->writeBuffer, bufferLen)) == NULL) { DEBUG_printf(("_httpTLSWrite: Unable to allocate buffer of %d bytes.", bufferLen)); WSASetLastError(E_OUTOFMEMORY); return (-1); } sspi->writeBuffer = temp; sspi->writeBufferLength = bufferLen; } bytesLeft = len; bufptr = buf; while (bytesLeft) { int chunk = min((int)sspi->streamSizes.cbMaximumMessage, bytesLeft); /* Size of data to write */ SECURITY_STATUS scRet; /* SSPI status */ /* * Copy user data into the buffer, starting just past the header... */ memcpy(sspi->writeBuffer + sspi->streamSizes.cbHeader, bufptr, chunk); /* * Setup the SSPI buffers */ message.ulVersion = SECBUFFER_VERSION; message.cBuffers = 4; message.pBuffers = buffers; buffers[0].pvBuffer = sspi->writeBuffer; buffers[0].cbBuffer = sspi->streamSizes.cbHeader; buffers[0].BufferType = SECBUFFER_STREAM_HEADER; buffers[1].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader; buffers[1].cbBuffer = (unsigned long) chunk; buffers[1].BufferType = SECBUFFER_DATA; buffers[2].pvBuffer = sspi->writeBuffer + sspi->streamSizes.cbHeader + chunk; buffers[2].cbBuffer = sspi->streamSizes.cbTrailer; buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; buffers[3].BufferType = SECBUFFER_EMPTY; /* * Encrypt the data */ scRet = EncryptMessage(&sspi->context, 0, &message, 0); if (FAILED(scRet)) { DEBUG_printf(("_httpTLSWrite: EncryptMessage failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); WSASetLastError(WSASYSCALLFAILURE); return (-1); } /* * Send the data. Remember the size of the total data to send is the size * of the header, the size of the data the caller passed in and the size * of the trailer... */ num = send(http->fd, sspi->writeBuffer, buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, 0); if (num <= 0) { DEBUG_printf(("_httpTLSWrite: send failed: %ld", WSAGetLastError())); return (num); } bytesLeft -= chunk; bufptr += chunk; } return (len); } #if 0 /* * 'http_setup_ssl()' - Set up SSL/TLS support on a connection. */ static int /* O - 0 on success, -1 on failure */ http_setup_ssl(http_t *http) /* I - Connection to server */ { char hostname[256], /* Hostname */ *hostptr; /* Pointer into hostname */ TCHAR username[256]; /* Username returned from GetUserName() */ TCHAR commonName[256];/* Common name for certificate */ DWORD dwSize; /* 32 bit size */ DEBUG_printf(("7http_setup_ssl(http=%p)", http)); /* * Get the hostname to use for SSL... */ if (httpAddrLocalhost(http->hostaddr)) { strlcpy(hostname, "localhost", sizeof(hostname)); } else { /* * Otherwise make sure the hostname we have does not end in a trailing dot. */ strlcpy(hostname, http->hostname, sizeof(hostname)); if ((hostptr = hostname + strlen(hostname) - 1) >= hostname && *hostptr == '.') *hostptr = '\0'; } http->tls = http_sspi_alloc(); if (!http->tls) { _cupsSetHTTPError(HTTP_STATUS_ERROR); return (-1); } dwSize = sizeof(username) / sizeof(TCHAR); GetUserName(username, &dwSize); _sntprintf_s(commonName, sizeof(commonName) / sizeof(TCHAR), sizeof(commonName) / sizeof(TCHAR), TEXT("CN=%s"), username); if (!_sspiGetCredentials(http->tls, L"ClientContainer", commonName, FALSE)) { _sspiFree(http->tls); http->tls = NULL; http->error = EIO; http->status = HTTP_STATUS_ERROR; _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Unable to establish a secure connection to host."), 1); return (-1); } _sspiSetAllowsAnyRoot(http->tls, TRUE); _sspiSetAllowsExpiredCerts(http->tls, TRUE); if (!_sspiConnect(http->tls, hostname)) { _sspiFree(http->tls); http->tls = NULL; http->error = EIO; http->status = HTTP_STATUS_ERROR; _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Unable to establish a secure connection to host."), 1); return (-1); } return (0); } #endif // 0 /* * 'http_sspi_alloc()' - Allocate SSPI object. */ static _http_sspi_t * /* O - New SSPI/SSL object */ http_sspi_alloc(void) { return ((_http_sspi_t *)calloc(sizeof(_http_sspi_t), 1)); } /* * 'http_sspi_client()' - Negotiate a TLS connection as a client. */ static int /* O - 0 on success, -1 on failure */ http_sspi_client(http_t *http, /* I - Client connection */ const char *hostname) /* I - Server hostname */ { _http_sspi_t *sspi = http->tls; /* SSPI data */ DWORD dwSize; /* Size for buffer */ DWORD dwSSPIFlags; /* SSL connection attributes we want */ DWORD dwSSPIOutFlags; /* SSL connection attributes we got */ TimeStamp tsExpiry; /* Time stamp */ SECURITY_STATUS scRet; /* Status */ int cbData; /* Data count */ SecBufferDesc inBuffer; /* Array of SecBuffer structs */ SecBuffer inBuffers[2]; /* Security package buffer */ SecBufferDesc outBuffer; /* Array of SecBuffer structs */ SecBuffer outBuffers[1]; /* Security package buffer */ int ret = 0; /* Return value */ char username[1024], /* Current username */ common_name[1024]; /* CN=username */ DEBUG_printf(("4http_sspi_client(http=%p, hostname=\"%s\")", http, hostname)); dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; /* * Lookup the client certificate... */ dwSize = sizeof(username); GetUserNameA(username, &dwSize); snprintf(common_name, sizeof(common_name), "CN=%s", username); if (!http_sspi_find_credentials(http, L"ClientContainer", common_name)) if (!http_sspi_make_credentials(http->tls, L"ClientContainer", common_name, _HTTP_MODE_CLIENT, 10)) { DEBUG_puts("5http_sspi_client: Unable to get client credentials."); return (-1); } /* * Initiate a ClientHello message and generate a token. */ outBuffers[0].pvBuffer = NULL; outBuffers[0].BufferType = SECBUFFER_TOKEN; outBuffers[0].cbBuffer = 0; outBuffer.cBuffers = 1; outBuffer.pBuffers = outBuffers; outBuffer.ulVersion = SECBUFFER_VERSION; scRet = InitializeSecurityContext(&sspi->creds, NULL, TEXT(""), dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, &sspi->context, &outBuffer, &dwSSPIOutFlags, &tsExpiry); if (scRet != SEC_I_CONTINUE_NEEDED) { DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(1) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); return (-1); } /* * Send response to server if there is one. */ if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) { if ((cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0)) <= 0) { DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError())); FreeContextBuffer(outBuffers[0].pvBuffer); DeleteSecurityContext(&sspi->context); return (-1); } DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData)); FreeContextBuffer(outBuffers[0].pvBuffer); outBuffers[0].pvBuffer = NULL; } dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; sspi->decryptBufferUsed = 0; /* * Loop until the handshake is finished or an error occurs. */ scRet = SEC_I_CONTINUE_NEEDED; while(scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_E_INCOMPLETE_MESSAGE || scRet == SEC_I_INCOMPLETE_CREDENTIALS) { if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE) { if (sspi->decryptBufferLength <= sspi->decryptBufferUsed) { BYTE *temp; /* New buffer */ if (sspi->decryptBufferLength >= 262144) { WSASetLastError(E_OUTOFMEMORY); DEBUG_puts("5http_sspi_client: Decryption buffer too large (>256k)"); return (-1); } if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL) { DEBUG_printf(("5http_sspi_client: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096)); WSASetLastError(E_OUTOFMEMORY); return (-1); } sspi->decryptBufferLength += 4096; sspi->decryptBuffer = temp; } cbData = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0); if (cbData < 0) { DEBUG_printf(("5http_sspi_client: recv failed: %d", WSAGetLastError())); return (-1); } else if (cbData == 0) { DEBUG_printf(("5http_sspi_client: Server unexpectedly disconnected.")); return (-1); } DEBUG_printf(("5http_sspi_client: %d bytes of handshake data received", cbData)); sspi->decryptBufferUsed += cbData; } /* * Set up the input buffers. Buffer 0 is used to pass in data received from * the server. Schannel will consume some or all of this. Leftover data * (if any) will be placed in buffer 1 and given a buffer type of * SECBUFFER_EXTRA. */ inBuffers[0].pvBuffer = sspi->decryptBuffer; inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed; inBuffers[0].BufferType = SECBUFFER_TOKEN; inBuffers[1].pvBuffer = NULL; inBuffers[1].cbBuffer = 0; inBuffers[1].BufferType = SECBUFFER_EMPTY; inBuffer.cBuffers = 2; inBuffer.pBuffers = inBuffers; inBuffer.ulVersion = SECBUFFER_VERSION; /* * Set up the output buffers. These are initialized to NULL so as to make it * less likely we'll attempt to free random garbage later. */ outBuffers[0].pvBuffer = NULL; outBuffers[0].BufferType = SECBUFFER_TOKEN; outBuffers[0].cbBuffer = 0; outBuffer.cBuffers = 1; outBuffer.pBuffers = outBuffers; outBuffer.ulVersion = SECBUFFER_VERSION; /* * Call InitializeSecurityContext. */ scRet = InitializeSecurityContext(&sspi->creds, &sspi->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry); /* * If InitializeSecurityContext was successful (or if the error was one of * the special extended ones), send the contents of the output buffer to the * server. */ if (scRet == SEC_E_OK || scRet == SEC_I_CONTINUE_NEEDED || FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) { if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) { cbData = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); if (cbData <= 0) { DEBUG_printf(("5http_sspi_client: send failed: %d", WSAGetLastError())); FreeContextBuffer(outBuffers[0].pvBuffer); DeleteSecurityContext(&sspi->context); return (-1); } DEBUG_printf(("5http_sspi_client: %d bytes of handshake data sent.", cbData)); /* * Free output buffer. */ FreeContextBuffer(outBuffers[0].pvBuffer); outBuffers[0].pvBuffer = NULL; } } /* * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, then we * need to read more data from the server and try again. */ if (scRet == SEC_E_INCOMPLETE_MESSAGE) continue; /* * If InitializeSecurityContext returned SEC_E_OK, then the handshake * completed successfully. */ if (scRet == SEC_E_OK) { /* * If the "extra" buffer contains data, this is encrypted application * protocol layer stuff. It needs to be saved. The application layer will * later decrypt it with DecryptMessage. */ DEBUG_puts("5http_sspi_client: Handshake was successful."); if (inBuffers[1].BufferType == SECBUFFER_EXTRA) { memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer); sspi->decryptBufferUsed = inBuffers[1].cbBuffer; DEBUG_printf(("5http_sspi_client: %d bytes of app data was bundled with handshake data", sspi->decryptBufferUsed)); } else sspi->decryptBufferUsed = 0; /* * Bail out to quit */ break; } /* * Check for fatal error. */ if (FAILED(scRet)) { DEBUG_printf(("5http_sspi_client: InitializeSecurityContext(2) failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); ret = -1; break; } /* * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS, * then the server just requested client authentication. */ if (scRet == SEC_I_INCOMPLETE_CREDENTIALS) { /* * Unimplemented */ DEBUG_printf(("5http_sspi_client: server requested client credentials.")); ret = -1; break; } /* * Copy any leftover data from the "extra" buffer, and go around again. */ if (inBuffers[1].BufferType == SECBUFFER_EXTRA) { memmove(sspi->decryptBuffer, sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer, inBuffers[1].cbBuffer); sspi->decryptBufferUsed = inBuffers[1].cbBuffer; } else { sspi->decryptBufferUsed = 0; } } if (!ret) { /* * Success! Get the server cert */ sspi->contextInitialized = TRUE; scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID *)&(sspi->remoteCert)); if (scRet != SEC_E_OK) { DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); return (-1); } /* * Find out how big the header/trailer will be: */ scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes); if (scRet != SEC_E_OK) { DEBUG_printf(("5http_sspi_client: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); ret = -1; } } return (ret); } /* * 'http_sspi_create_credential()' - Create an SSPI certificate context. */ static PCCERT_CONTEXT /* O - Certificate context */ http_sspi_create_credential( http_credential_t *cred) /* I - Credential */ { if (cred) return (CertCreateCertificateContext(X509_ASN_ENCODING, cred->data, cred->datalen)); else return (NULL); } /* * 'http_sspi_find_credentials()' - Retrieve a TLS certificate from the system store. */ static BOOL /* O - 1 on success, 0 on failure */ http_sspi_find_credentials( http_t *http, /* I - HTTP connection */ const LPWSTR container, /* I - Cert container name */ const char *common_name) /* I - Common name of certificate */ { _http_sspi_t *sspi = http->tls; /* SSPI data */ HCERTSTORE store = NULL; /* Certificate store */ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */ DWORD dwSize = 0; /* 32 bit size */ PBYTE p = NULL; /* Temporary storage */ HCRYPTPROV hProv = (HCRYPTPROV)NULL; /* Handle to a CSP */ CERT_NAME_BLOB sib; /* Arbitrary array of bytes */ SCHANNEL_CRED SchannelCred; /* Schannel credential data */ TimeStamp tsExpiry; /* Time stamp */ SECURITY_STATUS Status; /* Status */ BOOL ok = TRUE; /* Return value */ if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) { if (GetLastError() == NTE_EXISTS) { if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) { DEBUG_printf(("5http_sspi_find_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } } } 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"); if (!store) { DEBUG_printf(("5http_sspi_find_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } dwSize = 0; if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL)) { DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } p = (PBYTE)malloc(dwSize); if (!p) { DEBUG_printf(("5http_sspi_find_credentials: malloc failed for %d bytes.", dwSize)); ok = FALSE; goto cleanup; } if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL)) { DEBUG_printf(("5http_sspi_find_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } sib.cbData = dwSize; sib.pbData = p; storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL); if (!storedContext) { DEBUG_printf(("5http_sspi_find_credentials: Unable to find credentials for \"%s\".", common_name)); ok = FALSE; goto cleanup; } ZeroMemory(&SchannelCred, sizeof(SchannelCred)); SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; SchannelCred.cCreds = 1; SchannelCred.paCred = &storedContext; /* * Set supported protocols (can also be overriden in the registry...) */ #ifdef SP_PROT_TLS1_2_SERVER if (http->mode == _HTTP_MODE_SERVER) { if (tls_min_version > _HTTP_TLS_1_1) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER; else if (tls_min_version > _HTTP_TLS_1_0) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER; else if (tls_min_version == _HTTP_TLS_SSL3) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER | SP_PROT_SSL3_SERVER; else SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER; } else { if (tls_min_version > _HTTP_TLS_1_1) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT; else if (tls_min_version > _HTTP_TLS_1_0) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT; else if (tls_min_version == _HTTP_TLS_SSL3) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_SSL3_CLIENT; else SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT; } #else if (http->mode == _HTTP_MODE_SERVER) { if (tls_min_version == _HTTP_TLS_SSL3) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER; else SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER; } else { if (tls_min_version == _HTTP_TLS_SSL3) SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT; else SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT; } #endif /* SP_PROT_TLS1_2_SERVER */ /* 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... */ /* * Create an SSPI credential. */ Status = AcquireCredentialsHandle(NULL, UNISP_NAME, http->mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry); if (Status != SEC_E_OK) { DEBUG_printf(("5http_sspi_find_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status))); ok = FALSE; goto cleanup; } cleanup: /* * Cleanup */ if (storedContext) CertFreeCertificateContext(storedContext); if (p) free(p); if (store) CertCloseStore(store, 0); if (hProv) CryptReleaseContext(hProv, 0); return (ok); } /* * 'http_sspi_free()' - Close a connection and free resources. */ static void http_sspi_free(_http_sspi_t *sspi) /* I - SSPI data */ { if (!sspi) return; if (sspi->contextInitialized) DeleteSecurityContext(&sspi->context); if (sspi->decryptBuffer) free(sspi->decryptBuffer); if (sspi->readBuffer) free(sspi->readBuffer); if (sspi->writeBuffer) free(sspi->writeBuffer); if (sspi->localCert) CertFreeCertificateContext(sspi->localCert); if (sspi->remoteCert) CertFreeCertificateContext(sspi->remoteCert); free(sspi); } /* * 'http_sspi_make_credentials()' - Create a TLS certificate in the system store. */ static BOOL /* O - 1 on success, 0 on failure */ http_sspi_make_credentials( _http_sspi_t *sspi, /* I - SSPI data */ const LPWSTR container, /* I - Cert container name */ const char *common_name, /* I - Common name of certificate */ _http_mode_t mode, /* I - Client or server? */ int years) /* I - Years until expiration */ { HCERTSTORE store = NULL; /* Certificate store */ PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */ PCCERT_CONTEXT createdContext = NULL; /* Context created by us */ DWORD dwSize = 0; /* 32 bit size */ PBYTE p = NULL; /* Temporary storage */ HCRYPTPROV hProv = (HCRYPTPROV)NULL; /* Handle to a CSP */ CERT_NAME_BLOB sib; /* Arbitrary array of bytes */ SCHANNEL_CRED SchannelCred; /* Schannel credential data */ TimeStamp tsExpiry; /* Time stamp */ SECURITY_STATUS Status; /* Status */ HCRYPTKEY hKey = (HCRYPTKEY)NULL; /* Handle to crypto key */ CRYPT_KEY_PROV_INFO kpi; /* Key container info */ SYSTEMTIME et; /* System time */ CERT_EXTENSIONS exts; /* Array of cert extensions */ CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */ BOOL ok = TRUE; /* Return value */ DEBUG_printf(("4http_sspi_make_credentials(sspi=%p, container=%p, common_name=\"%s\", mode=%d, years=%d)", sspi, container, common_name, mode, years)); if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) { if (GetLastError() == NTE_EXISTS) { if (!CryptAcquireContextW(&hProv, (LPWSTR)container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) { DEBUG_printf(("5http_sspi_make_credentials: CryptAcquireContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } } } 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"); if (!store) { DEBUG_printf(("5http_sspi_make_credentials: CertOpenSystemStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } dwSize = 0; if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL)) { DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } p = (PBYTE)malloc(dwSize); if (!p) { DEBUG_printf(("5http_sspi_make_credentials: malloc failed for %d bytes", dwSize)); ok = FALSE; goto cleanup; } if (!CertStrToNameA(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL)) { DEBUG_printf(("5http_sspi_make_credentials: CertStrToName failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } /* * Create a private key and self-signed certificate... */ if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey)) { DEBUG_printf(("5http_sspi_make_credentials: CryptGenKey failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } ZeroMemory(&kpi, sizeof(kpi)); kpi.pwszContainerName = (LPWSTR)container; kpi.pwszProvName = MS_DEF_PROV_W; kpi.dwProvType = PROV_RSA_FULL; kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; kpi.dwKeySpec = AT_KEYEXCHANGE; GetSystemTime(&et); et.wYear += years; if (et.wMonth == 2 && et.wDay == 29) et.wDay = 28; /* Avoid Feb 29th due to leap years */ ZeroMemory(&exts, sizeof(exts)); createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, &et, &exts); if (!createdContext) { DEBUG_printf(("5http_sspi_make_credentials: CertCreateSelfSignCertificate failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } /* * Add the created context to the named store, and associate it with the named * container... */ if (!CertAddCertificateContextToStore(store, createdContext, CERT_STORE_ADD_REPLACE_EXISTING, &storedContext)) { DEBUG_printf(("5http_sspi_make_credentials: CertAddCertificateContextToStore failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } ZeroMemory(&ckp, sizeof(ckp)); ckp.pwszContainerName = (LPWSTR) container; ckp.pwszProvName = MS_DEF_PROV_W; ckp.dwProvType = PROV_RSA_FULL; ckp.dwFlags = CRYPT_MACHINE_KEYSET; ckp.dwKeySpec = AT_KEYEXCHANGE; if (!CertSetCertificateContextProperty(storedContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp)) { DEBUG_printf(("5http_sspi_make_credentials: CertSetCertificateContextProperty failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), GetLastError()))); ok = FALSE; goto cleanup; } /* * Get a handle to use the certificate... */ ZeroMemory(&SchannelCred, sizeof(SchannelCred)); SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; SchannelCred.cCreds = 1; SchannelCred.paCred = &storedContext; /* * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client. */ if (mode == _HTTP_MODE_SERVER) SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1; /* * Create an SSPI credential. */ Status = AcquireCredentialsHandle(NULL, UNISP_NAME, mode == _HTTP_MODE_SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &sspi->creds, &tsExpiry); if (Status != SEC_E_OK) { DEBUG_printf(("5http_sspi_make_credentials: AcquireCredentialsHandle failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), Status))); ok = FALSE; goto cleanup; } cleanup: /* * Cleanup */ if (hKey) CryptDestroyKey(hKey); if (createdContext) CertFreeCertificateContext(createdContext); if (storedContext) CertFreeCertificateContext(storedContext); if (p) free(p); if (store) CertCloseStore(store, 0); if (hProv) CryptReleaseContext(hProv, 0); return (ok); } /* * 'http_sspi_server()' - Negotiate a TLS connection as a server. */ static int /* O - 0 on success, -1 on failure */ http_sspi_server(http_t *http, /* I - HTTP connection */ const char *hostname) /* I - Hostname of server */ { _http_sspi_t *sspi = http->tls; /* I - SSPI data */ char common_name[512]; /* Common name for cert */ DWORD dwSSPIFlags; /* SSL connection attributes we want */ DWORD dwSSPIOutFlags; /* SSL connection attributes we got */ TimeStamp tsExpiry; /* Time stamp */ SECURITY_STATUS scRet; /* SSPI Status */ SecBufferDesc inBuffer; /* Array of SecBuffer structs */ SecBuffer inBuffers[2]; /* Security package buffer */ SecBufferDesc outBuffer; /* Array of SecBuffer structs */ SecBuffer outBuffers[1]; /* Security package buffer */ int num = 0; /* 32 bit status value */ BOOL fInitContext = TRUE; /* Has the context been init'd? */ int ret = 0; /* Return value */ DEBUG_printf(("4http_sspi_server(http=%p, hostname=\"%s\")", http, hostname)); dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY | ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_STREAM; sspi->decryptBufferUsed = 0; /* * Lookup the server certificate... */ snprintf(common_name, sizeof(common_name), "CN=%s", hostname); if (!http_sspi_find_credentials(http, L"ServerContainer", common_name)) if (!http_sspi_make_credentials(http->tls, L"ServerContainer", common_name, _HTTP_MODE_SERVER, 10)) { DEBUG_puts("5http_sspi_server: Unable to get server credentials."); return (-1); } /* * Set OutBuffer for AcceptSecurityContext call */ outBuffer.cBuffers = 1; outBuffer.pBuffers = outBuffers; outBuffer.ulVersion = SECBUFFER_VERSION; scRet = SEC_I_CONTINUE_NEEDED; while (scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_E_INCOMPLETE_MESSAGE || scRet == SEC_I_INCOMPLETE_CREDENTIALS) { if (sspi->decryptBufferUsed == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE) { if (sspi->decryptBufferLength <= sspi->decryptBufferUsed) { BYTE *temp; /* New buffer */ if (sspi->decryptBufferLength >= 262144) { WSASetLastError(E_OUTOFMEMORY); DEBUG_puts("5http_sspi_server: Decryption buffer too large (>256k)"); return (-1); } if ((temp = realloc(sspi->decryptBuffer, sspi->decryptBufferLength + 4096)) == NULL) { DEBUG_printf(("5http_sspi_server: Unable to allocate %d byte buffer.", sspi->decryptBufferLength + 4096)); WSASetLastError(E_OUTOFMEMORY); return (-1); } sspi->decryptBufferLength += 4096; sspi->decryptBuffer = temp; } for (;;) { num = recv(http->fd, sspi->decryptBuffer + sspi->decryptBufferUsed, (int)(sspi->decryptBufferLength - sspi->decryptBufferUsed), 0); if (num == -1 && WSAGetLastError() == WSAEWOULDBLOCK) Sleep(1); else break; } if (num < 0) { DEBUG_printf(("5http_sspi_server: recv failed: %d", WSAGetLastError())); return (-1); } else if (num == 0) { DEBUG_puts("5http_sspi_server: client disconnected"); return (-1); } DEBUG_printf(("5http_sspi_server: received %d (handshake) bytes from client.", num)); sspi->decryptBufferUsed += num; } /* * InBuffers[1] is for getting extra data that SSPI/SCHANNEL doesn't process * on this run around the loop. */ inBuffers[0].pvBuffer = sspi->decryptBuffer; inBuffers[0].cbBuffer = (unsigned long)sspi->decryptBufferUsed; inBuffers[0].BufferType = SECBUFFER_TOKEN; inBuffers[1].pvBuffer = NULL; inBuffers[1].cbBuffer = 0; inBuffers[1].BufferType = SECBUFFER_EMPTY; inBuffer.cBuffers = 2; inBuffer.pBuffers = inBuffers; inBuffer.ulVersion = SECBUFFER_VERSION; /* * Initialize these so if we fail, pvBuffer contains NULL, so we don't try to * free random garbage at the quit. */ outBuffers[0].pvBuffer = NULL; outBuffers[0].BufferType = SECBUFFER_TOKEN; outBuffers[0].cbBuffer = 0; scRet = AcceptSecurityContext(&sspi->creds, (fInitContext?NULL:&sspi->context), &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, (fInitContext?&sspi->context:NULL), &outBuffer, &dwSSPIOutFlags, &tsExpiry); fInitContext = FALSE; if (scRet == SEC_E_OK || scRet == SEC_I_CONTINUE_NEEDED || (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0))) { if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) { /* * Send response to server if there is one. */ num = send(http->fd, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); if (num <= 0) { DEBUG_printf(("5http_sspi_server: handshake send failed: %d", WSAGetLastError())); return (-1); } DEBUG_printf(("5http_sspi_server: sent %d handshake bytes to client.", outBuffers[0].cbBuffer)); FreeContextBuffer(outBuffers[0].pvBuffer); outBuffers[0].pvBuffer = NULL; } } if (scRet == SEC_E_OK) { /* * If there's extra data then save it for next time we go to decrypt. */ if (inBuffers[1].BufferType == SECBUFFER_EXTRA) { memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer); sspi->decryptBufferUsed = inBuffers[1].cbBuffer; } else { sspi->decryptBufferUsed = 0; } break; } else if (FAILED(scRet) && scRet != SEC_E_INCOMPLETE_MESSAGE) { DEBUG_printf(("5http_sspi_server: AcceptSecurityContext failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); ret = -1; break; } if (scRet != SEC_E_INCOMPLETE_MESSAGE && scRet != SEC_I_INCOMPLETE_CREDENTIALS) { if (inBuffers[1].BufferType == SECBUFFER_EXTRA) { memcpy(sspi->decryptBuffer, (LPBYTE)(sspi->decryptBuffer + sspi->decryptBufferUsed - inBuffers[1].cbBuffer), inBuffers[1].cbBuffer); sspi->decryptBufferUsed = inBuffers[1].cbBuffer; } else { sspi->decryptBufferUsed = 0; } } } if (!ret) { sspi->contextInitialized = TRUE; /* * Find out how big the header will be: */ scRet = QueryContextAttributes(&sspi->context, SECPKG_ATTR_STREAM_SIZES, &sspi->streamSizes); if (scRet != SEC_E_OK) { DEBUG_printf(("5http_sspi_server: QueryContextAttributes failed: %s", http_sspi_strerror(sspi->error, sizeof(sspi->error), scRet))); ret = -1; } } return (ret); } /* * 'http_sspi_strerror()' - Return a string for the specified error code. */ static const char * /* O - String for error */ http_sspi_strerror(char *buffer, /* I - Error message buffer */ size_t bufsize, /* I - Size of buffer */ DWORD code) /* I - Error code */ { if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL)) { /* * Strip trailing CR + LF... */ char *ptr; /* Pointer into error message */ for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr --) if (*ptr == '\n' || *ptr == '\r') *ptr = '\0'; else break; } else snprintf(buffer, bufsize, "Unknown error %x", code); return (buffer); } /* * 'http_sspi_verify()' - Verify a certificate. */ static DWORD /* O - Error code (0 == No error) */ http_sspi_verify( PCCERT_CONTEXT cert, /* I - Server certificate */ const char *common_name, /* I - Common name */ DWORD dwCertFlags) /* I - Verification flags */ { HTTPSPolicyCallbackData httpsPolicy; /* HTTPS Policy Struct */ CERT_CHAIN_POLICY_PARA policyPara; /* Cert chain policy parameters */ CERT_CHAIN_POLICY_STATUS policyStatus;/* Cert chain policy status */ CERT_CHAIN_PARA chainPara; /* Used for searching and matching criteria */ PCCERT_CHAIN_CONTEXT chainContext = NULL; /* Certificate chain */ PWSTR commonNameUnicode = NULL; /* Unicode common name */ LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; /* How are we using this certificate? */ DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR); /* Number of ites in rgszUsages */ DWORD count; /* 32 bit count variable */ DWORD status; /* Return value */ #ifdef DEBUG char error[1024]; /* Error message string */ #endif /* DEBUG */ if (!cert) return (SEC_E_WRONG_PRINCIPAL); /* * Convert common name to Unicode. */ if (!common_name || !*common_name) return (SEC_E_WRONG_PRINCIPAL); count = MultiByteToWideChar(CP_ACP, 0, common_name, -1, NULL, 0); commonNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR)); if (!commonNameUnicode) return (SEC_E_INSUFFICIENT_MEMORY); if (!MultiByteToWideChar(CP_ACP, 0, common_name, -1, commonNameUnicode, count)) { LocalFree(commonNameUnicode); return (SEC_E_WRONG_PRINCIPAL); } /* * Build certificate chain. */ ZeroMemory(&chainPara, sizeof(chainPara)); chainPara.cbSize = sizeof(chainPara); chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages; chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; if (!CertGetCertificateChain(NULL, cert, NULL, cert->hCertStore, &chainPara, 0, NULL, &chainContext)) { status = GetLastError(); DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(error, sizeof(error), status))); LocalFree(commonNameUnicode); return (status); } /* * Validate certificate chain. */ ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData)); httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData); httpsPolicy.dwAuthType = AUTHTYPE_SERVER; httpsPolicy.fdwChecks = dwCertFlags; httpsPolicy.pwszServerName = commonNameUnicode; memset(&policyPara, 0, sizeof(policyPara)); policyPara.cbSize = sizeof(policyPara); policyPara.pvExtraPolicyPara = &httpsPolicy; memset(&policyStatus, 0, sizeof(policyStatus)); policyStatus.cbSize = sizeof(policyStatus); if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, &policyPara, &policyStatus)) { status = GetLastError(); DEBUG_printf(("CertVerifyCertificateChainPolicy returned %s", http_sspi_strerror(error, sizeof(error), status))); } else if (policyStatus.dwError) status = policyStatus.dwError; else status = SEC_E_OK; if (chainContext) CertFreeCertificateChain(chainContext); if (commonNameUnicode) LocalFree(commonNameUnicode); return (status); }