Imported Upstream version 2.2.2
[platform/upstream/cups.git] / cups / tls-darwin.c
1 /*
2  * TLS support code for CUPS on macOS.
3  *
4  * Copyright 2007-2016 by Apple Inc.
5  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6  *
7  * These coded instructions, statements, and computer programs are the
8  * property of Apple Inc. and are protected by Federal copyright
9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10  * which should have been included with this file.  If this file is
11  * missing or damaged, see the license at "http://www.cups.org/".
12  *
13  * This file is subject to the Apple OS-Developed Software exception.
14  */
15
16 /**** This file is included from tls.c ****/
17
18 /*
19  * Include necessary headers...
20  */
21
22 #include <spawn.h>
23
24 extern char **environ;
25
26
27 /*
28  * Constants, very secure stuff...
29  */
30
31 #define _CUPS_CDSA_PASSWORD     "42"    /* CUPS keychain password */
32 #define _CUPS_CDSA_PASSLEN      2       /* Length of keychain password */
33
34
35 /*
36  * Local globals...
37  */
38
39 static int              tls_auto_create = 0;
40                                         /* Auto-create self-signed certs? */
41 static char             *tls_common_name = NULL;
42                                         /* Default common name */
43 #ifdef HAVE_SECKEYCHAINOPEN
44 static SecKeychainRef   tls_keychain = NULL;
45                                         /* Server cert keychain */
46 #else
47 static SecIdentityRef   tls_selfsigned = NULL;
48                                         /* Temporary self-signed cert */
49 #endif /* HAVE_SECKEYCHAINOPEN */
50 static char             *tls_keypath = NULL;
51                                         /* Server cert keychain path */
52 static _cups_mutex_t    tls_mutex = _CUPS_MUTEX_INITIALIZER;
53                                         /* Mutex for keychain/certs */
54 static int              tls_options = -1;/* Options for TLS connections */
55
56
57 /*
58  * Local functions...
59  */
60
61 static CFArrayRef       http_cdsa_copy_server(const char *common_name);
62 static SecCertificateRef http_cdsa_create_credential(http_credential_t *credential);
63 #ifdef HAVE_SECKEYCHAINOPEN
64 static const char       *http_cdsa_default_path(char *buffer, size_t bufsize);
65 static SecKeychainRef   http_cdsa_open_keychain(const char *path, char *filename, size_t filesize);
66 static SecKeychainRef   http_cdsa_open_system_keychain(void);
67 #endif /* HAVE_SECKEYCHAINOPEN */
68 static OSStatus         http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
69 static int              http_cdsa_set_credentials(http_t *http);
70 static OSStatus         http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
71
72
73 /*
74  * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
75  *
76  * @since CUPS 2.0/OS 10.10@
77  */
78
79 int                                     /* O - 1 on success, 0 on failure */
80 cupsMakeServerCredentials(
81     const char *path,                   /* I - Keychain path or @code NULL@ for default */
82     const char *common_name,            /* I - Common name */
83     int        num_alt_names,           /* I - Number of subject alternate names */
84     const char **alt_names,             /* I - Subject Alternate Names */
85     time_t     expiration_date)         /* I - Expiration date */
86 {
87 #if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE)
88   int                   status = 0;     /* Return status */
89   OSStatus              err;            /* Error code (if any) */
90   CFStringRef           cfcommon_name = NULL;
91                                         /* CF string for server name */
92   SecIdentityRef        ident = NULL;   /* Identity */
93   SecKeyRef             publicKey = NULL,
94                                         /* Public key */
95                         privateKey = NULL;
96                                         /* Private key */
97   SecCertificateRef     cert = NULL;    /* Self-signed certificate */
98   CFMutableDictionaryRef keyParams = NULL;
99                                         /* Key generation parameters */
100
101
102   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));
103
104   (void)path;
105   (void)num_alt_names;
106   (void)alt_names;
107   (void)expiration_date;
108
109   if (path)
110   {
111     DEBUG_puts("1cupsMakeServerCredentials: No keychain support compiled in, returning 0.");
112     return (0);
113   }
114
115   if (tls_selfsigned)
116   {
117     DEBUG_puts("1cupsMakeServerCredentials: Using existing self-signed cert.");
118     return (1);
119   }
120
121   cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
122   if (!cfcommon_name)
123   {
124     DEBUG_puts("1cupsMakeServerCredentials: Unable to create CF string of common name.");
125     goto cleanup;
126   }
127
128  /*
129   * Create a public/private key pair...
130   */
131
132   keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
133   if (!keyParams)
134   {
135     DEBUG_puts("1cupsMakeServerCredentials: Unable to create key parameters dictionary.");
136     goto cleanup;
137   }
138
139   CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
140   CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
141   CFDictionaryAddValue(keyParams, kSecAttrLabel, cfcommon_name);
142
143   err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
144   if (err != noErr)
145   {
146     DEBUG_printf(("1cupsMakeServerCredentials: Unable to generate key pair: %d.", (int)err));
147     goto cleanup;
148   }
149
150  /*
151   * Create a self-signed certificate using the public/private key pair...
152   */
153
154   CFIndex       usageInt = kSecKeyUsageAll;
155   CFNumberRef   usage = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &usageInt);
156   CFIndex       lenInt = 0;
157   CFNumberRef   len = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &lenInt);
158   CFTypeRef certKeys[] = { kSecCSRBasicContraintsPathLen, kSecSubjectAltName, kSecCertificateKeyUsage };
159   CFTypeRef certValues[] = { len, cfcommon_name, usage };
160   CFDictionaryRef certParams = CFDictionaryCreate(kCFAllocatorDefault, certKeys, certValues, sizeof(certKeys) / sizeof(certKeys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
161   CFRelease(usage);
162   CFRelease(len);
163
164   const void    *ca_o[] = { kSecOidOrganization, CFSTR("") };
165   const void    *ca_cn[] = { kSecOidCommonName, cfcommon_name };
166   CFArrayRef    ca_o_dn = CFArrayCreate(kCFAllocatorDefault, ca_o, 2, NULL);
167   CFArrayRef    ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
168   const void    *ca_dn_array[2];
169
170   ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_o_dn, 1, NULL);
171   ca_dn_array[1] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
172
173   CFArrayRef    subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, 2, NULL);
174
175   cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey);
176
177   CFRelease(subject);
178   CFRelease(certParams);
179
180   if (!cert)
181   {
182     DEBUG_puts("1cupsMakeServerCredentials: Unable to create self-signed certificate.");
183     goto cleanup;
184   }
185
186   ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
187
188   if (ident)
189   {
190     _cupsMutexLock(&tls_mutex);
191
192     if (tls_selfsigned)
193       CFRelease(ident);
194     else
195       tls_selfsigned = ident;
196
197     _cupsMutexLock(&tls_mutex);
198
199 #  if 0 /* Someday perhaps SecItemCopyMatching will work for identities, at which point  */
200     CFTypeRef itemKeys[] = { kSecClass, kSecAttrLabel, kSecValueRef };
201     CFTypeRef itemValues[] = { kSecClassIdentity, cfcommon_name, ident };
202     CFDictionaryRef itemAttrs = CFDictionaryCreate(kCFAllocatorDefault, itemKeys, itemValues, sizeof(itemKeys) / sizeof(itemKeys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
203
204     err = SecItemAdd(itemAttrs, NULL);
205     /* SecItemAdd consumes itemAttrs... */
206
207     CFRelease(ident);
208
209     if (err != noErr)
210     {
211       DEBUG_printf(("1cupsMakeServerCredentials: Unable to add identity to keychain: %d.", (int)err));
212       goto cleanup;
213     }
214 #  endif /* 0 */
215
216     status = 1;
217   }
218   else
219     DEBUG_puts("1cupsMakeServerCredentials: Unable to create identity from cert and keys.");
220
221   /*
222    * Cleanup and return...
223    */
224
225 cleanup:
226
227   if (cfcommon_name)
228     CFRelease(cfcommon_name);
229
230   if (keyParams)
231     CFRelease(keyParams);
232
233   if (cert)
234     CFRelease(cert);
235
236   if (publicKey)
237     CFRelease(publicKey);
238
239   if (privateKey)
240     CFRelease(privateKey);
241
242   DEBUG_printf(("1cupsMakeServerCredentials: Returning %d.", status));
243
244   return (status);
245
246 #else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
247   int           pid,                    /* Process ID of command */
248                 status,                 /* Status of command */
249                 i;                      /* Looping var */
250   char          command[1024],          /* Command */
251                 *argv[5],               /* Command-line arguments */
252                 *envp[1000],            /* Environment variables */
253                 days[32],               /* CERTTOOL_EXPIRATION_DAYS env var */
254                 keychain[1024],         /* Keychain argument */
255                 infofile[1024],         /* Type-in information for cert */
256                 filename[1024];         /* Default keychain path */
257   cups_file_t   *fp;                    /* Seed/info file */
258
259
260   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));
261
262   (void)num_alt_names;
263   (void)alt_names;
264
265   if (!path)
266     path = http_cdsa_default_path(filename, sizeof(filename));
267
268  /*
269   * Run the "certtool" command to generate a self-signed certificate...
270   */
271
272   if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
273     return (-1);
274
275  /*
276   * Create a file with the certificate information fields...
277   *
278   * Note: This assumes that the default questions are asked by the certtool
279   * command...
280   */
281
282  if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
283     return (-1);
284
285   cupsFilePrintf(fp,
286                  "CUPS Self-Signed Certificate\n"
287                                         /* Enter key and certificate label */
288                  "r\n"                  /* Generate RSA key pair */
289                  "2048\n"               /* 2048 bit encryption key */
290                  "y\n"                  /* OK (y = yes) */
291                  "b\n"                  /* Usage (b=signing/encryption) */
292                  "2\n"                  /* Sign with SHA256 */
293                  "y\n"                  /* OK (y = yes) */
294                  "%s\n"                 /* Common name */
295                  "\n"                   /* Country (default) */
296                  "\n"                   /* Organization (default) */
297                  "\n"                   /* Organizational unit (default) */
298                  "\n"                   /* State/Province (default) */
299                  "\n"                   /* Email address */
300                  "y\n",                 /* OK (y = yes) */
301                  common_name);
302   cupsFileClose(fp);
303
304   snprintf(keychain, sizeof(keychain), "k=%s", path);
305
306   argv[0] = "certtool";
307   argv[1] = "c";
308   argv[2] = keychain;
309   argv[3] = NULL;
310
311   snprintf(days, sizeof(days), "CERTTOOL_EXPIRATION_DAYS=%d", (int)((expiration_date - time(NULL) + 86399) / 86400));
312   envp[0] = days;
313   for (i = 0; i < (int)(sizeof(envp) / sizeof(envp[0]) - 2) && environ[i]; i ++)
314     envp[i + 1] = environ[i];
315   envp[i] = NULL;
316
317   posix_spawn_file_actions_t actions;   /* File actions */
318
319   posix_spawn_file_actions_init(&actions);
320   posix_spawn_file_actions_addclose(&actions, 0);
321   posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
322   posix_spawn_file_actions_addclose(&actions, 1);
323   posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0);
324   posix_spawn_file_actions_addclose(&actions, 2);
325   posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0);
326
327   if (posix_spawn(&pid, command, &actions, NULL, argv, envp))
328   {
329     unlink(infofile);
330     return (-1);
331   }
332
333   posix_spawn_file_actions_destroy(&actions);
334
335   unlink(infofile);
336
337   while (waitpid(pid, &status, 0) < 0)
338     if (errno != EINTR)
339     {
340       status = -1;
341       break;
342     }
343
344   return (!status);
345 #endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
346 }
347
348
349 /*
350  * 'cupsSetServerCredentials()' - Set the default server credentials.
351  *
352  * Note: The server credentials are used by all threads in the running process.
353  * This function is threadsafe.
354  *
355  * @since CUPS 2.0/macOS 10.10@
356  */
357
358 int                                     /* O - 1 on success, 0 on failure */
359 cupsSetServerCredentials(
360     const char *path,                   /* I - Keychain path or @code NULL@ for default */
361     const char *common_name,            /* I - Default common name for server */
362     int        auto_create)             /* I - 1 = automatically create self-signed certificates */
363 {
364   DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
365
366 #ifdef HAVE_SECKEYCHAINOPEN
367   char          filename[1024];         /* Keychain filename */
368   SecKeychainRef keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
369
370   if (!keychain)
371   {
372     DEBUG_puts("1cupsSetServerCredentials: Unable to open keychain.");
373     return (0);
374   }
375
376   _cupsMutexLock(&tls_mutex);
377
378  /*
379   * Close any keychain that is currently open...
380   */
381
382   if (tls_keychain)
383     CFRelease(tls_keychain);
384
385   if (tls_keypath)
386     _cupsStrFree(tls_keypath);
387
388   if (tls_common_name)
389     _cupsStrFree(tls_common_name);
390
391  /*
392   * Save the new keychain...
393   */
394
395   tls_keychain    = keychain;
396   tls_keypath     = _cupsStrAlloc(filename);
397   tls_auto_create = auto_create;
398   tls_common_name = _cupsStrAlloc(common_name);
399
400   _cupsMutexUnlock(&tls_mutex);
401
402   DEBUG_puts("1cupsSetServerCredentials: Opened keychain, returning 1.");
403   return (1);
404
405 #else
406   if (path)
407   {
408     DEBUG_puts("1cupsSetServerCredentials: No keychain support compiled in, returning 0.");
409     return (0);
410   }
411
412   tls_auto_create = auto_create;
413   tls_common_name = _cupsStrAlloc(common_name);
414
415   return (1);
416 #endif /* HAVE_SECKEYCHAINOPEN */
417 }
418
419
420 /*
421  * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
422  *                           an encrypted connection.
423  *
424  * @since CUPS 1.5/macOS 10.7@
425  */
426
427 int                                     /* O - Status of call (0 = success) */
428 httpCopyCredentials(
429     http_t       *http,                 /* I - Connection to server */
430     cups_array_t **credentials)         /* O - Array of credentials */
431 {
432   OSStatus              error;          /* Error code */
433   SecTrustRef           peerTrust;      /* Peer trust reference */
434   CFIndex               count;          /* Number of credentials */
435   SecCertificateRef     secCert;        /* Certificate reference */
436   CFDataRef             data;           /* Certificate data */
437   int                   i;              /* Looping var */
438
439
440   DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", (void *)http, (void *)credentials));
441
442   if (credentials)
443     *credentials = NULL;
444
445   if (!http || !http->tls || !credentials)
446     return (-1);
447
448   if (!(error = SSLCopyPeerTrust(http->tls, &peerTrust)) && peerTrust)
449   {
450     DEBUG_printf(("2httpCopyCredentials: Peer provided %d certificates.", (int)SecTrustGetCertificateCount(peerTrust)));
451
452     if ((*credentials = cupsArrayNew(NULL, NULL)) != NULL)
453     {
454       count = SecTrustGetCertificateCount(peerTrust);
455
456       for (i = 0; i < count; i ++)
457       {
458         secCert = SecTrustGetCertificateAtIndex(peerTrust, i);
459
460 #ifdef DEBUG
461         CFStringRef cf_name = SecCertificateCopySubjectSummary(secCert);
462         char name[1024];
463         if (cf_name)
464           CFStringGetCString(cf_name, name, sizeof(name), kCFStringEncodingUTF8);
465         else
466           strlcpy(name, "unknown", sizeof(name));
467
468         DEBUG_printf(("2httpCopyCredentials: Certificate %d name is \"%s\".", i, name));
469 #endif /* DEBUG */
470
471         if ((data = SecCertificateCopyData(secCert)) != NULL)
472         {
473           DEBUG_printf(("2httpCopyCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
474
475           httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
476           CFRelease(data);
477         }
478       }
479     }
480
481     CFRelease(peerTrust);
482   }
483
484   return (error);
485 }
486
487
488 /*
489  * '_httpCreateCredentials()' - Create credentials in the internal format.
490  */
491
492 http_tls_credentials_t                  /* O - Internal credentials */
493 _httpCreateCredentials(
494     cups_array_t *credentials)          /* I - Array of credentials */
495 {
496   CFMutableArrayRef     peerCerts;      /* Peer credentials reference */
497   SecCertificateRef     secCert;        /* Certificate reference */
498   http_credential_t     *credential;    /* Credential data */
499
500
501   if (!credentials)
502     return (NULL);
503
504   if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
505                                         cupsArrayCount(credentials),
506                                         &kCFTypeArrayCallBacks)) == NULL)
507     return (NULL);
508
509   for (credential = (http_credential_t *)cupsArrayFirst(credentials);
510        credential;
511        credential = (http_credential_t *)cupsArrayNext(credentials))
512   {
513     if ((secCert = http_cdsa_create_credential(credential)) != NULL)
514     {
515       CFArrayAppendValue(peerCerts, secCert);
516       CFRelease(secCert);
517     }
518   }
519
520   return (peerCerts);
521 }
522
523
524 /*
525  * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
526  *
527  * @since CUPS 2.0/macOS 10.10@
528  */
529
530 int                                     /* O - 1 if valid, 0 otherwise */
531 httpCredentialsAreValidForName(
532     cups_array_t *credentials,          /* I - Credentials */
533     const char   *common_name)          /* I - Name to check */
534 {
535   SecCertificateRef     secCert;        /* Certificate reference */
536   CFStringRef           cfcert_name = NULL;
537                                         /* Certificate's common name (CF string) */
538   char                  cert_name[256]; /* Certificate's common name (C string) */
539   int                   valid = 1;      /* Valid name? */
540
541
542   if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
543     return (0);
544
545  /*
546   * Compare the common names...
547   */
548
549   if ((cfcert_name = SecCertificateCopySubjectSummary(secCert)) == NULL)
550   {
551    /*
552     * Can't get common name, cannot be valid...
553     */
554
555     valid = 0;
556   }
557   else if (CFStringGetCString(cfcert_name, cert_name, sizeof(cert_name), kCFStringEncodingUTF8) &&
558            _cups_strcasecmp(common_name, cert_name))
559   {
560    /*
561     * Not an exact match for the common name, check for wildcard certs...
562     */
563
564     const char  *domain = strchr(common_name, '.');
565                                         /* Domain in common name */
566
567     if (strncmp(cert_name, "*.", 2) || !domain || _cups_strcasecmp(domain, cert_name + 1))
568     {
569      /*
570       * Not a wildcard match.
571       */
572
573       /* TODO: Check subject alternate names */
574       valid = 0;
575     }
576   }
577
578   if (cfcert_name)
579     CFRelease(cfcert_name);
580
581   CFRelease(secCert);
582
583   return (valid);
584 }
585
586
587 /*
588  * 'httpCredentialsGetTrust()' - Return the trust of credentials.
589  *
590  * @since CUPS 2.0/macOS 10.10@
591  */
592
593 http_trust_t                            /* O - Level of trust */
594 httpCredentialsGetTrust(
595     cups_array_t *credentials,          /* I - Credentials */
596     const char   *common_name)          /* I - Common name for trust lookup */
597 {
598   SecCertificateRef     secCert;        /* Certificate reference */
599   http_trust_t          trust = HTTP_TRUST_OK;
600                                         /* Trusted? */
601   cups_array_t          *tcreds = NULL; /* Trusted credentials */
602   _cups_globals_t       *cg = _cupsGlobals();
603                                         /* Per-thread globals */
604
605
606   if (!common_name)
607   {
608     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No common name specified."), 1);
609     return (HTTP_TRUST_UNKNOWN);
610   }
611
612   if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
613   {
614     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create credentials from array."), 1);
615     return (HTTP_TRUST_UNKNOWN);
616   }
617
618   if (cg->any_root < 0)
619     _cupsSetDefaults();
620
621  /*
622   * Look this common name up in the default keychains...
623   */
624
625   httpLoadCredentials(NULL, &tcreds, common_name);
626
627   if (tcreds)
628   {
629     char        credentials_str[1024],  /* String for incoming credentials */
630                 tcreds_str[1024];       /* String for saved credentials */
631
632     httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
633     httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
634
635     if (strcmp(credentials_str, tcreds_str))
636     {
637      /*
638       * Credentials don't match, let's look at the expiration date of the new
639       * credentials and allow if the new ones have a later expiration...
640       */
641
642       if (!cg->trust_first)
643       {
644        /*
645         * Do not trust certificates on first use...
646         */
647
648         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
649
650         trust = HTTP_TRUST_INVALID;
651       }
652       else if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds))
653       {
654        /*
655         * The new credentials are not newly issued...
656         */
657
658         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are older than stored credentials."), 1);
659
660         trust = HTTP_TRUST_INVALID;
661       }
662       else if (!httpCredentialsAreValidForName(credentials, common_name))
663       {
664        /*
665         * The common name does not match the issued certificate...
666         */
667
668         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are not valid for name."), 1);
669
670         trust = HTTP_TRUST_INVALID;
671       }
672       else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
673       {
674        /*
675         * Save the renewed credentials...
676         */
677
678         trust = HTTP_TRUST_RENEWED;
679
680         httpSaveCredentials(NULL, credentials, common_name);
681       }
682     }
683
684     httpFreeCredentials(tcreds);
685   }
686   else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
687   {
688     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1);
689     trust = HTTP_TRUST_INVALID;
690   }
691   else if (!cg->trust_first)
692   {
693    /*
694     * See if we have a site CA certificate we can compare...
695     */
696
697     if (!httpLoadCredentials(NULL, &tcreds, "site"))
698     {
699       if (cupsArrayCount(credentials) != (cupsArrayCount(tcreds) + 1))
700       {
701        /*
702         * Certificate isn't directly generated from the CA cert...
703         */
704
705         trust = HTTP_TRUST_INVALID;
706       }
707       else
708       {
709        /*
710         * Do a tail comparison of the two certificates...
711         */
712
713         http_credential_t       *a, *b;         /* Certificates */
714
715         for (a = (http_credential_t *)cupsArrayFirst(tcreds), b = (http_credential_t *)cupsArrayIndex(credentials, 1);
716              a && b;
717              a = (http_credential_t *)cupsArrayNext(tcreds), b = (http_credential_t *)cupsArrayNext(credentials))
718           if (a->datalen != b->datalen || memcmp(a->data, b->data, a->datalen))
719             break;
720
721         if (a || b)
722           trust = HTTP_TRUST_INVALID;
723       }
724
725       if (trust != HTTP_TRUST_OK)
726         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials do not validate against site CA certificate."), 1);
727     }
728     else
729     {
730       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
731       trust = HTTP_TRUST_INVALID;
732     }
733   }
734
735   if (trust == HTTP_TRUST_OK && !cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
736   {
737     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials have expired."), 1);
738     trust = HTTP_TRUST_EXPIRED;
739   }
740
741   if (trust == HTTP_TRUST_OK && !cg->any_root && cupsArrayCount(credentials) == 1)
742   {
743     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1);
744     trust = HTTP_TRUST_INVALID;
745   }
746
747   CFRelease(secCert);
748
749   return (trust);
750 }
751
752
753 /*
754  * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
755  *
756  * @since CUPS 2.0/macOS 10.10@
757  */
758
759 time_t                                  /* O - Expiration date of credentials */
760 httpCredentialsGetExpiration(
761     cups_array_t *credentials)          /* I - Credentials */
762 {
763   SecCertificateRef     secCert;        /* Certificate reference */
764   time_t                expiration;     /* Expiration date */
765
766
767   if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
768     return (0);
769
770   expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
771
772   CFRelease(secCert);
773
774   return (expiration);
775 }
776
777
778 /*
779  * 'httpCredentialsString()' - Return a string representing the credentials.
780  *
781  * @since CUPS 2.0/macOS 10.10@
782  */
783
784 size_t                                  /* O - Total size of credentials string */
785 httpCredentialsString(
786     cups_array_t *credentials,          /* I - Credentials */
787     char         *buffer,               /* I - Buffer or @code NULL@ */
788     size_t       bufsize)               /* I - Size of buffer */
789 {
790   http_credential_t     *first;         /* First certificate */
791   SecCertificateRef     secCert;        /* Certificate reference */
792
793
794   DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", (void *)credentials, (void *)buffer, CUPS_LLCAST bufsize));
795
796   if (!buffer)
797     return (0);
798
799   if (buffer && bufsize > 0)
800     *buffer = '\0';
801
802   if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
803       (secCert = http_cdsa_create_credential(first)) != NULL)
804   {
805     CFStringRef         cf_name;        /* CF common name string */
806     char                name[256];      /* Common name associated with cert */
807     time_t              expiration;     /* Expiration date of cert */
808     _cups_md5_state_t   md5_state;      /* MD5 state */
809     unsigned char       md5_digest[16]; /* MD5 result */
810
811     if ((cf_name = SecCertificateCopySubjectSummary(secCert)) != NULL)
812     {
813       CFStringGetCString(cf_name, name, (CFIndex)sizeof(name), kCFStringEncodingUTF8);
814       CFRelease(cf_name);
815     }
816     else
817       strlcpy(name, "unknown", sizeof(name));
818
819     expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
820
821     _cupsMD5Init(&md5_state);
822     _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
823     _cupsMD5Finish(&md5_state, md5_digest);
824
825     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]);
826
827     CFRelease(secCert);
828   }
829
830   DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
831
832   return (strlen(buffer));
833 }
834
835
836 /*
837  * '_httpFreeCredentials()' - Free internal credentials.
838  */
839
840 void
841 _httpFreeCredentials(
842     http_tls_credentials_t credentials) /* I - Internal credentials */
843 {
844   if (!credentials)
845     return;
846
847   CFRelease(credentials);
848 }
849
850
851 /*
852  * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
853  *
854  * @since CUPS 2.0/OS 10.10@
855  */
856
857 int                                     /* O - 0 on success, -1 on error */
858 httpLoadCredentials(
859     const char   *path,                 /* I  - Keychain path or @code NULL@ for default */
860     cups_array_t **credentials,         /* IO - Credentials */
861     const char   *common_name)          /* I  - Common name for credentials */
862 {
863   OSStatus              err;            /* Error info */
864 #ifdef HAVE_SECKEYCHAINOPEN
865   char                  filename[1024]; /* Filename for keychain */
866   SecKeychainRef        keychain = NULL,/* Keychain reference */
867                         syschain = NULL;/* System keychain */
868   CFArrayRef            list;           /* Keychain list */
869 #endif /* HAVE_SECKEYCHAINOPEN */
870   SecCertificateRef     cert = NULL;    /* Certificate */
871   CFDataRef             data;           /* Certificate data */
872   SecPolicyRef          policy = NULL;  /* Policy ref */
873   CFStringRef           cfcommon_name = NULL;
874                                         /* Server name */
875   CFMutableDictionaryRef query = NULL;  /* Query qualifiers */
876
877
878   DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
879
880   if (!credentials)
881     return (-1);
882
883   *credentials = NULL;
884
885 #ifdef HAVE_SECKEYCHAINOPEN
886   keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
887
888   if (!keychain)
889     goto cleanup;
890
891   syschain = http_cdsa_open_system_keychain();
892
893 #else
894   if (path)
895     return (-1);
896 #endif /* HAVE_SECKEYCHAINOPEN */
897
898   cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
899
900   policy = SecPolicyCreateSSL(1, cfcommon_name);
901
902   if (cfcommon_name)
903     CFRelease(cfcommon_name);
904
905   if (!policy)
906     goto cleanup;
907
908   if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
909     goto cleanup;
910
911   CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
912   CFDictionaryAddValue(query, kSecMatchPolicy, policy);
913   CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
914   CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
915
916 #ifdef HAVE_SECKEYCHAINOPEN
917   if (syschain)
918   {
919     const void *values[2] = { syschain, keychain };
920
921     list = CFArrayCreate(kCFAllocatorDefault, (const void **)values, 2, &kCFTypeArrayCallBacks);
922   }
923   else
924     list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
925   CFDictionaryAddValue(query, kSecMatchSearchList, list);
926   CFRelease(list);
927 #endif /* HAVE_SECKEYCHAINOPEN */
928
929   err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
930
931   if (err)
932     goto cleanup;
933
934   if (CFGetTypeID(cert) != SecCertificateGetTypeID())
935     goto cleanup;
936
937   if ((data = SecCertificateCopyData(cert)) != NULL)
938   {
939     DEBUG_printf(("1httpLoadCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
940
941     *credentials = cupsArrayNew(NULL, NULL);
942     httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
943     CFRelease(data);
944   }
945
946   cleanup :
947
948 #ifdef HAVE_SECKEYCHAINOPEN
949   if (keychain)
950     CFRelease(keychain);
951
952   if (syschain)
953     CFRelease(syschain);
954 #endif /* HAVE_SECKEYCHAINOPEN */
955   if (cert)
956     CFRelease(cert);
957   if (policy)
958     CFRelease(policy);
959   if (query)
960     CFRelease(query);
961
962   DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
963
964   return (*credentials ? 0 : -1);
965 }
966
967
968 /*
969  * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
970  *
971  * @since CUPS 2.0/OS 10.10@
972  */
973
974 int                                     /* O - -1 on error, 0 on success */
975 httpSaveCredentials(
976     const char   *path,                 /* I - Keychain path or @code NULL@ for default */
977     cups_array_t *credentials,          /* I - Credentials */
978     const char   *common_name)          /* I - Common name for credentials */
979 {
980   int                   ret = -1;       /* Return value */
981   OSStatus              err;            /* Error info */
982 #ifdef HAVE_SECKEYCHAINOPEN
983   char                  filename[1024]; /* Filename for keychain */
984   SecKeychainRef        keychain = NULL;/* Keychain reference */
985   CFArrayRef            list;           /* Keychain list */
986 #endif /* HAVE_SECKEYCHAINOPEN */
987   SecCertificateRef     cert = NULL;    /* Certificate */
988   CFMutableDictionaryRef attrs = NULL;  /* Attributes for add */
989
990
991   DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
992   if (!credentials)
993     goto cleanup;
994
995   if (!httpCredentialsAreValidForName(credentials, common_name))
996   {
997     DEBUG_puts("1httpSaveCredentials: Common name does not match.");
998     return (-1);
999   }
1000
1001   if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
1002   {
1003     DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
1004     goto cleanup;
1005   }
1006
1007 #ifdef HAVE_SECKEYCHAINOPEN
1008   keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
1009
1010   if (!keychain)
1011     goto cleanup;
1012
1013 #else
1014   if (path)
1015     return (-1);
1016 #endif /* HAVE_SECKEYCHAINOPEN */
1017
1018   if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
1019   {
1020     DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
1021     goto cleanup;
1022   }
1023
1024   CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
1025   CFDictionaryAddValue(attrs, kSecValueRef, cert);
1026
1027 #ifdef HAVE_SECKEYCHAINOPEN
1028   if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL)
1029   {
1030     DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains.");
1031     goto cleanup;
1032   }
1033   CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
1034   CFRelease(list);
1035 #endif /* HAVE_SECKEYCHAINOPEN */
1036
1037   /* Note: SecItemAdd consumes "attrs"... */
1038   err = SecItemAdd(attrs, NULL);
1039   DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
1040
1041   cleanup :
1042
1043 #ifdef HAVE_SECKEYCHAINOPEN
1044   if (keychain)
1045     CFRelease(keychain);
1046 #endif /* HAVE_SECKEYCHAINOPEN */
1047   if (cert)
1048     CFRelease(cert);
1049
1050   DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
1051
1052   return (ret);
1053 }
1054
1055
1056 /*
1057  * '_httpTLSInitialize()' - Initialize the TLS stack.
1058  */
1059
1060 void
1061 _httpTLSInitialize(void)
1062 {
1063  /*
1064   * Nothing to do...
1065   */
1066 }
1067
1068
1069 /*
1070  * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
1071  */
1072
1073 size_t
1074 _httpTLSPending(http_t *http)           /* I - HTTP connection */
1075 {
1076   size_t bytes;                         /* Bytes that are available */
1077
1078
1079   if (!SSLGetBufferedReadSize(http->tls, &bytes))
1080     return (bytes);
1081
1082   return (0);
1083 }
1084
1085
1086 /*
1087  * '_httpTLSRead()' - Read from a SSL/TLS connection.
1088  */
1089
1090 int                                     /* O - Bytes read */
1091 _httpTLSRead(http_t *http,              /* I - HTTP connection */
1092               char   *buf,              /* I - Buffer to store data */
1093               int    len)               /* I - Length of buffer */
1094 {
1095   int           result;                 /* Return value */
1096   OSStatus      error;                  /* Error info */
1097   size_t        processed;              /* Number of bytes processed */
1098
1099
1100   error = SSLRead(http->tls, buf, (size_t)len, &processed);
1101   DEBUG_printf(("6_httpTLSRead: error=%d, processed=%d", (int)error,
1102                 (int)processed));
1103   switch (error)
1104   {
1105     case 0 :
1106         result = (int)processed;
1107         break;
1108
1109     case errSSLWouldBlock :
1110         if (processed)
1111           result = (int)processed;
1112         else
1113         {
1114           result = -1;
1115           errno  = EINTR;
1116         }
1117         break;
1118
1119     case errSSLClosedGraceful :
1120     default :
1121         if (processed)
1122           result = (int)processed;
1123         else
1124         {
1125           result = -1;
1126           errno  = EPIPE;
1127         }
1128         break;
1129   }
1130
1131   return (result);
1132 }
1133
1134
1135 /*
1136  * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
1137  */
1138
1139 void
1140 _httpTLSSetOptions(int options)         /* I - Options */
1141 {
1142   tls_options = options;
1143 }
1144
1145
1146 /*
1147  * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
1148  */
1149
1150 int                                     /* O - 0 on success, -1 on failure */
1151 _httpTLSStart(http_t *http)             /* I - HTTP connection */
1152 {
1153   char                  hostname[256],  /* Hostname */
1154                         *hostptr;       /* Pointer into hostname */
1155   _cups_globals_t       *cg = _cupsGlobals();
1156                                         /* Pointer to library globals */
1157   OSStatus              error;          /* Error code */
1158   const char            *message = NULL;/* Error message */
1159   cups_array_t          *credentials;   /* Credentials array */
1160   cups_array_t          *names;         /* CUPS distinguished names */
1161   CFArrayRef            dn_array;       /* CF distinguished names array */
1162   CFIndex               count;          /* Number of credentials */
1163   CFDataRef             data;           /* Certificate data */
1164   int                   i;              /* Looping var */
1165   http_credential_t     *credential;    /* Credential data */
1166
1167
1168   DEBUG_printf(("3_httpTLSStart(http=%p)", (void *)http));
1169
1170   if (tls_options < 0)
1171   {
1172     DEBUG_puts("4_httpTLSStart: Setting defaults.");
1173     _cupsSetDefaults();
1174     DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
1175   }
1176
1177 #ifdef HAVE_SECKEYCHAINOPEN
1178   if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
1179   {
1180     DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
1181     http->error  = errno = EINVAL;
1182     http->status = HTTP_STATUS_ERROR;
1183     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
1184
1185     return (-1);
1186   }
1187 #endif /* HAVE_SECKEYCHAINOPEN */
1188
1189   if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
1190   {
1191     DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
1192     http->error  = errno = ENOMEM;
1193     http->status = HTTP_STATUS_ERROR;
1194     _cupsSetHTTPError(HTTP_STATUS_ERROR);
1195
1196     return (-1);
1197   }
1198
1199   error = SSLSetConnection(http->tls, http);
1200   DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
1201
1202   if (!error)
1203   {
1204     error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
1205     DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
1206   }
1207
1208   if (!error)
1209   {
1210     error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1211                                 true);
1212     DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d", (int)error));
1213   }
1214
1215   if (!error)
1216   {
1217     SSLProtocol minProtocol;
1218
1219     if (tls_options & _HTTP_TLS_DENY_TLS10)
1220       minProtocol = kTLSProtocol11;
1221     else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
1222       minProtocol = kSSLProtocol3;
1223     else
1224       minProtocol = kTLSProtocol1;
1225
1226     error = SSLSetProtocolVersionMin(http->tls, minProtocol);
1227     DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", minProtocol, (int)error));
1228   }
1229
1230 #  if HAVE_SSLSETENABLEDCIPHERS
1231   if (!error)
1232   {
1233     SSLCipherSuite      supported[100]; /* Supported cipher suites */
1234     size_t              num_supported;  /* Number of supported cipher suites */
1235     SSLCipherSuite      enabled[100];   /* Cipher suites to enable */
1236     size_t              num_enabled;    /* Number of cipher suites to enable */
1237
1238     num_supported = sizeof(supported) / sizeof(supported[0]);
1239     error         = SSLGetSupportedCiphers(http->tls, supported, &num_supported);
1240
1241     if (!error)
1242     {
1243       DEBUG_printf(("4_httpTLSStart: %d cipher suites supported.", (int)num_supported));
1244
1245       for (i = 0, num_enabled = 0; i < (int)num_supported && num_enabled < (sizeof(enabled) / sizeof(enabled[0])); i ++)
1246       {
1247         switch (supported[i])
1248         {
1249           /* Obviously insecure cipher suites that we never want to use */
1250           case SSL_NULL_WITH_NULL_NULL :
1251           case SSL_RSA_WITH_NULL_MD5 :
1252           case SSL_RSA_WITH_NULL_SHA :
1253           case SSL_RSA_EXPORT_WITH_RC4_40_MD5 :
1254           case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 :
1255           case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA :
1256           case SSL_RSA_WITH_DES_CBC_SHA :
1257           case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA :
1258           case SSL_DH_DSS_WITH_DES_CBC_SHA :
1259           case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA :
1260           case SSL_DH_RSA_WITH_DES_CBC_SHA :
1261           case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA :
1262           case SSL_DHE_DSS_WITH_DES_CBC_SHA :
1263           case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA :
1264           case SSL_DHE_RSA_WITH_DES_CBC_SHA :
1265           case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 :
1266           case SSL_DH_anon_WITH_RC4_128_MD5 :
1267           case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA :
1268           case SSL_DH_anon_WITH_DES_CBC_SHA :
1269           case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA :
1270           case SSL_FORTEZZA_DMS_WITH_NULL_SHA :
1271           case TLS_DH_anon_WITH_AES_128_CBC_SHA :
1272           case TLS_DH_anon_WITH_AES_256_CBC_SHA :
1273           case TLS_ECDH_ECDSA_WITH_NULL_SHA :
1274           case TLS_ECDHE_RSA_WITH_NULL_SHA :
1275           case TLS_ECDH_anon_WITH_NULL_SHA :
1276           case TLS_ECDH_anon_WITH_RC4_128_SHA :
1277           case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA :
1278           case TLS_ECDH_anon_WITH_AES_128_CBC_SHA :
1279           case TLS_ECDH_anon_WITH_AES_256_CBC_SHA :
1280           case TLS_RSA_WITH_NULL_SHA256 :
1281           case TLS_DH_anon_WITH_AES_128_CBC_SHA256 :
1282           case TLS_DH_anon_WITH_AES_256_CBC_SHA256 :
1283           case TLS_PSK_WITH_NULL_SHA :
1284           case TLS_DHE_PSK_WITH_NULL_SHA :
1285           case TLS_RSA_PSK_WITH_NULL_SHA :
1286           case TLS_DH_anon_WITH_AES_128_GCM_SHA256 :
1287           case TLS_DH_anon_WITH_AES_256_GCM_SHA384 :
1288           case TLS_PSK_WITH_NULL_SHA256 :
1289           case TLS_PSK_WITH_NULL_SHA384 :
1290           case TLS_DHE_PSK_WITH_NULL_SHA256 :
1291           case TLS_DHE_PSK_WITH_NULL_SHA384 :
1292           case TLS_RSA_PSK_WITH_NULL_SHA256 :
1293           case TLS_RSA_PSK_WITH_NULL_SHA384 :
1294           case SSL_RSA_WITH_DES_CBC_MD5 :
1295               DEBUG_printf(("4_httpTLSStart: Excluding insecure cipher suite %d", supported[i]));
1296               break;
1297
1298           /* RC4 cipher suites that should only be used as a last resort */
1299           case SSL_RSA_WITH_RC4_128_MD5 :
1300           case SSL_RSA_WITH_RC4_128_SHA :
1301           case TLS_ECDH_ECDSA_WITH_RC4_128_SHA :
1302           case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA :
1303           case TLS_ECDH_RSA_WITH_RC4_128_SHA :
1304           case TLS_ECDHE_RSA_WITH_RC4_128_SHA :
1305           case TLS_PSK_WITH_RC4_128_SHA :
1306           case TLS_DHE_PSK_WITH_RC4_128_SHA :
1307           case TLS_RSA_PSK_WITH_RC4_128_SHA :
1308               if (tls_options & _HTTP_TLS_ALLOW_RC4)
1309                 enabled[num_enabled ++] = supported[i];
1310               else
1311                 DEBUG_printf(("4_httpTLSStart: Excluding RC4 cipher suite %d", supported[i]));
1312               break;
1313
1314           /* DH/DHE cipher suites that are problematic with parameters < 1024 bits */
1315           case TLS_DH_DSS_WITH_AES_128_CBC_SHA :
1316           case TLS_DH_RSA_WITH_AES_128_CBC_SHA :
1317           case TLS_DHE_DSS_WITH_AES_128_CBC_SHA :
1318           case TLS_DHE_RSA_WITH_AES_128_CBC_SHA :
1319           case TLS_DH_DSS_WITH_AES_256_CBC_SHA :
1320           case TLS_DH_RSA_WITH_AES_256_CBC_SHA :
1321           case TLS_DHE_DSS_WITH_AES_256_CBC_SHA :
1322           case TLS_DHE_RSA_WITH_AES_256_CBC_SHA :
1323           case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA :
1324           case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA :
1325 //          case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA :
1326           case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA :
1327           case TLS_DH_DSS_WITH_AES_128_CBC_SHA256 :
1328           case TLS_DH_RSA_WITH_AES_128_CBC_SHA256 :
1329           case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 :
1330           case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 :
1331           case TLS_DH_DSS_WITH_AES_256_CBC_SHA256 :
1332           case TLS_DH_RSA_WITH_AES_256_CBC_SHA256 :
1333           case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 :
1334           case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 :
1335           case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA :
1336           case TLS_DHE_PSK_WITH_AES_128_CBC_SHA :
1337           case TLS_DHE_PSK_WITH_AES_256_CBC_SHA :
1338 //          case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 :
1339 //          case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 :
1340           case TLS_DH_RSA_WITH_AES_128_GCM_SHA256 :
1341           case TLS_DH_RSA_WITH_AES_256_GCM_SHA384 :
1342 //          case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 :
1343 //          case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 :
1344           case TLS_DH_DSS_WITH_AES_128_GCM_SHA256 :
1345           case TLS_DH_DSS_WITH_AES_256_GCM_SHA384 :
1346           case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 :
1347           case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 :
1348           case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
1349           case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
1350               if (tls_options & _HTTP_TLS_ALLOW_DH)
1351                 enabled[num_enabled ++] = supported[i];
1352               else
1353                 DEBUG_printf(("4_httpTLSStart: Excluding DH/DHE cipher suite %d", supported[i]));
1354               break;
1355
1356           /* Anything else we'll assume is secure */
1357           default :
1358               enabled[num_enabled ++] = supported[i];
1359               break;
1360         }
1361       }
1362
1363       DEBUG_printf(("4_httpTLSStart: %d cipher suites enabled.", (int)num_enabled));
1364       error = SSLSetEnabledCiphers(http->tls, enabled, num_enabled);
1365     }
1366   }
1367 #endif /* HAVE_SSLSETENABLEDCIPHERS */
1368
1369   if (!error && http->mode == _HTTP_MODE_CLIENT)
1370   {
1371    /*
1372     * Client: set client-side credentials, if any...
1373     */
1374
1375     if (cg->client_cert_cb)
1376     {
1377       error = SSLSetSessionOption(http->tls,
1378                                   kSSLSessionOptionBreakOnCertRequested, true);
1379       DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, "
1380                     "error=%d", (int)error));
1381     }
1382     else
1383     {
1384       error = http_cdsa_set_credentials(http);
1385       DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d",
1386                     (int)error));
1387     }
1388   }
1389   else if (!error)
1390   {
1391    /*
1392     * Server: find/create a certificate for TLS...
1393     */
1394
1395     if (http->fields[HTTP_FIELD_HOST][0])
1396     {
1397      /*
1398       * Use hostname for TLS upgrade...
1399       */
1400
1401       strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1402     }
1403     else
1404     {
1405      /*
1406       * Resolve hostname from connection address...
1407       */
1408
1409       http_addr_t       addr;           /* Connection address */
1410       socklen_t         addrlen;        /* Length of address */
1411
1412       addrlen = sizeof(addr);
1413       if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1414       {
1415         DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
1416         hostname[0] = '\0';
1417       }
1418       else if (httpAddrLocalhost(&addr))
1419         hostname[0] = '\0';
1420       else
1421       {
1422         httpAddrLookup(&addr, hostname, sizeof(hostname));
1423         DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1424       }
1425     }
1426
1427     if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1428       hostname[0] = '\0';               /* Don't allow numeric addresses */
1429
1430     if (hostname[0])
1431       http->tls_credentials = http_cdsa_copy_server(hostname);
1432     else if (tls_common_name)
1433       http->tls_credentials = http_cdsa_copy_server(tls_common_name);
1434
1435     if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name))
1436     {
1437       DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1438
1439       if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1440       {
1441         DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
1442         http->error  = errno = EINVAL;
1443         http->status = HTTP_STATUS_ERROR;
1444         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1445
1446         return (-1);
1447       }
1448
1449       http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name);
1450     }
1451
1452     if (!http->tls_credentials)
1453     {
1454       DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
1455       http->error  = errno = EINVAL;
1456       http->status = HTTP_STATUS_ERROR;
1457       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1);
1458
1459       return (-1);
1460     }
1461
1462     error = SSLSetCertificate(http->tls, http->tls_credentials);
1463
1464     DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
1465   }
1466
1467   DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", (void *)http->tls_credentials));
1468
1469  /*
1470   * Let the server know which hostname/domain we are trying to connect to
1471   * in case it wants to serve up a certificate with a matching common name.
1472   */
1473
1474   if (!error && http->mode == _HTTP_MODE_CLIENT)
1475   {
1476    /*
1477     * Client: get the hostname to use for TLS...
1478     */
1479
1480     if (httpAddrLocalhost(http->hostaddr))
1481     {
1482       strlcpy(hostname, "localhost", sizeof(hostname));
1483     }
1484     else
1485     {
1486      /*
1487       * Otherwise make sure the hostname we have does not end in a trailing dot.
1488       */
1489
1490       strlcpy(hostname, http->hostname, sizeof(hostname));
1491       if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1492           *hostptr == '.')
1493         *hostptr = '\0';
1494     }
1495
1496     error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1497
1498     DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
1499   }
1500
1501   if (!error)
1502   {
1503     int done = 0;                       /* Are we done yet? */
1504
1505     while (!error && !done)
1506     {
1507       error = SSLHandshake(http->tls);
1508
1509       DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
1510
1511       switch (error)
1512       {
1513         case noErr :
1514             done = 1;
1515             break;
1516
1517         case errSSLWouldBlock :
1518             error = noErr;              /* Force a retry */
1519             usleep(1000);               /* in 1 millisecond */
1520             break;
1521
1522         case errSSLServerAuthCompleted :
1523             error = 0;
1524             if (cg->server_cert_cb)
1525             {
1526               error = httpCopyCredentials(http, &credentials);
1527               if (!error)
1528               {
1529                 error = (cg->server_cert_cb)(http, http->tls, credentials,
1530                                              cg->server_cert_data);
1531                 httpFreeCredentials(credentials);
1532               }
1533
1534               DEBUG_printf(("4_httpTLSStart: Server certificate callback "
1535                             "returned %d.", (int)error));
1536             }
1537             break;
1538
1539         case errSSLClientCertRequested :
1540             error = 0;
1541
1542             if (cg->client_cert_cb)
1543             {
1544               names = NULL;
1545               if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
1546                   dn_array)
1547               {
1548                 if ((names = cupsArrayNew(NULL, NULL)) != NULL)
1549                 {
1550                   for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
1551                   {
1552                     data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
1553
1554                     if ((credential = malloc(sizeof(*credential))) != NULL)
1555                     {
1556                       credential->datalen = (size_t)CFDataGetLength(data);
1557                       if ((credential->data = malloc(credential->datalen)))
1558                       {
1559                         memcpy((void *)credential->data, CFDataGetBytePtr(data),
1560                                credential->datalen);
1561                         cupsArrayAdd(names, credential);
1562                       }
1563                       else
1564                         free(credential);
1565                     }
1566                   }
1567                 }
1568
1569                 CFRelease(dn_array);
1570               }
1571
1572               if (!error)
1573               {
1574                 error = (cg->client_cert_cb)(http, http->tls, names,
1575                                              cg->client_cert_data);
1576
1577                 DEBUG_printf(("4_httpTLSStart: Client certificate callback "
1578                               "returned %d.", (int)error));
1579               }
1580
1581               httpFreeCredentials(names);
1582             }
1583             break;
1584
1585         case errSSLUnknownRootCert :
1586             message = _("Unable to establish a secure connection to host "
1587                         "(untrusted certificate).");
1588             break;
1589
1590         case errSSLNoRootCert :
1591             message = _("Unable to establish a secure connection to host "
1592                         "(self-signed certificate).");
1593             break;
1594
1595         case errSSLCertExpired :
1596             message = _("Unable to establish a secure connection to host "
1597                         "(expired certificate).");
1598             break;
1599
1600         case errSSLCertNotYetValid :
1601             message = _("Unable to establish a secure connection to host "
1602                         "(certificate not yet valid).");
1603             break;
1604
1605         case errSSLHostNameMismatch :
1606             message = _("Unable to establish a secure connection to host "
1607                         "(host name mismatch).");
1608             break;
1609
1610         case errSSLXCertChainInvalid :
1611             message = _("Unable to establish a secure connection to host "
1612                         "(certificate chain invalid).");
1613             break;
1614
1615         case errSSLConnectionRefused :
1616             message = _("Unable to establish a secure connection to host "
1617                         "(peer dropped connection before responding).");
1618             break;
1619
1620         default :
1621             break;
1622       }
1623     }
1624   }
1625
1626   if (error)
1627   {
1628     http->error  = error;
1629     http->status = HTTP_STATUS_ERROR;
1630     errno        = ECONNREFUSED;
1631
1632     CFRelease(http->tls);
1633     http->tls = NULL;
1634
1635    /*
1636     * If an error string wasn't set by the callbacks use a generic one...
1637     */
1638
1639     if (!message)
1640 #ifdef HAVE_CSSMERRORSTRING
1641       message = cssmErrorString(error);
1642 #else
1643       message = _("Unable to establish a secure connection to host.");
1644 #endif /* HAVE_CSSMERRORSTRING */
1645
1646     _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
1647
1648     return (-1);
1649   }
1650
1651   return (0);
1652 }
1653
1654
1655 /*
1656  * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
1657  */
1658
1659 void
1660 _httpTLSStop(http_t *http)              /* I - HTTP connection */
1661 {
1662   while (SSLClose(http->tls) == errSSLWouldBlock)
1663     usleep(1000);
1664
1665   CFRelease(http->tls);
1666
1667   if (http->tls_credentials)
1668     CFRelease(http->tls_credentials);
1669
1670   http->tls             = NULL;
1671   http->tls_credentials = NULL;
1672 }
1673
1674
1675 /*
1676  * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1677  */
1678
1679 int                                     /* O - Bytes written */
1680 _httpTLSWrite(http_t     *http,         /* I - HTTP connection */
1681                const char *buf,         /* I - Buffer holding data */
1682                int        len)          /* I - Length of buffer */
1683 {
1684   ssize_t       result;                 /* Return value */
1685   OSStatus      error;                  /* Error info */
1686   size_t        processed;              /* Number of bytes processed */
1687
1688
1689   DEBUG_printf(("2_httpTLSWrite(http=%p, buf=%p, len=%d)", (void *)http, (void *)buf, len));
1690
1691   error = SSLWrite(http->tls, buf, (size_t)len, &processed);
1692
1693   switch (error)
1694   {
1695     case 0 :
1696         result = (int)processed;
1697         break;
1698
1699     case errSSLWouldBlock :
1700         if (processed)
1701         {
1702           result = (int)processed;
1703         }
1704         else
1705         {
1706           result = -1;
1707           errno  = EINTR;
1708         }
1709         break;
1710
1711     case errSSLClosedGraceful :
1712     default :
1713         if (processed)
1714         {
1715           result = (int)processed;
1716         }
1717         else
1718         {
1719           result = -1;
1720           errno  = EPIPE;
1721         }
1722         break;
1723   }
1724
1725   DEBUG_printf(("3_httpTLSWrite: Returning %d.", (int)result));
1726
1727   return ((int)result);
1728 }
1729
1730
1731 /*
1732  * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
1733  */
1734
1735 static CFArrayRef                       /* O - Array of certificates or NULL */
1736 http_cdsa_copy_server(
1737     const char *common_name)            /* I - Server's hostname */
1738 {
1739 #ifdef HAVE_SECKEYCHAINOPEN
1740   OSStatus              err;            /* Error info */
1741   SecIdentityRef        identity = NULL;/* Identity */
1742   CFArrayRef            certificates = NULL;
1743                                         /* Certificate array */
1744   SecPolicyRef          policy = NULL;  /* Policy ref */
1745   CFStringRef           cfcommon_name = NULL;
1746                                         /* Server name */
1747   CFMutableDictionaryRef query = NULL;  /* Query qualifiers */
1748   CFArrayRef            list = NULL;    /* Keychain list */
1749   SecKeychainRef        syschain = NULL;/* System keychain */
1750
1751
1752   DEBUG_printf(("3http_cdsa_copy_server(common_name=\"%s\")", common_name));
1753
1754   cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
1755
1756   policy = SecPolicyCreateSSL(1, cfcommon_name);
1757
1758   if (!policy)
1759   {
1760     DEBUG_puts("4http_cdsa_copy_server: Unable to create SSL policy.");
1761     goto cleanup;
1762   }
1763
1764   if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
1765   {
1766     DEBUG_puts("4http_cdsa_copy_server: Unable to create query dictionary.");
1767     goto cleanup;
1768   }
1769
1770   _cupsMutexLock(&tls_mutex);
1771
1772   CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
1773   CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1774   CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1775   CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
1776
1777   syschain = http_cdsa_open_system_keychain();
1778
1779   if (syschain)
1780   {
1781     const void *values[2] = { syschain, tls_keychain };
1782
1783     list = CFArrayCreate(kCFAllocatorDefault, (const void **)values, 2, &kCFTypeArrayCallBacks);
1784   }
1785   else
1786     list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
1787
1788   CFDictionaryAddValue(query, kSecMatchSearchList, list);
1789   CFRelease(list);
1790
1791   err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
1792
1793   _cupsMutexUnlock(&tls_mutex);
1794
1795   if (err != noErr)
1796   {
1797     DEBUG_printf(("4http_cdsa_copy_server: SecItemCopyMatching failed with status %d.", (int)err));
1798     goto cleanup;
1799   }
1800
1801   if (CFGetTypeID(identity) != SecIdentityGetTypeID())
1802   {
1803     DEBUG_puts("4http_cdsa_copy_server: Search returned something that is not an identity.");
1804     goto cleanup;
1805   }
1806
1807   if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
1808   {
1809     DEBUG_puts("4http_cdsa_copy_server: Unable to create array of certificates.");
1810     goto cleanup;
1811   }
1812
1813   cleanup :
1814
1815   if (syschain)
1816     CFRelease(syschain);
1817   if (identity)
1818     CFRelease(identity);
1819   if (policy)
1820     CFRelease(policy);
1821   if (cfcommon_name)
1822     CFRelease(cfcommon_name);
1823   if (query)
1824     CFRelease(query);
1825
1826   DEBUG_printf(("4http_cdsa_copy_server: Returning %p.", (void *)certificates));
1827
1828   return (certificates);
1829 #else
1830
1831   if (!tls_selfsigned)
1832     return (NULL);
1833
1834   return (CFArrayCreate(NULL, (const void **)&tls_selfsigned, 1, &kCFTypeArrayCallBacks));
1835 #endif /* HAVE_SECKEYCHAINOPEN */
1836 }
1837
1838
1839 /*
1840  * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
1841  */
1842
1843 static SecCertificateRef                        /* O - Certificate */
1844 http_cdsa_create_credential(
1845     http_credential_t *credential)              /* I - Credential */
1846 {
1847   if (!credential)
1848     return (NULL);
1849
1850   return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
1851 }
1852
1853
1854 #ifdef HAVE_SECKEYCHAINOPEN
1855 /*
1856  * 'http_cdsa_default_path()' - Get the default keychain path.
1857  */
1858
1859 static const char *                     /* O - Keychain path */
1860 http_cdsa_default_path(char   *buffer,  /* I - Path buffer */
1861                        size_t bufsize)  /* I - Size of buffer */
1862 {
1863   const char *home = getenv("HOME");    /* HOME environment variable */
1864
1865
1866  /*
1867   * Determine the default keychain path.  Note that the login and system
1868   * keychains are no longer accessible to user applications starting in macOS
1869   * 10.11.4 (!), so we need to create our own keychain just for CUPS.
1870   */
1871
1872   if (getuid() && home)
1873     snprintf(buffer, bufsize, "%s/.cups/ssl.keychain", home);
1874   else
1875     strlcpy(buffer, "/etc/cups/ssl.keychain", bufsize);
1876
1877   DEBUG_printf(("1http_cdsa_default_path: Using default path \"%s\".", buffer));
1878
1879   return (buffer);
1880 }
1881
1882
1883 /*
1884  * 'http_cdsa_open_keychain()' - Open (or create) a keychain.
1885  */
1886
1887 static SecKeychainRef                   /* O - Keychain or NULL */
1888 http_cdsa_open_keychain(
1889     const char *path,                   /* I - Path to keychain */
1890     char       *filename,               /* I - Keychain filename */
1891     size_t     filesize)                /* I - Size of filename buffer */
1892 {
1893   SecKeychainRef        keychain = NULL;/* Temporary keychain */
1894   OSStatus              err;            /* Error code */
1895   Boolean               interaction;    /* Interaction allowed? */
1896   SecKeychainStatus     status = 0;     /* Keychain status */
1897
1898
1899  /*
1900   * Get the keychain filename...
1901   */
1902
1903   if (!path)
1904     path = http_cdsa_default_path(filename, filesize);
1905   else
1906     strlcpy(filename, path, filesize);
1907
1908  /*
1909   * Save the interaction setting and disable while we open the keychain...
1910   */
1911
1912   SecKeychainGetUserInteractionAllowed(&interaction);
1913   SecKeychainSetUserInteractionAllowed(FALSE);
1914
1915   if (access(path, R_OK))
1916   {
1917    /*
1918     * Create a new keychain at the given path...
1919     */
1920
1921     err = SecKeychainCreate(path, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, FALSE, NULL, &keychain);
1922   }
1923   else
1924   {
1925    /*
1926     * Open the existing keychain and unlock as needed...
1927     */
1928
1929     err = SecKeychainOpen(path, &keychain);
1930
1931     if (err == noErr)
1932       err = SecKeychainGetStatus(keychain, &status);
1933
1934     if (err == noErr && !(status & kSecUnlockStateStatus))
1935       err = SecKeychainUnlock(keychain, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, TRUE);
1936   }
1937
1938  /*
1939   * Restore interaction setting...
1940   */
1941
1942   SecKeychainSetUserInteractionAllowed(interaction);
1943
1944  /*
1945   * Release the keychain if we had any errors...
1946   */
1947
1948   if (err != noErr)
1949   {
1950     /* TODO: Set cups last error string */
1951     DEBUG_printf(("4http_cdsa_open_keychain: Unable to open keychain (%d), returning NULL.", (int)err));
1952
1953     if (keychain)
1954     {
1955       CFRelease(keychain);
1956       keychain = NULL;
1957     }
1958   }
1959
1960  /*
1961   * Return the keychain or NULL...
1962   */
1963
1964   return (keychain);
1965 }
1966
1967
1968 /*
1969  * 'http_cdsa_open_system_keychain()' - Open the System keychain.
1970  */
1971
1972 static SecKeychainRef
1973 http_cdsa_open_system_keychain(void)
1974 {
1975   SecKeychainRef        keychain = NULL;/* Temporary keychain */
1976   OSStatus              err;            /* Error code */
1977   Boolean               interaction;    /* Interaction allowed? */
1978   SecKeychainStatus     status = 0;     /* Keychain status */
1979
1980
1981  /*
1982   * Save the interaction setting and disable while we open the keychain...
1983   */
1984
1985   SecKeychainGetUserInteractionAllowed(&interaction);
1986   SecKeychainSetUserInteractionAllowed(TRUE);
1987
1988   err = SecKeychainOpen("/Library/Keychains/System.keychain", &keychain);
1989
1990   if (err == noErr)
1991     err = SecKeychainGetStatus(keychain, &status);
1992
1993   if (err == noErr && !(status & kSecUnlockStateStatus))
1994     err = errSecInteractionNotAllowed;
1995
1996  /*
1997   * Restore interaction setting...
1998   */
1999
2000   SecKeychainSetUserInteractionAllowed(interaction);
2001
2002  /*
2003   * Release the keychain if we had any errors...
2004   */
2005
2006   if (err != noErr)
2007   {
2008     /* TODO: Set cups last error string */
2009     DEBUG_printf(("4http_cdsa_open_system_keychain: Unable to open keychain (%d), returning NULL.", (int)err));
2010
2011     if (keychain)
2012     {
2013       CFRelease(keychain);
2014       keychain = NULL;
2015     }
2016   }
2017
2018  /*
2019   * Return the keychain or NULL...
2020   */
2021
2022   return (keychain);
2023 }
2024 #endif /* HAVE_SECKEYCHAINOPEN */
2025
2026
2027 /*
2028  * 'http_cdsa_read()' - Read function for the CDSA library.
2029  */
2030
2031 static OSStatus                         /* O  - -1 on error, 0 on success */
2032 http_cdsa_read(
2033     SSLConnectionRef connection,        /* I  - SSL/TLS connection */
2034     void             *data,             /* I  - Data buffer */
2035     size_t           *dataLength)       /* IO - Number of bytes */
2036 {
2037   OSStatus      result;                 /* Return value */
2038   ssize_t       bytes;                  /* Number of bytes read */
2039   http_t        *http;                  /* HTTP connection */
2040
2041
2042   http = (http_t *)connection;
2043
2044   if (!http->blocking)
2045   {
2046    /*
2047     * Make sure we have data before we read...
2048     */
2049
2050     while (!_httpWait(http, http->wait_value, 0))
2051     {
2052       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
2053         continue;
2054
2055       http->error = ETIMEDOUT;
2056       return (-1);
2057     }
2058   }
2059
2060   do
2061   {
2062     bytes = recv(http->fd, data, *dataLength, 0);
2063   }
2064   while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2065
2066   if ((size_t)bytes == *dataLength)
2067   {
2068     result = 0;
2069   }
2070   else if (bytes > 0)
2071   {
2072     *dataLength = (size_t)bytes;
2073     result = errSSLWouldBlock;
2074   }
2075   else
2076   {
2077     *dataLength = 0;
2078
2079     if (bytes == 0)
2080       result = errSSLClosedGraceful;
2081     else if (errno == EAGAIN)
2082       result = errSSLWouldBlock;
2083     else
2084       result = errSSLClosedAbort;
2085   }
2086
2087   return (result);
2088 }
2089
2090
2091 /*
2092  * 'http_cdsa_set_credentials()' - Set the TLS credentials.
2093  */
2094
2095 static int                              /* O - Status of connection */
2096 http_cdsa_set_credentials(http_t *http) /* I - HTTP connection */
2097 {
2098   _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
2099   OSStatus              error = 0;      /* Error code */
2100   http_tls_credentials_t credentials = NULL;
2101                                         /* TLS credentials */
2102
2103
2104   DEBUG_printf(("7http_tls_set_credentials(%p)", (void *)http));
2105
2106  /*
2107   * Prefer connection specific credentials...
2108   */
2109
2110   if ((credentials = http->tls_credentials) == NULL)
2111     credentials = cg->tls_credentials;
2112
2113   if (credentials)
2114   {
2115     error = SSLSetCertificate(http->tls, credentials);
2116     DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
2117                   (int)error));
2118   }
2119   else
2120     DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
2121
2122   return (error);
2123 }
2124
2125
2126 /*
2127  * 'http_cdsa_write()' - Write function for the CDSA library.
2128  */
2129
2130 static OSStatus                         /* O  - -1 on error, 0 on success */
2131 http_cdsa_write(
2132     SSLConnectionRef connection,        /* I  - SSL/TLS connection */
2133     const void       *data,             /* I  - Data buffer */
2134     size_t           *dataLength)       /* IO - Number of bytes */
2135 {
2136   OSStatus      result;                 /* Return value */
2137   ssize_t       bytes;                  /* Number of bytes read */
2138   http_t        *http;                  /* HTTP connection */
2139
2140
2141   http = (http_t *)connection;
2142
2143   do
2144   {
2145     bytes = write(http->fd, data, *dataLength);
2146   }
2147   while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2148
2149   if ((size_t)bytes == *dataLength)
2150   {
2151     result = 0;
2152   }
2153   else if (bytes >= 0)
2154   {
2155     *dataLength = (size_t)bytes;
2156     result = errSSLWouldBlock;
2157   }
2158   else
2159   {
2160     *dataLength = 0;
2161
2162     if (errno == EAGAIN)
2163       result = errSSLWouldBlock;
2164     else
2165       result = errSSLClosedAbort;
2166   }
2167
2168   return (result);
2169 }