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