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