/*
- * "$Id$"
+ * TLS support for CUPS on Windows using the Security Support Provider
+ * Interface (SSPI).
*
- * TLS support for CUPS on Windows using SSPI.
+ * Copyright 2010-2018 by Apple Inc.
*
- * Copyright 2010-2014 by Apple Inc.
- *
- * These coded instructions, statements, and computer programs are the
- * property of Apple Inc. and are protected by Federal copyright
- * law. Distribution and use rights are outlined in the file "LICENSE.txt"
- * which should have been included with this file. If this file is
- * file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * This file is subject to the Apple OS-Developed Software exception.
+ * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
*/
+/**** This file is included from tls.c ****/
+
/*
* Include necessary headers...
*/
# 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...
*/
/*
* 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
int /* O - 1 on success, 0 on failure */
* Note: The server credentials are used by all threads in the running process.
* This function is threadsafe.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
int /* O - 1 on success, 0 on failure */
* 'httpCopyCredentials()' - Copy the credentials associated with the peer in
* an encrypted connection.
*
- * @since CUPS 1.5/OS X 10.7@
+ * @since CUPS 1.5/macOS 10.7@
*/
int /* O - Status of call (0 = success) */
/*
* 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
int /* O - 1 if valid, 0 otherwise */
if (cert)
{
- if (CertNameToStr(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
+ if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
{
/*
* Extract common name at end...
/*
* 'httpCredentialsGetTrust()' - Return the trust of credentials.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
http_trust_t /* O - Level of trust */
if (!cert)
return (HTTP_TRUST_UNKNOWN);
+ if (cg->any_root < 0)
+ _cupsSetDefaults();
+
if (cg->any_root)
certFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
/*
* 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
time_t /* O - Expiration date of credentials */
/*
* 'httpCredentialsString()' - Return a string representing the credentials.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
size_t /* O - Total size of credentials string */
SYSTEMTIME systime; /* System time */
struct tm tm; /* UNIX date/time */
time_t expiration; /* Expiration date of cert */
- _cups_md5_state_t md5_state; /* MD5 state */
unsigned char md5_digest[16]; /* MD5 result */
FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
expiration = mktime(&tm);
- if (CertNameToStr(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
+ if (CertNameToStrA(X509_ASN_ENCODING, &(cert->pCertInfo->Subject), CERT_SIMPLE_NAME_STR, cert_name, sizeof(cert_name)))
{
/*
* Extract common name at end...
else
strlcpy(cert_name, "unknown", sizeof(cert_name));
- _cupsMD5Init(&md5_state);
- _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
- _cupsMD5Finish(&md5_state, md5_digest);
+ 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]);
/*
* 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
int /* O - 0 on success, -1 on error */
dwSize = 0;
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+ 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;
goto cleanup;
}
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+ 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;
/*
* 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS 10.10@
*/
int /* O - -1 on error, 0 on success */
cups_array_t *credentials, /* I - Credentials */
const char *common_name) /* I - Common name for credentials */
{
- _http_sspi_t *sspi = http->tls; /* SSPI data */
HCERTSTORE store = NULL; /* Certificate store */
PCCERT_CONTEXT storedContext = NULL; /* Context created from the store */
PCCERT_CONTEXT createdContext = NULL; /* Context created by us */
PBYTE p = NULL; /* Temporary storage */
HCRYPTPROV hProv = (HCRYPTPROV)NULL;
/* Handle to a CSP */
- CERT_NAME_BLOB sib; /* Arbitrary array of bytes */
+ CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */
int ret = -1; /* Return value */
#ifdef DEBUG
char error[1024]; /* Error message buffer */
return (-1);
}
- createdContext = http_sspi_create_credential(credentials);
+ createdContext = http_sspi_create_credential((http_credential_t *)cupsArrayFirst(credentials));
if (!createdContext)
{
DEBUG_puts("1httpSaveCredentials: Bad credentials, returning -1.");
dwSize = 0;
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+ 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;
goto cleanup;
}
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+ 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;
}
+/*
+ * '_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.
*/
*hostptr; /* Pointer into hostname */
- DEBUG_printf(("7_httpTLSStart(http=%p)", http));
+ 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);
* Server: determine hostname to use...
*/
- if (http->fields[HTTP_FIELD_HOST][0])
+ if (http->fields[HTTP_FIELD_HOST])
{
/*
* Use hostname for TLS upgrade...
*/
dwSize = sizeof(username);
- GetUserName(username, &dwSize);
+ GetUserNameA(username, &dwSize);
snprintf(common_name, sizeof(common_name), "CN=%s", username);
if (!http_sspi_find_credentials(http, L"ClientContainer", common_name))
dwSize = 0;
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+ 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;
}
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+ 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;
SchannelCred.paCred = &storedContext;
/*
- * SSPI doesn't seem to like it if grbitEnabledProtocols is set for a client.
+ * Set supported protocols (can also be overriden in the registry...)
*/
+#ifdef SP_PROT_TLS1_2_SERVER
if (http->mode == _HTTP_MODE_SERVER)
- SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1;
+ {
+ 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.
dwSize = 0;
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, NULL, &dwSize, NULL))
+ 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;
}
- if (!CertStrToName(X509_ASN_ENCODING, common_name, CERT_OID_NAME_STR, NULL, p, &dwSize, NULL))
+ 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;
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));
size_t bufsize, /* I - Size of buffer */
DWORD code) /* I - Error code */
{
- if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
+ if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, bufsize, NULL))
{
/*
* Strip trailing CR + LF...
/* 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)
{
status = GetLastError();
-#ifdef DEBUG
- char error[1024]; /* Error message string */
-
DEBUG_printf(("CertGetCertificateChain returned: %s", http_sspi_strerror(error, sizeof(error), status)));
-#endif /* DEBUG */
LocalFree(commonNameUnicode);
return (status);
{
status = GetLastError();
-#ifdef DEBUG
- char error[1024]; /* Error message string */
-
DEBUG_printf(("CertVerifyCertificateChainPolicy returned %s", http_sspi_strerror(error, sizeof(error), status)));
-#endif /* DEBUG */
}
else if (policyStatus.dwError)
status = policyStatus.dwError;
return (status);
}
-
-
-/*
- * End of "$Id$".
- */