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