• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * TLS support code for CUPS on macOS.
3  *
4  * Copyright © 2021-2023 by OpenPrinting
5  * Copyright © 2007-2021 by Apple Inc.
6  * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
9  * information.
10  */
11 
12 /**** This file is included from tls.c ****/
13 
14 /*
15  * Include necessary headers...
16  */
17 
18 #include <spawn.h>
19 #include "tls-darwin.h"
20 
21 /*
22  * Constants, very secure stuff...
23  */
24 
25 #define _CUPS_CDSA_PASSWORD	"42"	/* CUPS keychain password */
26 #define _CUPS_CDSA_PASSLEN	2	/* Length of keychain password */
27 
28 
29 /*
30  * Local globals...
31  */
32 
33 static int		tls_auto_create = 0;
34 					/* Auto-create self-signed certs? */
35 static char		*tls_common_name = NULL;
36 					/* Default common name */
37 #if TARGET_OS_OSX
38 static int		tls_cups_keychain = 0;
39 					/* Opened the CUPS keychain? */
40 static SecKeychainRef	tls_keychain = NULL;
41 					/* Server cert keychain */
42 #else
43 static SecIdentityRef	tls_selfsigned = NULL;
44 					/* Temporary self-signed cert */
45 #endif /* TARGET_OS_OSX */
46 static char		*tls_keypath = NULL;
47 					/* Server cert keychain path */
48 static _cups_mutex_t	tls_mutex = _CUPS_MUTEX_INITIALIZER;
49 					/* Mutex for keychain/certs */
50 static int		tls_options = -1,/* Options for TLS connections */
51 			tls_min_version = _HTTP_TLS_1_0,
52 			tls_max_version = _HTTP_TLS_MAX;
53 
54 
55 /*
56  * Local functions...
57  */
58 
59 static CFArrayRef	http_cdsa_copy_server(const char *common_name);
60 static SecCertificateRef http_cdsa_create_credential(http_credential_t *credential);
61 #if TARGET_OS_OSX
62 static const char	*http_cdsa_default_path(char *buffer, size_t bufsize);
63 static SecKeychainRef	http_cdsa_open_keychain(const char *path, char *filename, size_t filesize);
64 static SecKeychainRef	http_cdsa_open_system_keychain(void);
65 #endif /* TARGET_OS_OSX */
66 static OSStatus		http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
67 static int		http_cdsa_set_credentials(http_t *http);
68 static OSStatus		http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
69 
70 
71 /*
72  * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
73  *
74  * @since CUPS 2.0/OS 10.10@
75  */
76 
77 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)78 cupsMakeServerCredentials(
79     const char *path,			/* I - Keychain path or @code NULL@ for default */
80     const char *common_name,		/* I - Common name */
81     int        num_alt_names,		/* I - Number of subject alternate names */
82     const char **alt_names,		/* I - Subject Alternate Names */
83     time_t     expiration_date)		/* I - Expiration date */
84 {
85 #if TARGET_OS_OSX
86   int		pid,			/* Process ID of command */
87 		status,			/* Status of command */
88 		i;			/* Looping var */
89   char		command[1024],		/* Command */
90 		*argv[5],		/* Command-line arguments */
91 		*envp[1000],		/* Environment variables */
92 		days[32],		/* CERTTOOL_EXPIRATION_DAYS env var */
93 		keychain[1024],		/* Keychain argument */
94 		infofile[1024],		/* Type-in information for cert */
95 		filename[1024];		/* Default keychain path */
96   cups_file_t	*fp;			/* Seed/info file */
97 
98 
99   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));
100 
101   (void)num_alt_names;
102   (void)alt_names;
103 
104   if (!path)
105     path = http_cdsa_default_path(filename, sizeof(filename));
106 
107  /*
108   * Run the "certtool" command to generate a self-signed certificate...
109   */
110 
111   if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
112     return (0);
113 
114  /*
115   * Create a file with the certificate information fields...
116   *
117   * Note: This assumes that the default questions are asked by the certtool
118   * command...
119   */
120 
121  if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
122     return (0);
123 
124   cupsFilePrintf(fp,
125 		 "CUPS Self-Signed Certificate\n"
126 					/* Enter key and certificate label */
127 		 "r\n"			/* Generate RSA key pair */
128 		 "2048\n"		/* 2048 bit encryption key */
129 		 "y\n"			/* OK (y = yes) */
130 		 "b\n"			/* Usage (b=signing/encryption) */
131 		 "2\n"			/* Sign with SHA256 */
132 		 "y\n"			/* OK (y = yes) */
133 		 "%s\n"			/* Common name */
134 		 "\n"			/* Country (default) */
135 		 "\n"			/* Organization (default) */
136 		 "\n"			/* Organizational unit (default) */
137 		 "\n"			/* State/Province (default) */
138 		 "\n"			/* Email address */
139 		 "y\n",			/* OK (y = yes) */
140 		 common_name);
141   cupsFileClose(fp);
142 
143   snprintf(keychain, sizeof(keychain), "k=%s", path);
144 
145   argv[0] = "certtool";
146   argv[1] = "c";
147   argv[2] = keychain;
148   argv[3] = NULL;
149 
150   snprintf(days, sizeof(days), "CERTTOOL_EXPIRATION_DAYS=%d", (int)((expiration_date - time(NULL) + 86399) / 86400));
151   envp[0] = days;
152   for (i = 0; i < (int)(sizeof(envp) / sizeof(envp[0]) - 2) && environ[i]; i ++)
153     envp[i + 1] = environ[i];
154   envp[i] = NULL;
155 
156   posix_spawn_file_actions_t actions;	/* File actions */
157 
158   posix_spawn_file_actions_init(&actions);
159   posix_spawn_file_actions_addclose(&actions, 0);
160   posix_spawn_file_actions_addopen(&actions, 0, infofile, O_RDONLY, 0);
161   posix_spawn_file_actions_addclose(&actions, 1);
162   posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0);
163   posix_spawn_file_actions_addclose(&actions, 2);
164   posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0);
165 
166   if (posix_spawn(&pid, command, &actions, NULL, argv, envp))
167   {
168     unlink(infofile);
169     return (0);
170   }
171 
172   posix_spawn_file_actions_destroy(&actions);
173 
174   unlink(infofile);
175 
176   while (waitpid(pid, &status, 0) < 0)
177     if (errno != EINTR)
178     {
179       return (0);
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[] = { kSecCSRBasicConstraintsPathLen, 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     _cupsMutexUnlock(&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   CFIndex		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 %ld certificates.", (long)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 %ld name is \"%s\".", (long)i, name));
466 #endif /* DEBUG */
467 
468 	if ((data = SecCertificateCopyData(secCert)) != NULL)
469 	{
470 	  DEBUG_printf(("2httpCopyCredentials: Adding %ld byte certificate blob.", (long)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 (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     CFRelease(credentials);
905 }
906 
907 
908 /*
909  * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
910  *
911  * @since CUPS 2.0/OS 10.10@
912  */
913 
914 int					/* O - 0 on success, -1 on error */
httpLoadCredentials(const char * path,cups_array_t ** credentials,const char * common_name)915 httpLoadCredentials(
916     const char   *path,			/* I  - Keychain path or @code NULL@ for default */
917     cups_array_t **credentials,		/* IO - Credentials */
918     const char   *common_name)		/* I  - Common name for credentials */
919 {
920   OSStatus		err;		/* Error info */
921 #if TARGET_OS_OSX
922   char			filename[1024];	/* Filename for keychain */
923   SecKeychainRef	keychain = NULL,/* Keychain reference */
924 			syschain = NULL;/* System keychain */
925   CFArrayRef		list;		/* Keychain list */
926 #endif /* TARGET_OS_OSX */
927   SecCertificateRef	cert = NULL;	/* Certificate */
928   CFDataRef		data;		/* Certificate data */
929   SecPolicyRef		policy = NULL;	/* Policy ref */
930   CFStringRef		cfcommon_name = NULL;
931 					/* Server name */
932   CFMutableDictionaryRef query = NULL;	/* Query qualifiers */
933 
934 
935   DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
936 
937   if (!credentials)
938     return (-1);
939 
940   *credentials = NULL;
941 
942 #if TARGET_OS_OSX
943   keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
944 
945   if (!keychain)
946     goto cleanup;
947 
948   syschain = http_cdsa_open_system_keychain();
949 
950 #else
951   if (path)
952     return (-1);
953 #endif /* TARGET_OS_OSX */
954 
955   cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
956 
957   policy = SecPolicyCreateSSL(1, cfcommon_name);
958 
959   if (cfcommon_name)
960     CFRelease(cfcommon_name);
961 
962   if (!policy)
963     goto cleanup;
964 
965   if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
966     goto cleanup;
967 
968   CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
969   CFDictionaryAddValue(query, kSecMatchPolicy, policy);
970   CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
971   CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
972 
973 #if TARGET_OS_OSX
974   if (syschain)
975   {
976     const void *values[2] = { syschain, keychain };
977 
978     list = CFArrayCreate(kCFAllocatorDefault, (const void **)values, 2, &kCFTypeArrayCallBacks);
979   }
980   else
981     list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
982   CFDictionaryAddValue(query, kSecMatchSearchList, list);
983   CFRelease(list);
984 #endif /* TARGET_OS_OSX */
985 
986   err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
987 
988   if (err)
989     goto cleanup;
990 
991   if (CFGetTypeID(cert) != SecCertificateGetTypeID())
992     goto cleanup;
993 
994   if ((data = SecCertificateCopyData(cert)) != NULL)
995   {
996     DEBUG_printf(("1httpLoadCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
997 
998     *credentials = cupsArrayNew(NULL, NULL);
999     httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
1000     CFRelease(data);
1001   }
1002 
1003   cleanup :
1004 
1005 #if TARGET_OS_OSX
1006   if (keychain)
1007     CFRelease(keychain);
1008 
1009   if (syschain)
1010     CFRelease(syschain);
1011 #endif /* TARGET_OS_OSX */
1012   if (cert)
1013     CFRelease(cert);
1014   if (policy)
1015     CFRelease(policy);
1016   if (query)
1017     CFRelease(query);
1018 
1019   DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
1020 
1021   return (*credentials ? 0 : -1);
1022 }
1023 
1024 
1025 /*
1026  * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
1027  *
1028  * @since CUPS 2.0/OS 10.10@
1029  */
1030 
1031 int					/* O - -1 on error, 0 on success */
httpSaveCredentials(const char * path,cups_array_t * credentials,const char * common_name)1032 httpSaveCredentials(
1033     const char   *path,			/* I - Keychain path or @code NULL@ for default */
1034     cups_array_t *credentials,		/* I - Credentials */
1035     const char   *common_name)		/* I - Common name for credentials */
1036 {
1037   int			ret = 0;	/* Return value */
1038   OSStatus		err;		/* Error info */
1039 #if TARGET_OS_OSX
1040   char			filename[1024];	/* Filename for keychain */
1041   SecKeychainRef	keychain = NULL;/* Keychain reference */
1042   CFArrayRef		list;		/* Keychain list */
1043 #endif /* TARGET_OS_OSX */
1044   SecCertificateRef	cert = NULL;	/* Certificate */
1045   CFMutableDictionaryRef attrs = NULL;	/* Attributes for add */
1046 
1047 
1048   DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
1049   if (!credentials)
1050   {
1051     DEBUG_puts("1httpSaveCredentials: No credentials, returning -1.");
1052     return (-1);
1053   }
1054 
1055   if (!httpCredentialsAreValidForName(credentials, common_name))
1056   {
1057     DEBUG_puts("1httpSaveCredentials: Common name does not match.");
1058     return (-1);
1059   }
1060 
1061   if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
1062   {
1063     DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
1064     goto cleanup;
1065   }
1066 
1067 #if TARGET_OS_OSX
1068   keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
1069 
1070   if (!keychain)
1071   {
1072     DEBUG_puts("1httpSaveCredentials: No keychain, returning -1.");
1073     return (-1);
1074   }
1075 
1076 #else
1077   if (path)
1078   {
1079     DEBUG_puts("1httpSaveCredentials: No path, returning -1.");
1080     return (-1);
1081   }
1082 #endif /* TARGET_OS_OSX */
1083 
1084   if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
1085   {
1086     DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
1087     goto cleanup;
1088   }
1089 
1090   CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
1091   CFDictionaryAddValue(attrs, kSecValueRef, cert);
1092 
1093 #if TARGET_OS_OSX
1094   if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL)
1095   {
1096     DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains.");
1097     ret = -1;
1098     goto cleanup;
1099   }
1100   CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
1101   CFRelease(list);
1102 #endif /* TARGET_OS_OSX */
1103 
1104   /* Note: SecItemAdd consumes "attrs"... */
1105   if((err = SecItemAdd(attrs, NULL)) != 0)
1106   {
1107     DEBUG_printf(("1httpSaveCredentials: SecItemAdd failed, returned %d.", (int)err));
1108     ret = -1;
1109   }
1110 
1111   cleanup :
1112 
1113 #if TARGET_OS_OSX
1114   CFRelease(keychain);
1115 #endif /* TARGET_OS_OSX */
1116   CFRelease(cert);
1117 
1118   DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
1119 
1120   return (ret);
1121 }
1122 
1123 
1124 /*
1125  * '_httpTLSInitialize()' - Initialize the TLS stack.
1126  */
1127 
1128 void
_httpTLSInitialize(void)1129 _httpTLSInitialize(void)
1130 {
1131  /*
1132   * Nothing to do...
1133   */
1134 }
1135 
1136 
1137 /*
1138  * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
1139  */
1140 
1141 size_t
_httpTLSPending(http_t * http)1142 _httpTLSPending(http_t *http)		/* I - HTTP connection */
1143 {
1144   size_t bytes;				/* Bytes that are available */
1145 
1146 
1147   if (!SSLGetBufferedReadSize(http->tls, &bytes))
1148     return (bytes);
1149 
1150   return (0);
1151 }
1152 
1153 
1154 /*
1155  * '_httpTLSRead()' - Read from a SSL/TLS connection.
1156  */
1157 
1158 int					/* O - Bytes read */
_httpTLSRead(http_t * http,char * buf,int len)1159 _httpTLSRead(http_t *http,		/* I - HTTP connection */
1160 	      char   *buf,		/* I - Buffer to store data */
1161 	      int    len)		/* I - Length of buffer */
1162 {
1163   int		result;			/* Return value */
1164   OSStatus	error;			/* Error info */
1165   size_t	processed;		/* Number of bytes processed */
1166 
1167 
1168   error = SSLRead(http->tls, buf, (size_t)len, &processed);
1169   DEBUG_printf(("5_httpTLSRead: error=%d, processed=%d", (int)error, (int)processed));
1170   switch (error)
1171   {
1172     case 0 :
1173 	result = (int)processed;
1174 	break;
1175 
1176     case errSSLWouldBlock :
1177 	if (processed)
1178 	  result = (int)processed;
1179 	else
1180 	{
1181 	  result = -1;
1182 	  errno  = EINTR;
1183 	}
1184 	break;
1185 
1186     case errSSLClosedGraceful :
1187     default :
1188 	if (processed)
1189 	  result = (int)processed;
1190 	else
1191 	{
1192 	  result = -1;
1193 	  errno  = EPIPE;
1194 	}
1195 	break;
1196   }
1197 
1198   return (result);
1199 }
1200 
1201 
1202 /*
1203  * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
1204  */
1205 
1206 void
_httpTLSSetOptions(int options,int min_version,int max_version)1207 _httpTLSSetOptions(int options,		/* I - Options */
1208                    int min_version,	/* I - Minimum TLS version */
1209                    int max_version)	/* I - Maximum TLS version */
1210 {
1211   if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
1212   {
1213     tls_options     = options;
1214     tls_min_version = min_version;
1215     tls_max_version = max_version;
1216   }
1217 }
1218 
1219 
1220 /*
1221  * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
1222  */
1223 
1224 int					/* O - 0 on success, -1 on failure */
_httpTLSStart(http_t * http)1225 _httpTLSStart(http_t *http)		/* I - HTTP connection */
1226 {
1227   char			hostname[256],	/* Hostname */
1228 			*hostptr;	/* Pointer into hostname */
1229   _cups_globals_t	*cg = _cupsGlobals();
1230 					/* Pointer to library globals */
1231   OSStatus		error;		/* Error code */
1232   const char		*message = NULL;/* Error message */
1233   char			msgbuf[1024];	/* Error message buffer */
1234   cups_array_t		*credentials;	/* Credentials array */
1235   cups_array_t		*names;		/* CUPS distinguished names */
1236   CFArrayRef		dn_array;	/* CF distinguished names array */
1237   CFIndex		count;		/* Number of credentials */
1238   CFDataRef		data;		/* Certificate data */
1239   int			i;		/* Looping var */
1240   http_credential_t	*credential;	/* Credential data */
1241 
1242 
1243   DEBUG_printf(("3_httpTLSStart(http=%p)", (void *)http));
1244 
1245   if (tls_options < 0)
1246   {
1247     DEBUG_puts("4_httpTLSStart: Setting defaults.");
1248     _cupsSetDefaults();
1249     DEBUG_printf(("4_httpTLSStart: tls_options=%x, tls_min_version=%d, tls_max_version=%d", tls_options, tls_min_version, tls_max_version));
1250   }
1251 
1252 #if TARGET_OS_OSX
1253   if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
1254   {
1255     DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
1256     http->error  = errno = EINVAL;
1257     http->status = HTTP_STATUS_ERROR;
1258     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
1259 
1260     return (-1);
1261   }
1262 #endif /* TARGET_OS_OSX */
1263 
1264   if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
1265   {
1266     DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
1267     http->error  = errno = ENOMEM;
1268     http->status = HTTP_STATUS_ERROR;
1269     _cupsSetHTTPError(HTTP_STATUS_ERROR);
1270 
1271     return (-1);
1272   }
1273 
1274   error = SSLSetConnection(http->tls, http);
1275   DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
1276 
1277   if (!error)
1278   {
1279     error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
1280     DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
1281   }
1282 
1283   if (!error)
1284   {
1285     error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1286                                 true);
1287     DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d", (int)error));
1288   }
1289 
1290   if (!error)
1291   {
1292     static const SSLProtocol protocols[] =	/* Min/max protocol versions */
1293     {
1294       kSSLProtocol3,
1295       kTLSProtocol1,
1296       kTLSProtocol11,
1297       kTLSProtocol12,
1298       kTLSProtocol13
1299     };
1300 
1301     if (tls_min_version < _HTTP_TLS_MAX)
1302     {
1303       error = SSLSetProtocolVersionMin(http->tls, protocols[tls_min_version]);
1304       DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", protocols[tls_min_version], (int)error));
1305     }
1306 
1307     if (!error && tls_max_version < _HTTP_TLS_MAX)
1308     {
1309       error = SSLSetProtocolVersionMax(http->tls, protocols[tls_max_version]);
1310       DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMax(%d), error=%d", protocols[tls_max_version], (int)error));
1311     }
1312   }
1313 
1314   if (!error)
1315   {
1316     SSLCipherSuite	supported[100];	/* Supported cipher suites */
1317     size_t		num_supported;	/* Number of supported cipher suites */
1318     SSLCipherSuite	enabled[100];	/* Cipher suites to enable */
1319     size_t		num_enabled;	/* Number of cipher suites to enable */
1320 
1321     num_supported = sizeof(supported) / sizeof(supported[0]);
1322     error         = SSLGetSupportedCiphers(http->tls, supported, &num_supported);
1323 
1324     if (!error)
1325     {
1326       DEBUG_printf(("4_httpTLSStart: %d cipher suites supported.", (int)num_supported));
1327 
1328       for (i = 0, num_enabled = 0; i < (int)num_supported && num_enabled < (sizeof(enabled) / sizeof(enabled[0])); i ++)
1329       {
1330         switch (supported[i])
1331 	{
1332 	  /* Obviously insecure cipher suites that we never want to use */
1333 	  case SSL_NULL_WITH_NULL_NULL :
1334 	  case SSL_RSA_WITH_NULL_MD5 :
1335 	  case SSL_RSA_WITH_NULL_SHA :
1336 	  case SSL_RSA_EXPORT_WITH_RC4_40_MD5 :
1337 	  case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 :
1338 	  case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA :
1339 	  case SSL_RSA_WITH_DES_CBC_SHA :
1340 	  case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA :
1341 	  case SSL_DH_DSS_WITH_DES_CBC_SHA :
1342 	  case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA :
1343 	  case SSL_DH_RSA_WITH_DES_CBC_SHA :
1344 	  case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA :
1345 	  case SSL_DHE_DSS_WITH_DES_CBC_SHA :
1346 	  case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA :
1347 	  case SSL_DHE_RSA_WITH_DES_CBC_SHA :
1348 	  case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 :
1349 	  case SSL_DH_anon_WITH_RC4_128_MD5 :
1350 	  case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA :
1351 	  case SSL_DH_anon_WITH_DES_CBC_SHA :
1352 	  case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA :
1353 	  case SSL_FORTEZZA_DMS_WITH_NULL_SHA :
1354 	  case TLS_DH_anon_WITH_AES_128_CBC_SHA :
1355 	  case TLS_DH_anon_WITH_AES_256_CBC_SHA :
1356 	  case TLS_ECDH_ECDSA_WITH_NULL_SHA :
1357 	  case TLS_ECDHE_RSA_WITH_NULL_SHA :
1358 	  case TLS_ECDH_anon_WITH_NULL_SHA :
1359 	  case TLS_ECDH_anon_WITH_RC4_128_SHA :
1360 	  case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA :
1361 	  case TLS_ECDH_anon_WITH_AES_128_CBC_SHA :
1362 	  case TLS_ECDH_anon_WITH_AES_256_CBC_SHA :
1363 	  case TLS_RSA_WITH_NULL_SHA256 :
1364 	  case TLS_DH_anon_WITH_AES_128_CBC_SHA256 :
1365 	  case TLS_DH_anon_WITH_AES_256_CBC_SHA256 :
1366 	  case TLS_PSK_WITH_NULL_SHA :
1367 	  case TLS_DHE_PSK_WITH_NULL_SHA :
1368 	  case TLS_RSA_PSK_WITH_NULL_SHA :
1369 	  case TLS_DH_anon_WITH_AES_128_GCM_SHA256 :
1370 	  case TLS_DH_anon_WITH_AES_256_GCM_SHA384 :
1371 	  case TLS_PSK_WITH_NULL_SHA256 :
1372 	  case TLS_PSK_WITH_NULL_SHA384 :
1373 	  case TLS_DHE_PSK_WITH_NULL_SHA256 :
1374 	  case TLS_DHE_PSK_WITH_NULL_SHA384 :
1375 	  case TLS_RSA_PSK_WITH_NULL_SHA256 :
1376 	  case TLS_RSA_PSK_WITH_NULL_SHA384 :
1377 	  case SSL_RSA_WITH_DES_CBC_MD5 :
1378 	      DEBUG_printf(("4_httpTLSStart: Excluding insecure cipher suite %d", supported[i]));
1379 	      break;
1380 
1381           /* RC4 cipher suites that should only be used as a last resort */
1382 	  case SSL_RSA_WITH_RC4_128_MD5 :
1383 	  case SSL_RSA_WITH_RC4_128_SHA :
1384 	  case TLS_ECDH_ECDSA_WITH_RC4_128_SHA :
1385 	  case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA :
1386 	  case TLS_ECDH_RSA_WITH_RC4_128_SHA :
1387 	  case TLS_ECDHE_RSA_WITH_RC4_128_SHA :
1388 	  case TLS_PSK_WITH_RC4_128_SHA :
1389 	  case TLS_DHE_PSK_WITH_RC4_128_SHA :
1390 	  case TLS_RSA_PSK_WITH_RC4_128_SHA :
1391 	      if (tls_options & _HTTP_TLS_ALLOW_RC4)
1392 	        enabled[num_enabled ++] = supported[i];
1393 	      else
1394 		DEBUG_printf(("4_httpTLSStart: Excluding RC4 cipher suite %d", supported[i]));
1395 	      break;
1396 
1397           /* DH/DHE cipher suites that are problematic with parameters < 1024 bits */
1398           case TLS_DH_DSS_WITH_AES_128_CBC_SHA :
1399           case TLS_DH_RSA_WITH_AES_128_CBC_SHA :
1400           case TLS_DHE_DSS_WITH_AES_128_CBC_SHA :
1401           case TLS_DHE_RSA_WITH_AES_128_CBC_SHA :
1402           case TLS_DH_DSS_WITH_AES_256_CBC_SHA :
1403           case TLS_DH_RSA_WITH_AES_256_CBC_SHA :
1404           case TLS_DHE_DSS_WITH_AES_256_CBC_SHA :
1405           case TLS_DHE_RSA_WITH_AES_256_CBC_SHA :
1406           case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA :
1407           case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA :
1408           case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA :
1409           case TLS_DH_DSS_WITH_AES_128_CBC_SHA256 :
1410           case TLS_DH_RSA_WITH_AES_128_CBC_SHA256 :
1411           case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 :
1412           case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 :
1413           case TLS_DH_DSS_WITH_AES_256_CBC_SHA256 :
1414           case TLS_DH_RSA_WITH_AES_256_CBC_SHA256 :
1415           case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 :
1416           case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 :
1417           case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA :
1418           case TLS_DHE_PSK_WITH_AES_128_CBC_SHA :
1419           case TLS_DHE_PSK_WITH_AES_256_CBC_SHA :
1420           case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
1421           case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
1422 	      if (tls_options & _HTTP_TLS_DENY_CBC)
1423 	      {
1424 	        DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
1425 	        break;
1426 	      }
1427 
1428 //          case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 :
1429 //          case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 :
1430           case TLS_DH_RSA_WITH_AES_128_GCM_SHA256 :
1431           case TLS_DH_RSA_WITH_AES_256_GCM_SHA384 :
1432 //          case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 :
1433 //          case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 :
1434           case TLS_DH_DSS_WITH_AES_128_GCM_SHA256 :
1435           case TLS_DH_DSS_WITH_AES_256_GCM_SHA384 :
1436           case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 :
1437           case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 :
1438               if (tls_options & _HTTP_TLS_ALLOW_DH)
1439 	        enabled[num_enabled ++] = supported[i];
1440 	      else
1441 		DEBUG_printf(("4_httpTLSStart: Excluding DH/DHE cipher suite %d", supported[i]));
1442               break;
1443 
1444           case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA :
1445           case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 :
1446           case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 :
1447           case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 :
1448           case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 :
1449           case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 :
1450           case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 :
1451           case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 :
1452           case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 :
1453           case TLS_RSA_WITH_3DES_EDE_CBC_SHA :
1454           case TLS_RSA_WITH_AES_128_CBC_SHA :
1455           case TLS_RSA_WITH_AES_256_CBC_SHA :
1456               if (tls_options & _HTTP_TLS_DENY_CBC)
1457 	      {
1458 	        DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
1459 	        break;
1460 	      }
1461 
1462           /* Anything else we'll assume is "secure" */
1463           default :
1464 	      enabled[num_enabled ++] = supported[i];
1465 	      break;
1466 	}
1467       }
1468 
1469       DEBUG_printf(("4_httpTLSStart: %d cipher suites enabled.", (int)num_enabled));
1470       error = SSLSetEnabledCiphers(http->tls, enabled, num_enabled);
1471     }
1472   }
1473 
1474   if (!error && http->mode == _HTTP_MODE_CLIENT)
1475   {
1476    /*
1477     * Client: set client-side credentials, if any...
1478     */
1479 
1480     if (cg->client_cert_cb)
1481     {
1482       error = SSLSetSessionOption(http->tls,
1483 				  kSSLSessionOptionBreakOnCertRequested, true);
1484       DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, error=%d", (int)error));
1485     }
1486     else
1487     {
1488       error = http_cdsa_set_credentials(http);
1489       DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d", (int)error));
1490     }
1491   }
1492   else if (!error)
1493   {
1494    /*
1495     * Server: find/create a certificate for TLS...
1496     */
1497 
1498     if (http->fields[HTTP_FIELD_HOST])
1499     {
1500      /*
1501       * Use hostname for TLS upgrade...
1502       */
1503 
1504       strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1505     }
1506     else
1507     {
1508      /*
1509       * Resolve hostname from connection address...
1510       */
1511 
1512       http_addr_t	addr;		/* Connection address */
1513       socklen_t		addrlen;	/* Length of address */
1514 
1515       addrlen = sizeof(addr);
1516       if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1517       {
1518 	DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
1519 	hostname[0] = '\0';
1520       }
1521       else if (httpAddrLocalhost(&addr))
1522 	hostname[0] = '\0';
1523       else
1524       {
1525 	httpAddrLookup(&addr, hostname, sizeof(hostname));
1526         DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1527       }
1528     }
1529 
1530     if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1531       hostname[0] = '\0';		/* Don't allow numeric addresses */
1532 
1533     _cupsMutexLock(&tls_mutex);
1534 
1535     if (hostname[0])
1536       http->tls_credentials = http_cdsa_copy_server(hostname);
1537     else if (tls_common_name)
1538       http->tls_credentials = http_cdsa_copy_server(tls_common_name);
1539 
1540     if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name))
1541     {
1542       DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1543 
1544       if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1545       {
1546 	DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
1547 	http->error  = errno = EINVAL;
1548 	http->status = HTTP_STATUS_ERROR;
1549 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1550 	_cupsMutexUnlock(&tls_mutex);
1551 
1552 	return (-1);
1553       }
1554 
1555       http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name);
1556     }
1557 
1558     _cupsMutexUnlock(&tls_mutex);
1559 
1560     if (!http->tls_credentials)
1561     {
1562       DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
1563       http->error  = errno = EINVAL;
1564       http->status = HTTP_STATUS_ERROR;
1565       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1);
1566 
1567       return (-1);
1568     }
1569 
1570     error = SSLSetCertificate(http->tls, http->tls_credentials);
1571 
1572     DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
1573   }
1574 
1575   DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", (void *)http->tls_credentials));
1576 
1577  /*
1578   * Let the server know which hostname/domain we are trying to connect to
1579   * in case it wants to serve up a certificate with a matching common name.
1580   */
1581 
1582   if (!error && http->mode == _HTTP_MODE_CLIENT)
1583   {
1584    /*
1585     * Client: get the hostname to use for TLS...
1586     */
1587 
1588     if (httpAddrLocalhost(http->hostaddr))
1589     {
1590       strlcpy(hostname, "localhost", sizeof(hostname));
1591     }
1592     else
1593     {
1594      /*
1595       * Otherwise make sure the hostname we have does not end in a trailing dot.
1596       */
1597 
1598       strlcpy(hostname, http->hostname, sizeof(hostname));
1599       if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1600 	  *hostptr == '.')
1601 	*hostptr = '\0';
1602     }
1603 
1604     error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1605 
1606     DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
1607   }
1608 
1609   if (!error)
1610   {
1611     int			done = 0;	/* Are we done yet? */
1612     double		old_timeout;	/* Old timeout value */
1613     http_timeout_cb_t	old_cb;		/* Old timeout callback */
1614     void		*old_data;	/* Old timeout data */
1615 
1616    /*
1617     * Enforce a minimum timeout of 10 seconds for the TLS handshake...
1618     */
1619 
1620     old_timeout  = http->timeout_value;
1621     old_cb       = http->timeout_cb;
1622     old_data     = http->timeout_data;
1623 
1624     if (!old_cb || old_timeout < 10.0)
1625     {
1626       DEBUG_puts("4_httpTLSStart: Setting timeout to 10 seconds.");
1627       httpSetTimeout(http, 10.0, NULL, NULL);
1628     }
1629 
1630    /*
1631     * Do the TLS handshake...
1632     */
1633 
1634     while (!error && !done)
1635     {
1636       error = SSLHandshake(http->tls);
1637 
1638       DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
1639 
1640       switch (error)
1641       {
1642 	case noErr :
1643 	    done = 1;
1644 	    break;
1645 
1646 	case errSSLWouldBlock :
1647 	    error = noErr;		/* Force a retry */
1648 	    usleep(1000);		/* in 1 millisecond */
1649 	    break;
1650 
1651 	case errSSLServerAuthCompleted :
1652 	    error = 0;
1653 	    if (cg->server_cert_cb)
1654 	    {
1655 	      error = httpCopyCredentials(http, &credentials);
1656 	      if (!error)
1657 	      {
1658 		error = (cg->server_cert_cb)(http, http->tls, credentials,
1659 					     cg->server_cert_data);
1660 		httpFreeCredentials(credentials);
1661 	      }
1662 
1663 	      DEBUG_printf(("4_httpTLSStart: Server certificate callback returned %d.", (int)error));
1664 	    }
1665 	    break;
1666 
1667 	case errSSLClientCertRequested :
1668 	    error = 0;
1669 
1670 	    if (cg->client_cert_cb)
1671 	    {
1672 	      names = NULL;
1673         error = SSLCopyDistinguishedNames(http->tls, &dn_array);
1674         if (dn_array)
1675         {
1676           if (!error) {
1677 		if ((names = cupsArrayNew(NULL, NULL)) != NULL)
1678 		{
1679 		  for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
1680 		  {
1681 		    data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
1682 
1683 		    if ((credential = malloc(sizeof(*credential))) != NULL)
1684 		    {
1685 		      credential->datalen = (size_t)CFDataGetLength(data);
1686 		      if ((credential->data = malloc(credential->datalen)))
1687 		      {
1688 			memcpy((void *)credential->data, CFDataGetBytePtr(data),
1689 			       credential->datalen);
1690 			cupsArrayAdd(names, credential);
1691 		      }
1692 		      else
1693 		        free(credential);
1694 		    }
1695 		  }
1696 		}
1697 	      }
1698 		CFRelease(dn_array);
1699         }
1700 
1701 	      if (!error)
1702 	      {
1703 		error = (cg->client_cert_cb)(http, http->tls, names,
1704 					     cg->client_cert_data);
1705 
1706 		DEBUG_printf(("4_httpTLSStart: Client certificate callback returned %d.", (int)error));
1707 	      }
1708 
1709 	      httpFreeCredentials(names);
1710 	    }
1711 	    break;
1712 
1713 	case errSSLUnknownRootCert :
1714 	    message = _("Unable to establish a secure connection to host (untrusted certificate).");
1715 	    break;
1716 
1717 	case errSSLNoRootCert :
1718 	    message = _("Unable to establish a secure connection to host (self-signed certificate).");
1719 	    break;
1720 
1721 	case errSSLCertExpired :
1722 	    message = _("Unable to establish a secure connection to host (expired certificate).");
1723 	    break;
1724 
1725 	case errSSLCertNotYetValid :
1726 	    message = _("Unable to establish a secure connection to host (certificate not yet valid).");
1727 	    break;
1728 
1729 	case errSSLHostNameMismatch :
1730 	    message = _("Unable to establish a secure connection to host (host name mismatch).");
1731 	    break;
1732 
1733 	case errSSLXCertChainInvalid :
1734 	    message = _("Unable to establish a secure connection to host (certificate chain invalid).");
1735 	    break;
1736 
1737 	case errSSLConnectionRefused :
1738 	    message = _("Unable to establish a secure connection to host (peer dropped connection before responding).");
1739 	    break;
1740 
1741  	default :
1742 	    break;
1743       }
1744     }
1745 
1746    /*
1747     * Restore the previous timeout settings...
1748     */
1749 
1750     httpSetTimeout(http, old_timeout, old_cb, old_data);
1751   }
1752 
1753   if (error)
1754   {
1755     http->error  = error;
1756     http->status = HTTP_STATUS_ERROR;
1757     errno        = ECONNREFUSED;
1758 
1759     CFRelease(http->tls);
1760     http->tls = NULL;
1761 
1762    /*
1763     * If an error string wasn't set by the callbacks use a generic one...
1764     */
1765 
1766     if (!message)
1767     {
1768       if (!cg->lang_default)
1769         cg->lang_default = cupsLangDefault();
1770 
1771       snprintf(msgbuf, sizeof(msgbuf), _cupsLangString(cg->lang_default, _("Unable to establish a secure connection to host (%d).")), error);
1772       message = msgbuf;
1773     }
1774 
1775     _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
1776 
1777     return (-1);
1778   }
1779 
1780   return (0);
1781 }
1782 
1783 
1784 /*
1785  * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
1786  */
1787 
1788 void
_httpTLSStop(http_t * http)1789 _httpTLSStop(http_t *http)		/* I - HTTP connection */
1790 {
1791   while (SSLClose(http->tls) == errSSLWouldBlock)
1792     usleep(1000);
1793 
1794   CFRelease(http->tls);
1795   http->tls = NULL;
1796 
1797   if (http->tls_credentials)
1798   {
1799     CFRelease(http->tls_credentials);
1800     http->tls_credentials = NULL;
1801   }
1802 }
1803 
1804 
1805 /*
1806  * '_httpTLSWrite()' - Write to a SSL/TLS connection.
1807  */
1808 
1809 int					/* O - Bytes written */
_httpTLSWrite(http_t * http,const char * buf,int len)1810 _httpTLSWrite(http_t     *http,		/* I - HTTP connection */
1811 	       const char *buf,		/* I - Buffer holding data */
1812 	       int        len)		/* I - Length of buffer */
1813 {
1814   ssize_t	result;			/* Return value */
1815   OSStatus	error;			/* Error info */
1816   size_t	processed;		/* Number of bytes processed */
1817 
1818 
1819   DEBUG_printf(("4_httpTLSWrite(http=%p, buf=%p, len=%d)", (void *)http, (void *)buf, len));
1820 
1821   error = SSLWrite(http->tls, buf, (size_t)len, &processed);
1822 
1823   switch (error)
1824   {
1825     case 0 :
1826 	result = (int)processed;
1827 	break;
1828 
1829     case errSSLWouldBlock :
1830 	if (processed)
1831 	{
1832 	  result = (int)processed;
1833 	}
1834 	else
1835 	{
1836 	  result = -1;
1837 	  errno  = EINTR;
1838 	}
1839 	break;
1840 
1841     case errSSLClosedGraceful :
1842     default :
1843 	if (processed)
1844 	{
1845 	  result = (int)processed;
1846 	}
1847 	else
1848 	{
1849 	  result = -1;
1850 	  errno  = EPIPE;
1851 	}
1852 	break;
1853   }
1854 
1855   DEBUG_printf(("5_httpTLSWrite: Returning %d.", (int)result));
1856 
1857   return ((int)result);
1858 }
1859 
1860 
1861 /*
1862  * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
1863  */
1864 
1865 static CFArrayRef			/* O - Array of certificates or NULL */
http_cdsa_copy_server(const char * common_name)1866 http_cdsa_copy_server(
1867     const char *common_name)		/* I - Server's hostname */
1868 {
1869 #if TARGET_OS_OSX
1870   OSStatus		err;		/* Error info */
1871   SecIdentityRef	identity = NULL;/* Identity */
1872   CFArrayRef		certificates = NULL;
1873 					/* Certificate array */
1874   SecPolicyRef		policy = NULL;	/* Policy ref */
1875   CFStringRef		cfcommon_name = NULL;
1876 					/* Server name */
1877   CFMutableDictionaryRef query = NULL;	/* Query qualifiers */
1878   CFArrayRef		list = NULL;	/* Keychain list */
1879   SecKeychainRef	syschain = NULL;/* System keychain */
1880   SecKeychainStatus	status = 0;	/* Keychain status */
1881 
1882 
1883   DEBUG_printf(("3http_cdsa_copy_server(common_name=\"%s\")", common_name));
1884 
1885   cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
1886 
1887   policy = SecPolicyCreateSSL(1, cfcommon_name);
1888 
1889   if (!policy)
1890   {
1891     DEBUG_puts("4http_cdsa_copy_server: Unable to create SSL policy.");
1892     goto cleanup;
1893   }
1894 
1895   if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
1896   {
1897     DEBUG_puts("4http_cdsa_copy_server: Unable to create query dictionary.");
1898     goto cleanup;
1899   }
1900 
1901   _cupsMutexLock(&tls_mutex);
1902 
1903   err = SecKeychainGetStatus(tls_keychain, &status);
1904 
1905   if (err == noErr && !(status & kSecUnlockStateStatus) && tls_cups_keychain)
1906     SecKeychainUnlock(tls_keychain, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, TRUE);
1907 
1908   CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
1909   CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1910   CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1911   CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
1912 
1913   syschain = http_cdsa_open_system_keychain();
1914 
1915   if (syschain)
1916   {
1917     const void *values[2] = { syschain, tls_keychain };
1918 
1919     list = CFArrayCreate(kCFAllocatorDefault, (const void **)values, 2, &kCFTypeArrayCallBacks);
1920   }
1921   else
1922     list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
1923 
1924   CFDictionaryAddValue(query, kSecMatchSearchList, list);
1925   CFRelease(list);
1926 
1927   err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
1928 
1929   _cupsMutexUnlock(&tls_mutex);
1930 
1931   if (err != noErr)
1932   {
1933     DEBUG_printf(("4http_cdsa_copy_server: SecItemCopyMatching failed with status %d.", (int)err));
1934     goto cleanup;
1935   }
1936 
1937   if (CFGetTypeID(identity) != SecIdentityGetTypeID())
1938   {
1939     DEBUG_puts("4http_cdsa_copy_server: Search returned something that is not an identity.");
1940     goto cleanup;
1941   }
1942 
1943   if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
1944   {
1945     DEBUG_puts("4http_cdsa_copy_server: Unable to create array of certificates.");
1946     goto cleanup;
1947   }
1948 
1949   cleanup :
1950 
1951   if (syschain)
1952     CFRelease(syschain);
1953   if (identity)
1954     CFRelease(identity);
1955   if (policy)
1956     CFRelease(policy);
1957   if (cfcommon_name)
1958     CFRelease(cfcommon_name);
1959   if (query)
1960     CFRelease(query);
1961 
1962   DEBUG_printf(("4http_cdsa_copy_server: Returning %p.", (void *)certificates));
1963 
1964   return (certificates);
1965 #else
1966 
1967   (void)common_name;
1968 
1969   if (!tls_selfsigned)
1970     return (NULL);
1971 
1972   return (CFArrayCreate(NULL, (const void **)&tls_selfsigned, 1, &kCFTypeArrayCallBacks));
1973 #endif /* TARGET_OS_OSX */
1974 }
1975 
1976 
1977 /*
1978  * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
1979  */
1980 
1981 static SecCertificateRef			/* O - Certificate */
http_cdsa_create_credential(http_credential_t * credential)1982 http_cdsa_create_credential(
1983     http_credential_t *credential)		/* I - Credential */
1984 {
1985   SecCertificateRef	cert;			/* Certificate */
1986   CFDataRef		data;			/* Data object */
1987 
1988 
1989   if (!credential)
1990     return (NULL);
1991 
1992   data = CFDataCreate(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen);
1993   cert = SecCertificateCreateWithData(kCFAllocatorDefault, data);
1994   CFRelease(data);
1995 
1996   return (cert);
1997 }
1998 
1999 
2000 #if TARGET_OS_OSX
2001 /*
2002  * 'http_cdsa_default_path()' - Get the default keychain path.
2003  */
2004 
2005 static const char *			/* O - Keychain path */
http_cdsa_default_path(char * buffer,size_t bufsize)2006 http_cdsa_default_path(char   *buffer,	/* I - Path buffer */
2007                        size_t bufsize)	/* I - Size of buffer */
2008 {
2009   _cups_globals_t	*cg = _cupsGlobals();
2010 					/* Pointer to library globals */
2011 
2012 
2013  /*
2014   * Determine the default keychain path.  Note that the login and system
2015   * keychains are no longer accessible to user applications starting in macOS
2016   * 10.11.4 (!), so we need to create our own keychain just for CUPS.
2017   */
2018 
2019   if (cg->home)
2020     snprintf(buffer, bufsize, "%s/.cups/ssl.keychain", cg->home);
2021   else
2022     strlcpy(buffer, "/etc/cups/ssl.keychain", bufsize);
2023 
2024   DEBUG_printf(("1http_cdsa_default_path: Using default path \"%s\".", buffer));
2025 
2026   return (buffer);
2027 }
2028 
2029 
2030 /*
2031  * 'http_cdsa_open_keychain()' - Open (or create) a keychain.
2032  */
2033 
2034 static SecKeychainRef			/* O - Keychain or NULL */
http_cdsa_open_keychain(const char * path,char * filename,size_t filesize)2035 http_cdsa_open_keychain(
2036     const char *path,			/* I - Path to keychain */
2037     char       *filename,		/* I - Keychain filename */
2038     size_t     filesize)		/* I - Size of filename buffer */
2039 {
2040   SecKeychainRef	keychain = NULL;/* Temporary keychain */
2041   OSStatus		err;		/* Error code */
2042   Boolean		interaction;	/* Interaction allowed? */
2043   SecKeychainStatus	status = 0;	/* Keychain status */
2044 
2045 
2046  /*
2047   * Get the keychain filename...
2048   */
2049 
2050   if (!path)
2051   {
2052     path = http_cdsa_default_path(filename, filesize);
2053     tls_cups_keychain = 1;
2054   }
2055   else
2056   {
2057     strlcpy(filename, path, filesize);
2058     tls_cups_keychain = 0;
2059   }
2060 
2061  /*
2062   * Save the interaction setting and disable while we open the keychain...
2063   */
2064 
2065   SecKeychainGetUserInteractionAllowed(&interaction);
2066   SecKeychainSetUserInteractionAllowed(FALSE);
2067 
2068   if (access(path, R_OK) && tls_cups_keychain)
2069   {
2070    /*
2071     * Create a new keychain at the given path...
2072     */
2073 
2074     err = SecKeychainCreate(path, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, FALSE, NULL, &keychain);
2075   }
2076   else
2077   {
2078    /*
2079     * Open the existing keychain and unlock as needed...
2080     */
2081 
2082     err = SecKeychainOpen(path, &keychain);
2083 
2084     if (err == noErr)
2085       err = SecKeychainGetStatus(keychain, &status);
2086 
2087     if (err == noErr && !(status & kSecUnlockStateStatus) && tls_cups_keychain)
2088       err = SecKeychainUnlock(keychain, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, TRUE);
2089   }
2090 
2091  /*
2092   * Restore interaction setting...
2093   */
2094 
2095   SecKeychainSetUserInteractionAllowed(interaction);
2096 
2097  /*
2098   * Release the keychain if we had any errors...
2099   */
2100 
2101   if (err != noErr)
2102   {
2103     /* TODO: Set cups last error string */
2104     DEBUG_printf(("4http_cdsa_open_keychain: Unable to open keychain (%d), returning NULL.", (int)err));
2105 
2106     if (keychain)
2107     {
2108       CFRelease(keychain);
2109       keychain = NULL;
2110     }
2111   }
2112 
2113  /*
2114   * Return the keychain or NULL...
2115   */
2116 
2117   return (keychain);
2118 }
2119 
2120 
2121 /*
2122  * 'http_cdsa_open_system_keychain()' - Open the System keychain.
2123  */
2124 
2125 static SecKeychainRef
http_cdsa_open_system_keychain(void)2126 http_cdsa_open_system_keychain(void)
2127 {
2128   SecKeychainRef	keychain = NULL;/* Temporary keychain */
2129   OSStatus		err;		/* Error code */
2130   Boolean		interaction;	/* Interaction allowed? */
2131   SecKeychainStatus	status = 0;	/* Keychain status */
2132 
2133 
2134  /*
2135   * Save the interaction setting and disable while we open the keychain...
2136   */
2137 
2138   SecKeychainGetUserInteractionAllowed(&interaction);
2139   SecKeychainSetUserInteractionAllowed(TRUE);
2140 
2141   err = SecKeychainOpen("/Library/Keychains/System.keychain", &keychain);
2142 
2143   if (err == noErr)
2144     err = SecKeychainGetStatus(keychain, &status);
2145 
2146   if (err == noErr && !(status & kSecUnlockStateStatus))
2147     err = errSecInteractionNotAllowed;
2148 
2149  /*
2150   * Restore interaction setting...
2151   */
2152 
2153   SecKeychainSetUserInteractionAllowed(interaction);
2154 
2155  /*
2156   * Release the keychain if we had any errors...
2157   */
2158 
2159   if (err != noErr)
2160   {
2161     /* TODO: Set cups last error string */
2162     DEBUG_printf(("4http_cdsa_open_system_keychain: Unable to open keychain (%d), returning NULL.", (int)err));
2163 
2164     if (keychain)
2165     {
2166       CFRelease(keychain);
2167       keychain = NULL;
2168     }
2169   }
2170 
2171  /*
2172   * Return the keychain or NULL...
2173   */
2174 
2175   return (keychain);
2176 }
2177 #endif /* TARGET_OS_OSX */
2178 
2179 
2180 /*
2181  * 'http_cdsa_read()' - Read function for the CDSA library.
2182  */
2183 
2184 static OSStatus				/* O  - -1 on error, 0 on success */
http_cdsa_read(SSLConnectionRef connection,void * data,size_t * dataLength)2185 http_cdsa_read(
2186     SSLConnectionRef connection,	/* I  - SSL/TLS connection */
2187     void             *data,		/* I  - Data buffer */
2188     size_t           *dataLength)	/* IO - Number of bytes */
2189 {
2190   OSStatus	result;			/* Return value */
2191   ssize_t	bytes;			/* Number of bytes read */
2192   http_t	*http;			/* HTTP connection */
2193 
2194 
2195   http = (http_t *)connection;
2196 
2197   if (!http->blocking || http->timeout_value > 0.0)
2198   {
2199    /*
2200     * Make sure we have data before we read...
2201     */
2202 
2203     while (!_httpWait(http, http->wait_value, 0))
2204     {
2205       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
2206 	continue;
2207 
2208       http->error = ETIMEDOUT;
2209       return (-1);
2210     }
2211   }
2212 
2213   do
2214   {
2215     bytes = recv(http->fd, data, *dataLength, 0);
2216   }
2217   while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2218 
2219   if ((size_t)bytes == *dataLength)
2220   {
2221     result = 0;
2222   }
2223   else if (bytes > 0)
2224   {
2225     *dataLength = (size_t)bytes;
2226     result = errSSLWouldBlock;
2227   }
2228   else
2229   {
2230     *dataLength = 0;
2231 
2232     if (bytes == 0)
2233       result = errSSLClosedGraceful;
2234     else if (errno == EAGAIN)
2235       result = errSSLWouldBlock;
2236     else
2237       result = errSSLClosedAbort;
2238   }
2239 
2240   return (result);
2241 }
2242 
2243 
2244 /*
2245  * 'http_cdsa_set_credentials()' - Set the TLS credentials.
2246  */
2247 
2248 static int				/* O - Status of connection */
http_cdsa_set_credentials(http_t * http)2249 http_cdsa_set_credentials(http_t *http)	/* I - HTTP connection */
2250 {
2251   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
2252   OSStatus		error = 0;	/* Error code */
2253   http_tls_credentials_t credentials = NULL;
2254 					/* TLS credentials */
2255 
2256 
2257   DEBUG_printf(("7http_tls_set_credentials(%p)", (void *)http));
2258 
2259  /*
2260   * Prefer connection specific credentials...
2261   */
2262 
2263   if ((credentials = http->tls_credentials) == NULL)
2264     credentials = cg->tls_credentials;
2265 
2266   if (credentials)
2267   {
2268     error = SSLSetCertificate(http->tls, credentials);
2269     DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
2270 		  (int)error));
2271   }
2272   else
2273     DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
2274 
2275   return (error);
2276 }
2277 
2278 
2279 /*
2280  * 'http_cdsa_write()' - Write function for the CDSA library.
2281  */
2282 
2283 static OSStatus				/* O  - -1 on error, 0 on success */
http_cdsa_write(SSLConnectionRef connection,const void * data,size_t * dataLength)2284 http_cdsa_write(
2285     SSLConnectionRef connection,	/* I  - SSL/TLS connection */
2286     const void       *data,		/* I  - Data buffer */
2287     size_t           *dataLength)	/* IO - Number of bytes */
2288 {
2289   OSStatus	result;			/* Return value */
2290   ssize_t	bytes;			/* Number of bytes read */
2291   http_t	*http;			/* HTTP connection */
2292 
2293 
2294   http = (http_t *)connection;
2295 
2296   do
2297   {
2298     bytes = write(http->fd, data, *dataLength);
2299   }
2300   while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2301 
2302   if ((size_t)bytes == *dataLength)
2303   {
2304     result = 0;
2305   }
2306   else if (bytes >= 0)
2307   {
2308     *dataLength = (size_t)bytes;
2309     result = errSSLWouldBlock;
2310   }
2311   else
2312   {
2313     *dataLength = 0;
2314 
2315     if (errno == EAGAIN)
2316       result = errSSLWouldBlock;
2317     else
2318       result = errSSLClosedAbort;
2319   }
2320 
2321   return (result);
2322 }
2323