X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=cups%2Ftls-darwin.c;h=b3bd50bf819e1d7e5a0422ae543371fce87ec8cb;hb=HEAD;hp=30b1e86de843cb9a08dced62995bf720c8aacb31;hpb=9a4e273486f56952c18aeb624f7b5800aaf5c991;p=thirdparty%2Fcups.git diff --git a/cups/tls-darwin.c b/cups/tls-darwin.c index 30b1e86de..0741a320e 100644 --- a/cups/tls-darwin.c +++ b/cups/tls-darwin.c @@ -1,16 +1,12 @@ /* * TLS support code for CUPS on macOS. * - * Copyright 2007-2016 by Apple Inc. - * Copyright 1997-2007 by Easy Software Products, all rights reserved. + * Copyright © 2021 by OpenPrinting + * Copyright © 2007-2021 by Apple Inc. + * Copyright © 1997-2007 by Easy Software Products, all rights reserved. * - * 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 - * 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 ****/ @@ -20,9 +16,7 @@ */ #include - -extern char **environ; /* @private@ */ - +#include "tls-darwin.h" /* * Constants, very secure stuff... @@ -40,7 +34,7 @@ static int tls_auto_create = 0; /* Auto-create self-signed certs? */ static char *tls_common_name = NULL; /* Default common name */ -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX static int tls_cups_keychain = 0; /* Opened the CUPS keychain? */ static SecKeychainRef tls_keychain = NULL; @@ -48,12 +42,14 @@ static SecKeychainRef tls_keychain = NULL; #else static SecIdentityRef tls_selfsigned = NULL; /* Temporary self-signed cert */ -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ static char *tls_keypath = NULL; /* Server cert keychain path */ static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER; /* Mutex for keychain/certs */ -static int tls_options = -1;/* Options for TLS connections */ +static int tls_options = -1,/* Options for TLS connections */ + tls_min_version = _HTTP_TLS_1_0, + tls_max_version = _HTTP_TLS_MAX; /* @@ -62,11 +58,11 @@ static int tls_options = -1;/* Options for TLS connections */ static CFArrayRef http_cdsa_copy_server(const char *common_name); static SecCertificateRef http_cdsa_create_credential(http_credential_t *credential); -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX static const char *http_cdsa_default_path(char *buffer, size_t bufsize); static SecKeychainRef http_cdsa_open_keychain(const char *path, char *filename, size_t filesize); static SecKeychainRef http_cdsa_open_system_keychain(void); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength); static int http_cdsa_set_credentials(http_t *http); static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength); @@ -86,7 +82,107 @@ cupsMakeServerCredentials( const char **alt_names, /* I - Subject Alternate Names */ time_t expiration_date) /* I - Expiration date */ { -#if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) +#if TARGET_OS_OSX + int pid, /* Process ID of command */ + status, /* Status of command */ + i; /* Looping var */ + char command[1024], /* Command */ + *argv[5], /* Command-line arguments */ + *envp[1000], /* Environment variables */ + days[32], /* CERTTOOL_EXPIRATION_DAYS env var */ + keychain[1024], /* Keychain argument */ + infofile[1024], /* Type-in information for cert */ + filename[1024]; /* Default keychain path */ + cups_file_t *fp; /* Seed/info file */ + + + DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, (void *)alt_names, (int)expiration_date)); + + (void)num_alt_names; + (void)alt_names; + + if (!path) + path = http_cdsa_default_path(filename, sizeof(filename)); + + /* + * Run the "certtool" command to generate a self-signed certificate... + */ + + if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command))) + return (-1); + + /* + * Create a file with the certificate information fields... + * + * Note: This assumes that the default questions are asked by the certtool + * command... + */ + + if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) + return (-1); + + cupsFilePrintf(fp, + "CUPS Self-Signed Certificate\n" + /* Enter key and certificate label */ + "r\n" /* Generate RSA key pair */ + "2048\n" /* 2048 bit encryption key */ + "y\n" /* OK (y = yes) */ + "b\n" /* Usage (b=signing/encryption) */ + "2\n" /* Sign with SHA256 */ + "y\n" /* OK (y = yes) */ + "%s\n" /* Common name */ + "\n" /* Country (default) */ + "\n" /* Organization (default) */ + "\n" /* Organizational unit (default) */ + "\n" /* State/Province (default) */ + "\n" /* Email address */ + "y\n", /* OK (y = yes) */ + common_name); + cupsFileClose(fp); + + snprintf(keychain, sizeof(keychain), "k=%s", path); + + argv[0] = "certtool"; + argv[1] = "c"; + argv[2] = keychain; + argv[3] = NULL; + + snprintf(days, sizeof(days), "CERTTOOL_EXPIRATION_DAYS=%d", (int)((expiration_date - time(NULL) + 86399) / 86400)); + envp[0] = days; + for (i = 0; i < (int)(sizeof(envp) / sizeof(envp[0]) - 2) && environ[i]; i ++) + envp[i + 1] = environ[i]; + envp[i] = NULL; + + posix_spawn_file_actions_t actions; /* File actions */ + + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addclose(&actions, 0); + posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0); + posix_spawn_file_actions_addclose(&actions, 1); + posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0); + posix_spawn_file_actions_addclose(&actions, 2); + posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0); + + if (posix_spawn(&pid, command, &actions, NULL, argv, envp)) + { + unlink(infofile); + return (-1); + } + + posix_spawn_file_actions_destroy(&actions); + + unlink(infofile); + + while (waitpid(pid, &status, 0) < 0) + if (errno != EINTR) + { + status = -1; + break; + } + + return (!status); + +#else int status = 0; /* Return status */ OSStatus err; /* Error code (if any) */ CFStringRef cfcommon_name = NULL; @@ -196,7 +292,7 @@ cupsMakeServerCredentials( else tls_selfsigned = ident; - _cupsMutexLock(&tls_mutex); + _cupsMutexUnlock(&tls_mutex); # if 0 /* Someday perhaps SecItemCopyMatching will work for identities, at which point */ CFTypeRef itemKeys[] = { kSecClass, kSecAttrLabel, kSecValueRef }; @@ -244,107 +340,7 @@ cleanup: DEBUG_printf(("1cupsMakeServerCredentials: Returning %d.", status)); return (status); - -#else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */ - int pid, /* Process ID of command */ - status, /* Status of command */ - i; /* Looping var */ - char command[1024], /* Command */ - *argv[5], /* Command-line arguments */ - *envp[1000], /* Environment variables */ - days[32], /* CERTTOOL_EXPIRATION_DAYS env var */ - keychain[1024], /* Keychain argument */ - infofile[1024], /* Type-in information for cert */ - filename[1024]; /* Default keychain path */ - cups_file_t *fp; /* Seed/info file */ - - - DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, (void *)alt_names, (int)expiration_date)); - - (void)num_alt_names; - (void)alt_names; - - if (!path) - path = http_cdsa_default_path(filename, sizeof(filename)); - - /* - * Run the "certtool" command to generate a self-signed certificate... - */ - - if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command))) - return (-1); - - /* - * Create a file with the certificate information fields... - * - * Note: This assumes that the default questions are asked by the certtool - * command... - */ - - if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) - return (-1); - - cupsFilePrintf(fp, - "CUPS Self-Signed Certificate\n" - /* Enter key and certificate label */ - "r\n" /* Generate RSA key pair */ - "2048\n" /* 2048 bit encryption key */ - "y\n" /* OK (y = yes) */ - "b\n" /* Usage (b=signing/encryption) */ - "2\n" /* Sign with SHA256 */ - "y\n" /* OK (y = yes) */ - "%s\n" /* Common name */ - "\n" /* Country (default) */ - "\n" /* Organization (default) */ - "\n" /* Organizational unit (default) */ - "\n" /* State/Province (default) */ - "\n" /* Email address */ - "y\n", /* OK (y = yes) */ - common_name); - cupsFileClose(fp); - - snprintf(keychain, sizeof(keychain), "k=%s", path); - - argv[0] = "certtool"; - argv[1] = "c"; - argv[2] = keychain; - argv[3] = NULL; - - snprintf(days, sizeof(days), "CERTTOOL_EXPIRATION_DAYS=%d", (int)((expiration_date - time(NULL) + 86399) / 86400)); - envp[0] = days; - for (i = 0; i < (int)(sizeof(envp) / sizeof(envp[0]) - 2) && environ[i]; i ++) - envp[i + 1] = environ[i]; - envp[i] = NULL; - - posix_spawn_file_actions_t actions; /* File actions */ - - posix_spawn_file_actions_init(&actions); - posix_spawn_file_actions_addclose(&actions, 0); - posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0); - posix_spawn_file_actions_addclose(&actions, 1); - posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0); - posix_spawn_file_actions_addclose(&actions, 2); - posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0); - - if (posix_spawn(&pid, command, &actions, NULL, argv, envp)) - { - unlink(infofile); - return (-1); - } - - posix_spawn_file_actions_destroy(&actions); - - unlink(infofile); - - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - { - status = -1; - break; - } - - return (!status); -#endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ } @@ -365,7 +361,7 @@ cupsSetServerCredentials( { DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create)); -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX char filename[1024]; /* Keychain filename */ SecKeychainRef keychain = http_cdsa_open_keychain(path, filename, sizeof(filename)); @@ -415,7 +411,7 @@ cupsSetServerCredentials( tls_common_name = _cupsStrAlloc(common_name); return (1); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ } @@ -804,27 +800,89 @@ httpCredentialsString( if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL && (secCert = http_cdsa_create_credential(first)) != NULL) { - CFStringRef cf_name; /* CF common name string */ - char name[256]; /* Common name associated with cert */ + /* + * Copy certificate (string) values from the SecCertificateRef and produce + * a one-line summary. The API for accessing certificate values like the + * issuer name is, um, "interesting"... + */ + +# if TARGET_OS_OSX + CFDictionaryRef cf_dict; /* Dictionary for certificate */ +# endif /* TARGET_OS_OSX */ + CFStringRef cf_string; /* CF string */ + char commonName[256],/* Common name associated with cert */ + issuer[256], /* Issuer name */ + sigalg[256]; /* Signature algorithm */ time_t expiration; /* Expiration date of cert */ - _cups_md5_state_t md5_state; /* MD5 state */ unsigned char md5_digest[16]; /* MD5 result */ - if ((cf_name = SecCertificateCopySubjectSummary(secCert)) != NULL) + if (SecCertificateCopyCommonName(secCert, &cf_string) == noErr) { - CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8); - CFRelease(cf_name); + CFStringGetCString(cf_string, commonName, (CFIndex)sizeof(commonName), kCFStringEncodingUTF8); + CFRelease(cf_string); } else - strlcpy(name, "unknown", sizeof(name)); + { + strlcpy(commonName, "unknown", sizeof(commonName)); + } + + strlcpy(issuer, "unknown", sizeof(issuer)); + strlcpy(sigalg, "UnknownSignature", sizeof(sigalg)); + +# if TARGET_OS_OSX + if ((cf_dict = SecCertificateCopyValues(secCert, NULL, NULL)) != NULL) + { + CFDictionaryRef cf_issuer = CFDictionaryGetValue(cf_dict, kSecOIDX509V1IssuerName); + CFDictionaryRef cf_sigalg = CFDictionaryGetValue(cf_dict, kSecOIDX509V1SignatureAlgorithm); + + if (cf_issuer) + { + CFArrayRef cf_values = CFDictionaryGetValue(cf_issuer, kSecPropertyKeyValue); + CFIndex i, count = CFArrayGetCount(cf_values); + CFDictionaryRef cf_value; + + for (i = 0; i < count; i ++) + { + cf_value = CFArrayGetValueAtIndex(cf_values, i); + + if (!CFStringCompare(CFDictionaryGetValue(cf_value, kSecPropertyKeyLabel), kSecOIDOrganizationName, kCFCompareCaseInsensitive)) + CFStringGetCString(CFDictionaryGetValue(cf_value, kSecPropertyKeyValue), issuer, (CFIndex)sizeof(issuer), kCFStringEncodingUTF8); + } + } + + if (cf_sigalg) + { + CFArrayRef cf_values = CFDictionaryGetValue(cf_sigalg, kSecPropertyKeyValue); + CFIndex i, count = CFArrayGetCount(cf_values); + CFDictionaryRef cf_value; + + for (i = 0; i < count; i ++) + { + cf_value = CFArrayGetValueAtIndex(cf_values, i); + + if (!CFStringCompare(CFDictionaryGetValue(cf_value, kSecPropertyKeyLabel), CFSTR("Algorithm"), kCFCompareCaseInsensitive)) + { + CFStringRef cf_algorithm = CFDictionaryGetValue(cf_value, kSecPropertyKeyValue); + + if (!CFStringCompare(cf_algorithm, CFSTR("1.2.840.113549.1.1.5"), kCFCompareCaseInsensitive)) + strlcpy(sigalg, "SHA1WithRSAEncryption", sizeof(sigalg)); + else if (!CFStringCompare(cf_algorithm, CFSTR("1.2.840.113549.1.1.11"), kCFCompareCaseInsensitive)) + strlcpy(sigalg, "SHA256WithRSAEncryption", sizeof(sigalg)); + else if (!CFStringCompare(cf_algorithm, CFSTR("1.2.840.113549.1.1.4"), kCFCompareCaseInsensitive)) + strlcpy(sigalg, "MD5WithRSAEncryption", sizeof(sigalg)); + } + } + } + + CFRelease(cf_dict); + } +# endif /* TARGET_OS_OSX */ expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970); - _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", 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]); + snprintf(buffer, bufsize, "%s (issued by %s) / %s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", commonName, issuer, httpGetDateString(expiration), sigalg, 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]); CFRelease(secCert); } @@ -863,12 +921,12 @@ httpLoadCredentials( const char *common_name) /* I - Common name for credentials */ { OSStatus err; /* Error info */ -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX char filename[1024]; /* Filename for keychain */ SecKeychainRef keychain = NULL,/* Keychain reference */ syschain = NULL;/* System keychain */ CFArrayRef list; /* Keychain list */ -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ SecCertificateRef cert = NULL; /* Certificate */ CFDataRef data; /* Certificate data */ SecPolicyRef policy = NULL; /* Policy ref */ @@ -884,7 +942,7 @@ httpLoadCredentials( *credentials = NULL; -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX keychain = http_cdsa_open_keychain(path, filename, sizeof(filename)); if (!keychain) @@ -895,7 +953,7 @@ httpLoadCredentials( #else if (path) return (-1); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8); @@ -915,7 +973,7 @@ httpLoadCredentials( CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne); -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX if (syschain) { const void *values[2] = { syschain, keychain }; @@ -926,7 +984,7 @@ httpLoadCredentials( list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks); CFDictionaryAddValue(query, kSecMatchSearchList, list); CFRelease(list); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ err = SecItemCopyMatching(query, (CFTypeRef *)&cert); @@ -947,13 +1005,13 @@ httpLoadCredentials( cleanup : -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX if (keychain) CFRelease(keychain); if (syschain) CFRelease(syschain); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ if (cert) CFRelease(cert); if (policy) @@ -981,11 +1039,11 @@ httpSaveCredentials( { int ret = -1; /* Return value */ OSStatus err; /* Error info */ -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX char filename[1024]; /* Filename for keychain */ SecKeychainRef keychain = NULL;/* Keychain reference */ CFArrayRef list; /* Keychain list */ -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ SecCertificateRef cert = NULL; /* Certificate */ CFMutableDictionaryRef attrs = NULL; /* Attributes for add */ @@ -1006,7 +1064,7 @@ httpSaveCredentials( goto cleanup; } -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX keychain = http_cdsa_open_keychain(path, filename, sizeof(filename)); if (!keychain) @@ -1015,7 +1073,7 @@ httpSaveCredentials( #else if (path) return (-1); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL) { @@ -1026,7 +1084,7 @@ httpSaveCredentials( CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate); CFDictionaryAddValue(attrs, kSecValueRef, cert); -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL) { DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains."); @@ -1034,7 +1092,7 @@ httpSaveCredentials( } CFDictionaryAddValue(attrs, kSecMatchSearchList, list); CFRelease(list); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ /* Note: SecItemAdd consumes "attrs"... */ err = SecItemAdd(attrs, NULL); @@ -1042,10 +1100,10 @@ httpSaveCredentials( cleanup : -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX if (keychain) CFRelease(keychain); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ if (cert) CFRelease(cert); @@ -1139,9 +1197,16 @@ _httpTLSRead(http_t *http, /* I - HTTP connection */ */ void -_httpTLSSetOptions(int options) /* I - Options */ +_httpTLSSetOptions(int options, /* I - Options */ + int min_version, /* I - Minimum TLS version */ + int max_version) /* I - Maximum TLS version */ { - tls_options = options; + if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0) + { + tls_options = options; + tls_min_version = min_version; + tls_max_version = max_version; + } } @@ -1158,6 +1223,7 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ /* Pointer to library globals */ OSStatus error; /* Error code */ const char *message = NULL;/* Error message */ + char msgbuf[1024]; /* Error message buffer */ cups_array_t *credentials; /* Credentials array */ cups_array_t *names; /* CUPS distinguished names */ CFArrayRef dn_array; /* CF distinguished names array */ @@ -1173,10 +1239,10 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ { DEBUG_puts("4_httpTLSStart: Setting defaults."); _cupsSetDefaults(); - DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options)); + DEBUG_printf(("4_httpTLSStart: tls_options=%x, tls_min_version=%d, tls_max_version=%d", tls_options, tls_min_version, tls_max_version)); } -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX if (http->mode == _HTTP_MODE_SERVER && !tls_keychain) { DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called."); @@ -1186,7 +1252,7 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ return (-1); } -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL) { @@ -1216,20 +1282,28 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ if (!error) { - SSLProtocol minProtocol; - - if (tls_options & _HTTP_TLS_DENY_TLS10) - minProtocol = kTLSProtocol11; - else if (tls_options & _HTTP_TLS_ALLOW_SSL3) - minProtocol = kSSLProtocol3; - else - minProtocol = kTLSProtocol1; + static const SSLProtocol protocols[] = /* Min/max protocol versions */ + { + kSSLProtocol3, + kTLSProtocol1, + kTLSProtocol11, + kTLSProtocol12, + kTLSProtocol13 + }; + + if (tls_min_version < _HTTP_TLS_MAX) + { + error = SSLSetProtocolVersionMin(http->tls, protocols[tls_min_version]); + DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", protocols[tls_min_version], (int)error)); + } - error = SSLSetProtocolVersionMin(http->tls, minProtocol); - DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", minProtocol, (int)error)); + if (!error && tls_max_version < _HTTP_TLS_MAX) + { + error = SSLSetProtocolVersionMax(http->tls, protocols[tls_max_version]); + DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMax(%d), error=%d", protocols[tls_max_version], (int)error)); + } } -# if HAVE_SSLSETENABLEDCIPHERS if (!error) { SSLCipherSuite supported[100]; /* Supported cipher suites */ @@ -1324,7 +1398,6 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA : case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA : case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA : -// case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA : case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA : case TLS_DH_DSS_WITH_AES_128_CBC_SHA256 : case TLS_DH_RSA_WITH_AES_128_CBC_SHA256 : @@ -1337,6 +1410,14 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA : case TLS_DHE_PSK_WITH_AES_128_CBC_SHA : case TLS_DHE_PSK_WITH_AES_256_CBC_SHA : + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 : + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 : + if (tls_options & _HTTP_TLS_DENY_CBC) + { + DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i])); + break; + } + // case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 : // case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 : case TLS_DH_RSA_WITH_AES_128_GCM_SHA256 : @@ -1347,15 +1428,31 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ case TLS_DH_DSS_WITH_AES_256_GCM_SHA384 : case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 : case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 : - case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 : - case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 : if (tls_options & _HTTP_TLS_ALLOW_DH) enabled[num_enabled ++] = supported[i]; else DEBUG_printf(("4_httpTLSStart: Excluding DH/DHE cipher suite %d", supported[i])); break; - /* Anything else we'll assume is secure */ + case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA : + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 : + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 : + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 : + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 : + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 : + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 : + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 : + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 : + case TLS_RSA_WITH_3DES_EDE_CBC_SHA : + case TLS_RSA_WITH_AES_128_CBC_SHA : + case TLS_RSA_WITH_AES_256_CBC_SHA : + if (tls_options & _HTTP_TLS_DENY_CBC) + { + DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i])); + break; + } + + /* Anything else we'll assume is "secure" */ default : enabled[num_enabled ++] = supported[i]; break; @@ -1366,7 +1463,6 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ error = SSLSetEnabledCiphers(http->tls, enabled, num_enabled); } } -#endif /* HAVE_SSLSETENABLEDCIPHERS */ if (!error && http->mode == _HTTP_MODE_CLIENT) { @@ -1394,7 +1490,7 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ * Server: find/create a certificate for TLS... */ - if (http->fields[HTTP_FIELD_HOST][0]) + if (http->fields[HTTP_FIELD_HOST]) { /* * Use hostname for TLS upgrade... @@ -1502,7 +1598,28 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ if (!error) { - int done = 0; /* Are we done yet? */ + int done = 0; /* Are we done yet? */ + double old_timeout; /* Old timeout value */ + http_timeout_cb_t old_cb; /* Old timeout callback */ + void *old_data; /* Old timeout data */ + + /* + * Enforce a minimum timeout of 10 seconds for the TLS handshake... + */ + + old_timeout = http->timeout_value; + old_cb = http->timeout_cb; + old_data = http->timeout_data; + + if (!old_cb || old_timeout < 10.0) + { + DEBUG_puts("4_httpTLSStart: Setting timeout to 10 seconds."); + httpSetTimeout(http, 10.0, NULL, NULL); + } + + /* + * Do the TLS handshake... + */ while (!error && !done) { @@ -1623,6 +1740,12 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ break; } } + + /* + * Restore the previous timeout settings... + */ + + httpSetTimeout(http, old_timeout, old_cb, old_data); } if (error) @@ -1639,11 +1762,13 @@ _httpTLSStart(http_t *http) /* I - HTTP connection */ */ if (!message) -#ifdef HAVE_CSSMERRORSTRING - message = cssmErrorString(error); -#else - message = _("Unable to establish a secure connection to host."); -#endif /* HAVE_CSSMERRORSTRING */ + { + if (!cg->lang_default) + cg->lang_default = cupsLangDefault(); + + snprintf(msgbuf, sizeof(msgbuf), _cupsLangString(cg->lang_default, _("Unable to establish a secure connection to host (%d).")), error); + message = msgbuf; + } _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1); @@ -1738,7 +1863,7 @@ static CFArrayRef /* O - Array of certificates or NULL */ http_cdsa_copy_server( const char *common_name) /* I - Server's hostname */ { -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX OSStatus err; /* Error info */ SecIdentityRef identity = NULL;/* Identity */ CFArrayRef certificates = NULL; @@ -1836,11 +1961,13 @@ http_cdsa_copy_server( return (certificates); #else + (void)common_name; + if (!tls_selfsigned) return (NULL); return (CFArrayCreate(NULL, (const void **)&tls_selfsigned, 1, &kCFTypeArrayCallBacks)); -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ } @@ -1852,14 +1979,22 @@ static SecCertificateRef /* O - Certificate */ http_cdsa_create_credential( http_credential_t *credential) /* I - Credential */ { + SecCertificateRef cert; /* Certificate */ + CFDataRef data; /* Data object */ + + if (!credential) return (NULL); - return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen)); + data = CFDataCreate(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen); + cert = SecCertificateCreateWithData(kCFAllocatorDefault, data); + CFRelease(data); + + return (cert); } -#ifdef HAVE_SECKEYCHAINOPEN +#if TARGET_OS_OSX /* * 'http_cdsa_default_path()' - Get the default keychain path. */ @@ -1868,7 +2003,8 @@ static const char * /* O - Keychain path */ http_cdsa_default_path(char *buffer, /* I - Path buffer */ size_t bufsize) /* I - Size of buffer */ { - const char *home = getenv("HOME"); /* HOME environment variable */ + _cups_globals_t *cg = _cupsGlobals(); + /* Pointer to library globals */ /* @@ -1877,8 +2013,8 @@ http_cdsa_default_path(char *buffer, /* I - Path buffer */ * 10.11.4 (!), so we need to create our own keychain just for CUPS. */ - if (getuid() && home) - snprintf(buffer, bufsize, "%s/.cups/ssl.keychain", home); + if (cg->home) + snprintf(buffer, bufsize, "%s/.cups/ssl.keychain", cg->home); else strlcpy(buffer, "/etc/cups/ssl.keychain", bufsize); @@ -2035,7 +2171,7 @@ http_cdsa_open_system_keychain(void) return (keychain); } -#endif /* HAVE_SECKEYCHAINOPEN */ +#endif /* TARGET_OS_OSX */ /* @@ -2055,7 +2191,7 @@ http_cdsa_read( http = (http_t *)connection; - if (!http->blocking) + if (!http->blocking || http->timeout_value > 0.0) { /* * Make sure we have data before we read...