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