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