#ifdef HAVE_SECKEYCHAINOPEN
static CFArrayRef http_cdsa_copy_server(const char *common_name);
#endif /* HAVE_SECKEYCHAINOPEN */
+static SecCertificateRef http_cdsa_create_credential(http_credential_t *credential);
+static const char *http_cdsa_default_path(char *buffer, size_t bufsize);
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);
/*
* '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 */
cupsMakeServerCredentials(
- const char *path, /* I - Path to keychain/directory */
+ 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 */
{
#if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
+ char filename[1024]; /* Default keychain path */
int status = 0; /* Return status */
OSStatus err; /* Error code (if any) */
CFStringRef cfcommon_name = NULL;
/* Key generation parameters */
+ 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)num_alt_names;
(void)alt_names;
(void)expiration_date;
- cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name,
- kCFStringEncodingUTF8);
+ if (!path)
+ path = http_cdsa_default_path(filename, sizeof(filename));
+
+ cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
if (!cfcommon_name)
goto cleanup;
* Create a public/private key pair...
*/
- keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
+ keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!keyParams)
goto cleanup;
CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
- CFDictionaryAddValue(keyParams, kSecAttrLabel,
- CFSTR("CUPS Self-Signed Certificate"));
+ CFDictionaryAddValue(keyParams, kSecAttrLabel, CFSTR("CUPS Self-Signed Certificate"));
err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
if (err != noErr)
*envp[1000], /* Environment variables */
days[32], /* CERTTOOL_EXPIRATION_DAYS env var */
keychain[1024], /* Keychain argument */
- infofile[1024]; /* Type-in information for cert */
+ 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, 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...
*/
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))
{
* 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 X 10.10@
*/
int /* O - 1 on success, 0 on failure */
cupsSetServerCredentials(
- const char *path, /* I - Path to keychain/directory */
+ 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));
#ifdef HAVE_SECKEYCHAINOPEN
+ char filename[1024]; /* Filename for keychain */
SecKeychainRef keychain = NULL;/* Temporary keychain */
+ if (!path)
+ path = http_cdsa_default_path(filename, sizeof(filename));
+
if (SecKeychainOpen(path, &keychain) != noErr)
{
/* TODO: Set cups last error string */
}
-/*
- * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
- */
-
-static SecCertificateRef /* O - Certificate */
-http_cdsa_create_credential(
- http_credential_t *credential) /* I - Credential */
-{
- if (!credential)
- return (NULL);
-
- return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
-}
-
-
/*
* '_httpCreateCredentials()' - Create credentials in the internal format.
*/
/*
- * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
+ * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
+ *
+ * @since CUPS 2.0/OS X 10.10@
+ */
+
+int /* O - 1 if valid, 0 otherwise */
+httpCredentialsAreValidForName(
+ cups_array_t *credentials, /* I - Credentials */
+ const char *common_name) /* I - Name to check */
+{
+ SecCertificateRef secCert; /* Certificate reference */
+ CFStringRef cfcert_name = NULL;
+ /* Certificate's common name (CF string) */
+ char cert_name[256]; /* Certificate's common name (C string) */
+ int valid = 1; /* Valid name? */
+
+
+ if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
+ return (0);
+
+ /*
+ * Compare the common names...
+ */
+
+ if ((cfcert_name = SecCertificateCopySubjectSummary(secCert)) == NULL)
+ {
+ /*
+ * Can't get common name, cannot be valid...
+ */
+
+ valid = 0;
+ }
+ else if (CFStringGetCString(cfcert_name, cert_name, sizeof(cert_name), kCFStringEncodingUTF8) &&
+ _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;
+ }
+ }
+
+ if (cfcert_name)
+ CFRelease(cfcert_name);
+
+ CFRelease(secCert);
+
+ return (valid);
+}
+
+
+/*
+ * 'httpCredentialsGetTrust()' - Return the trust of credentials.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS X 10.10@
*/
-int /* O - 1 if trusted, 0 if not/unknown */
-httpCredentialsAreTrusted(
+http_trust_t /* O - Level of trust */
+httpCredentialsGetTrust(
cups_array_t *credentials, /* I - Credentials */
const char *common_name) /* I - Common name for trust lookup */
{
SecCertificateRef secCert; /* Certificate reference */
- int trusted = 1; /* Trusted? */
- int save = 0; /* Save credentials? */
+ http_trust_t trust = HTTP_TRUST_OK;
+ /* Trusted? */
cups_array_t *tcreds = NULL; /* Trusted credentials */
_cups_globals_t *cg = _cupsGlobals();
/* Per-thread globals */
if (!common_name)
- return (0);
+ return (HTTP_TRUST_UNKNOWN);
if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
- return (0);
+ return (HTTP_TRUST_UNKNOWN);
/*
* Look this common name up in the default keychains...
*/
if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds) ||
- !httpCredentialsIsValidName(credentials, common_name))
+ !httpCredentialsAreValidForName(credentials, common_name))
{
/*
* Either the new credentials are not newly issued, or the common name
* does not match the issued certificate...
*/
- trusted = 0;
+ trust = HTTP_TRUST_INVALID;
}
- else
+ else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
{
/*
- * Flag that we should save the new credentials...
+ * Save the renewed credentials...
*/
- save = 1;
+ trust = HTTP_TRUST_RENEWED;
+
+ httpSaveCredentials(NULL, credentials, common_name);
}
}
httpFreeCredentials(tcreds);
}
- else if (!httpCredentialsIsValidName(credentials, common_name))
- trusted = 0;
- else
- save = 1;
+ else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
+ trust = HTTP_TRUST_INVALID;
if (!cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
- trusted = 0;
+ trust = HTTP_TRUST_EXPIRED;
else if (!cg->any_root && cupsArrayCount(credentials) == 1)
- trusted = 0;
-
- if (trusted && save)
- httpSaveCredentials(NULL, credentials, common_name);
+ trust = HTTP_TRUST_INVALID;
CFRelease(secCert);
- return (trusted);
+ return (trust);
}
/*
* 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS X 10.10@
*/
time_t /* O - Expiration date of credentials */
}
-/*
- * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
- *
- * @since CUPS 2.0@
- */
-
-int /* O - 1 if valid, 0 otherwise */
-httpCredentialsIsValidName(
- cups_array_t *credentials, /* I - Credentials */
- const char *common_name) /* I - Name to check */
-{
- SecCertificateRef secCert; /* Certificate reference */
- CFStringRef cfcert_name = NULL;
- /* Certificate's common name (CF string) */
- char cert_name[256]; /* Certificate's common name (C string) */
- int valid = 1; /* Valid name? */
-
-
- if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
- return (0);
-
- /*
- * Compare the common names...
- */
-
- if ((cfcert_name = SecCertificateCopySubjectSummary(secCert)) == NULL)
- {
- /*
- * Can't get common name, cannot be valid...
- */
-
- valid = 0;
- }
- else if (CFStringGetCString(cfcert_name, cert_name, sizeof(cert_name), kCFStringEncodingUTF8) &&
- _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;
- }
- }
-
- if (cfcert_name)
- CFRelease(cfcert_name);
-
- CFRelease(secCert);
-
- return (valid);
-}
-
-
/*
* 'httpCredentialsString()' - Return a string representing the credentials.
*
- * @since CUPS 2.0@
+ * @since CUPS 2.0/OS X 10.10@
*/
size_t /* O - Total size of credentials string */
/*
* '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 */
httpLoadCredentials(
- const char *path, /* I - Keychain/PKCS#12 path */
+ 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 */
{
*credentials = NULL;
if (!path)
- {
- const char *home = getenv("HOME"); /* HOME environment variable */
-
- if (getuid() && home)
- snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
- else
- strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
-
- path = filename;
-
- DEBUG_printf(("1httpLoadCredentials: Using default path \"%s\".", path));
- }
-
+ path = http_cdsa_default_path(filename, sizeof(filename));
if ((err = SecKeychainOpen(path, &keychain)) != noErr)
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 */
httpSaveCredentials(
- const char *path, /* I - Keychain/PKCS#12 path */
+ 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 */
{
SecKeychainRef keychain = NULL;/* Keychain reference */
SecIdentitySearchRef search = NULL; /* Search reference */
SecCertificateRef cert = NULL; /* Certificate */
- CFStringRef cfcommon_name = NULL;
- /* Server name */
CFMutableDictionaryRef attrs = NULL; /* Attributes for add */
CFArrayRef list = NULL; /* Keychain list */
if (!credentials)
goto cleanup;
+ if (!httpCredentialsAreValidForName(credentials, common_name))
+ {
+ DEBUG_puts("1httpSaveCredentials: Common name does not match.");
+ return (-1);
+ }
+
if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
{
DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
}
if (!path)
- {
- const char *home = getenv("HOME"); /* HOME environment variable */
-
- if (getuid() && home)
- snprintf(filename, sizeof(filename), "%s/Library/Keychains/login.keychain", home);
- else
- strlcpy(filename, "/Library/Keychains/System.keychain", sizeof(filename));
-
- path = filename;
-
- DEBUG_printf(("1httpSaveCredentials: Using default path \"%s\".", path));
- }
+ path = http_cdsa_default_path(filename, sizeof(filename));
if ((err = SecKeychainOpen(path, &keychain)) != noErr)
{
goto cleanup;
}
- if ((cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8)) == NULL)
- {
- DEBUG_puts("1httpSaveCredentials: Unable to create common name string.");
- goto cleanup;
- }
-
if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
{
DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
}
CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
- CFDictionaryAddValue(attrs, kSecAttrLabel, cfcommon_name);
- CFDictionaryAddValue(attrs, kSecAttrSubject, cfcommon_name);
CFDictionaryAddValue(attrs, kSecValueRef, cert);
CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
/* Note: SecItemAdd consumes "attrs"... */
- if ((err = SecItemAdd(attrs, NULL)) == noErr)
- ret = 0;
-
+ err = SecItemAdd(attrs, NULL);
DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
cleanup :
- if (cfcommon_name)
- CFRelease(cfcommon_name);
if (list)
CFRelease(list);
if (keychain)
if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
goto cleanup;
+ _cupsMutexLock(&tls_mutex);
+
list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
+ _cupsMutexUnlock(&tls_mutex);
+
if (err)
goto cleanup;
#endif /* HAVE_SECKEYCHAINOPEN */
+/*
+ * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
+ */
+
+static SecCertificateRef /* O - Certificate */
+http_cdsa_create_credential(
+ http_credential_t *credential) /* I - Credential */
+{
+ if (!credential)
+ return (NULL);
+
+ return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
+}
+
+
+/*
+ * 'http_cdsa_default_path()' - Get the default keychain path.
+ */
+
+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 */
+
+
+ if (getuid() && home)
+ snprintf(buffer, bufsize, "%s/Library/Keychains/login.keychain", home);
+ else
+ strlcpy(buffer, "/Library/Keychains/System.keychain", bufsize);
+
+ DEBUG_printf(("1http_cdsa_default_path: Using default path \"%s\".", buffer));
+
+ return (buffer);
+}
+
+
/*
* 'http_cdsa_read()' - Read function for the CDSA library.
*/