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