]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/tls-darwin.c
Update "since CUPS 2.0" into to mention OS X 10.10.
[thirdparty/cups.git] / cups / tls-darwin.c
index 1a9bfefd2b6896f4f34a43d2f9776a7e7d3f9728..fefd6d104cf46e075160b512f76796479c7d5f08 100644 (file)
@@ -51,6 +51,8 @@ static _cups_mutex_t  tls_mutex = _CUPS_MUTEX_INITIALIZER;
 #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);
@@ -59,18 +61,19 @@ static OSStatus             http_cdsa_write(SSLConnectionRef connection, const void *data,
 /*
  * '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;
@@ -84,12 +87,16 @@ cupsMakeServerCredentials(
                                        /* 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;
 
@@ -97,16 +104,13 @@ cupsMakeServerCredentials(
   * 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)
@@ -179,13 +183,19 @@ cleanup:
                *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...
   */
@@ -240,6 +250,10 @@ cleanup:
   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))
   {
@@ -269,21 +283,25 @@ cleanup:
  * 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 */
@@ -395,21 +413,6 @@ httpCopyCredentials(
 }
 
 
-/*
- * '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.
  */
@@ -447,29 +450,92 @@ _httpCreateCredentials(
 
 
 /*
- * '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...
@@ -493,50 +559,47 @@ httpCredentialsAreTrusted(
       */
 
       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 */
@@ -558,73 +621,10 @@ httpCredentialsGetExpiration(
 }
 
 
-/*
- * '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 */
@@ -697,12 +697,12 @@ _httpFreeCredentials(
 /*
  * '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 */
 {
@@ -728,19 +728,7 @@ httpLoadCredentials(
   *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;
@@ -815,12 +803,12 @@ httpLoadCredentials(
 /*
  * '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 */
 {
@@ -831,8 +819,6 @@ httpSaveCredentials(
   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 */
 
@@ -841,6 +827,12 @@ httpSaveCredentials(
   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.");
@@ -848,18 +840,7 @@ httpSaveCredentials(
   }
 
   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)
   {
@@ -873,12 +854,6 @@ httpSaveCredentials(
     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.");
@@ -886,21 +861,15 @@ httpSaveCredentials(
   }
 
   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)
@@ -1466,6 +1435,8 @@ http_cdsa_copy_server(
   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);
@@ -1478,6 +1449,8 @@ http_cdsa_copy_server(
 
   err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
 
+  _cupsMutexUnlock(&tls_mutex);
+
   if (err)
     goto cleanup;
 
@@ -1504,6 +1477,43 @@ http_cdsa_copy_server(
 #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.
  */