#include "debug-private.h"
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Secur32.lib")
#pragma comment(lib, "Ws2_32.lib")
#ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA
# define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100
#endif
#ifndef SECURITY_FLAG_IGNORE_CERT_CN_INVALID
# define SECURITY_FLAG_IGNORE_CERT_CN_INVALID 0x00001000
#endif
#ifndef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
# define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000
#endif
static int tls_options = -1;
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);
int
cupsMakeServerCredentials(
const char *path,
const char *common_name,
int num_alt_names,
const char **alt_names,
time_t expiration_date)
{
_http_sspi_t *sspi;
int ret;
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);
}
int
cupsSetServerCredentials(
const char *path,
const char *common_name,
int auto_create)
{
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);
}
int
httpCopyCredentials(
http_t *http,
cups_array_t **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);
}
http_tls_credentials_t
_httpCreateCredentials(
cups_array_t *credentials)
{
return (http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials)));
}
int
httpCredentialsAreValidForName(
cups_array_t *credentials,
const char *common_name)
{
int valid = 1;
PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
char cert_name[1024];
if (cert)
{
if (CertNameToStr(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
{
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));
if (_cups_strcasecmp(common_name, cert_name))
{
const char *domain = strchr(common_name, '.');
if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
{
valid = 0;
}
}
return (valid);
}
http_trust_t
httpCredentialsGetTrust(
cups_array_t *credentials,
const char *common_name)
{
http_trust_t trust = HTTP_TRUST_OK;
PCCERT_CONTEXT cert = NULL;
DWORD certFlags = 0;
_cups_globals_t *cg = _cupsGlobals();
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);
}
time_t
httpCredentialsGetExpiration(
cups_array_t *credentials)
{
time_t expiration_date = 0;
PCCERT_CONTEXT cert = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
if (cert)
{
SYSTEMTIME systime;
struct tm tm;
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);
}
size_t
httpCredentialsString(
cups_array_t *credentials,
char *buffer,
size_t bufsize)
{
http_credential_t *first = (http_credential_t *)cupsArrayFirst(credentials);
PCCERT_CONTEXT cert;
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];
SYSTEMTIME systime;
struct tm tm;
time_t expiration;
_cups_md5_state_t md5_state;
unsigned char md5_digest[16];
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 (CertNameToStr(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
{
char *ptr = strrchr(cert_name, ',');
if (ptr && ptr[1])
_cups_strcpy(cert_name, ptr + 2);
}
else
strlcpy(cert_name, "unknown", sizeof(cert_name));
_cupsMD5Init(&md5_state);
_cupsMD5Append(&md5_state, first->data, (int)first->datalen);
_cupsMD5Finish(&md5_state, 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));
}
void
_httpFreeCredentials(
http_tls_credentials_t credentials)
{
if (!credentials)
return;
CertFreeCertificateContext(credentials);
}
int
httpLoadCredentials(
const char *path,
cups_array_t **credentials,
const char *common_name)
{
HCERTSTORE store = NULL;
PCCERT_CONTEXT storedContext = NULL;
DWORD dwSize = 0;
PBYTE p = NULL;
HCRYPTPROV hProv = (HCRYPTPROV)NULL;
CERT_NAME_BLOB sib;
#ifdef DEBUG
char error[1024];
#endif
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 (!CertStrToName(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 (!CertStrToName(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:
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);
}
int
httpSaveCredentials(
const char *path,
cups_array_t *credentials,
const char *common_name)
{
HCERTSTORE store = NULL;
PCCERT_CONTEXT storedContext = NULL;
PCCERT_CONTEXT createdContext = NULL;
DWORD dwSize = 0;
PBYTE p = NULL;
HCRYPTPROV hProv = (HCRYPTPROV)NULL;
CRYPT_KEY_PROV_INFO ckp;
int ret = -1;
#ifdef DEBUG
char error[1024];
#endif
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 (!CertStrToName(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 (!CertStrToName(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;
}
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:
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);
}
void
_httpTLSInitialize(void)
{
}
size_t
_httpTLSPending(http_t *http)
{
if (http->tls)
return (http->tls->readBufferUsed);
else
return (0);
}
int
_httpTLSRead(http_t *http,
char *buf,
int len)
{
int i;
_http_sspi_t *sspi = http->tls;
SecBufferDesc message;
SecBuffer buffers[4] = { 0 };
int num = 0;
PSecBuffer pDataBuffer;
PSecBuffer pExtraBuffer;
SECURITY_STATUS scRet;
DEBUG_printf(("4_httpTLSRead(http=%p, buf=%p, len=%d)", http, buf, len));
if (sspi->readBufferUsed > 0)
{
int bytesToCopy = min(sspi->readBufferUsed, len);
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);
}
message.ulVersion = SECBUFFER_VERSION;
message.cBuffers = 4;
message.pBuffers = buffers;
do
{
if (sspi->decryptBufferLength <= sspi->decryptBufferUsed)
{
BYTE *temp;
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);
}
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 (pDataBuffer)
{
int bytesToCopy = min((int)pDataBuffer->cbBuffer, len);
int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy;
if (bytesToCopy)
memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy);
if (bytesToSave)
{
if ((sspi->readBufferLength - sspi->readBufferUsed) < bytesToSave)
{
BYTE *temp;
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 (pExtraBuffer)
{
memmove(sspi->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
sspi->decryptBufferUsed = pExtraBuffer->cbBuffer;
}
else
{
sspi->decryptBufferUsed = 0;
}
return (num);
}
void
_httpTLSSetOptions(int options)
{
tls_options = options;
}
int
_httpTLSStart(http_t *http)
{
char hostname[256],
*hostptr;
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)
{
if (httpAddrLocalhost(http->hostaddr))
{
strlcpy(hostname, "localhost", sizeof(hostname));
}
else
{
strlcpy(hostname, http->hostname, sizeof(hostname));
if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
*hostptr == '.')
*hostptr = '\0';
}
return (http_sspi_client(http, hostname));
}
else
{
if (http->fields[HTTP_FIELD_HOST][0])
{
strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
}
else
{
http_addr_t addr;
socklen_t addrlen;
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));
}
}
void
_httpTLSStop(http_t *http)
{
_http_sspi_t *sspi = http->tls;
if (sspi->contextInitialized && http->fd >= 0)
{
SecBufferDesc message;
SecBuffer buffers[1] = { 0 };
DWORD dwType;
DWORD status;
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;
DWORD cbMessage;
DWORD cbData;
DWORD dwSSPIFlags;
DWORD dwSSPIOutFlags;
TimeStamp tsExpiry;
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;
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;
}
int
_httpTLSWrite(http_t *http,
const char *buf,
int len)
{
_http_sspi_t *sspi = http->tls;
SecBufferDesc message;
SecBuffer buffers[4] = { 0 };
int bufferLen;
int bytesLeft;
const char *bufptr;
int num = 0;
bufferLen = sspi->streamSizes.cbMaximumMessage + sspi->streamSizes.cbHeader + sspi->streamSizes.cbTrailer;
if (bufferLen > sspi->writeBufferLength)
{
BYTE *temp;
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);
SECURITY_STATUS scRet;
memcpy(sspi->writeBuffer + sspi->streamSizes.cbHeader, bufptr, chunk);
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;
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);
}
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
static int
http_setup_ssl(http_t *http)
{
char hostname[256],
*hostptr;
TCHAR username[256];
TCHAR commonName[256];
DWORD dwSize;
DEBUG_printf(("7http_setup_ssl(http=%p)", http));
if (httpAddrLocalhost(http->hostaddr))
{
strlcpy(hostname, "localhost", sizeof(hostname));
}
else
{
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
static _http_sspi_t *
http_sspi_alloc(void)
{
return ((_http_sspi_t *)calloc(sizeof(_http_sspi_t), 1));
}
static int
http_sspi_client(http_t *http,
const char *hostname)
{
_http_sspi_t *sspi = http->tls;
DWORD dwSize;
DWORD dwSSPIFlags;
DWORD dwSSPIOutFlags;
TimeStamp tsExpiry;
SECURITY_STATUS scRet;
int cbData;
SecBufferDesc inBuffer;
SecBuffer inBuffers[2];
SecBufferDesc outBuffer;
SecBuffer outBuffers[1];
int ret = 0;
char username[1024],
common_name[1024];
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;
dwSize = sizeof(username);
GetUserName(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);
}
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);
}
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;
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;
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;
}
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;
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, &sspi->context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);
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));
FreeContextBuffer(outBuffers[0].pvBuffer);
outBuffers[0].pvBuffer = NULL;
}
}
if (scRet == SEC_E_INCOMPLETE_MESSAGE)
continue;
if (scRet == SEC_E_OK)
{
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;
break;
}
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 (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
{
DEBUG_printf(("5http_sspi_client: server requested client credentials."));
ret = -1;
break;
}
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)
{
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);
}
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);
}
static PCCERT_CONTEXT
http_sspi_create_credential(
http_credential_t *cred)
{
if (cred)
return (CertCreateCertificateContext(X509_ASN_ENCODING, cred->data, cred->datalen));
else
return (NULL);
}
static BOOL
http_sspi_find_credentials(
http_t *http,
const LPWSTR container,
const char *common_name)
{
_http_sspi_t *sspi = http->tls;
HCERTSTORE store = NULL;
PCCERT_CONTEXT storedContext = NULL;
DWORD dwSize = 0;
PBYTE p = NULL;
HCRYPTPROV hProv = (HCRYPTPROV)NULL;
CERT_NAME_BLOB sib;
SCHANNEL_CRED SchannelCred;
TimeStamp tsExpiry;
SECURITY_STATUS Status;
BOOL ok = TRUE;
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 (!CertStrToName(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 (!CertStrToName(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;
#ifdef SP_PROT_TLS1_2_SERVER
if (http->mode == _HTTP_MODE_SERVER)
{
if (tls_options & _HTTP_TLS_DENY_TLS10)
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER;
else if (tls_options & _HTTP_TLS_ALLOW_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_options & _HTTP_TLS_DENY_TLS10)
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT;
else if (tls_options & _HTTP_TLS_ALLOW_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_options & _HTTP_TLS_ALLOW_SSL3)
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER;
else
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER;
}
else
{
if (tls_options & _HTTP_TLS_ALLOW_SSL3)
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT;
else
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
}
#endif
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:
if (storedContext)
CertFreeCertificateContext(storedContext);
if (p)
free(p);
if (store)
CertCloseStore(store, 0);
if (hProv)
CryptReleaseContext(hProv, 0);
return (ok);
}
static void
http_sspi_free(_http_sspi_t *sspi)
{
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);
}
static BOOL
http_sspi_make_credentials(
_http_sspi_t *sspi,
const LPWSTR container,
const char *common_name,
_http_mode_t mode,
int years)
{
HCERTSTORE store = NULL;
PCCERT_CONTEXT storedContext = NULL;
PCCERT_CONTEXT createdContext = NULL;
DWORD dwSize = 0;
PBYTE p = NULL;
HCRYPTPROV hProv = (HCRYPTPROV)NULL;
CERT_NAME_BLOB sib;
SCHANNEL_CRED SchannelCred;
TimeStamp tsExpiry;
SECURITY_STATUS Status;
HCRYPTKEY hKey = (HCRYPTKEY)NULL;
CRYPT_KEY_PROV_INFO kpi;
SYSTEMTIME et;
CERT_EXTENSIONS exts;
CRYPT_KEY_PROV_INFO ckp;
BOOL ok = TRUE;
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 (!CertStrToName(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 (!CertStrToName(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;
}
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;
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;
}
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;
}
ZeroMemory(&SchannelCred, sizeof(SchannelCred));
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
SchannelCred.cCreds = 1;
SchannelCred.paCred = &storedContext;
if (mode == _HTTP_MODE_SERVER)
SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
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:
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);
}
static int
http_sspi_server(http_t *http,
const char *hostname)
{
_http_sspi_t *sspi = http->tls;
char common_name[512];
DWORD dwSSPIFlags;
DWORD dwSSPIOutFlags;
TimeStamp tsExpiry;
SECURITY_STATUS scRet;
SecBufferDesc inBuffer;
SecBuffer inBuffers[2];
SecBufferDesc outBuffer;
SecBuffer outBuffers[1];
int num = 0;
BOOL fInitContext = TRUE;
int ret = 0;
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;
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);
}
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;
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[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;
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)
{
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 (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;
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);
}
static const char *
http_sspi_strerror(char *buffer,
size_t bufsize,
DWORD code)
{
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
{
char *ptr;
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);
}
static DWORD
http_sspi_verify(
PCCERT_CONTEXT cert,
const char *common_name,
DWORD dwCertFlags)
{
HTTPSPolicyCallbackData httpsPolicy;
CERT_CHAIN_POLICY_PARA policyPara;
CERT_CHAIN_POLICY_STATUS policyStatus;
CERT_CHAIN_PARA chainPara;
PCCERT_CHAIN_CONTEXT chainContext = NULL;
PWSTR commonNameUnicode = NULL;
LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
szOID_SERVER_GATED_CRYPTO,
szOID_SGC_NETSCAPE };
DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
DWORD count;
DWORD status;
#ifdef DEBUG
char error[1024];
#endif
if (!cert)
return (SEC_E_WRONG_PRINCIPAL);
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);
}
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);
}
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);
}