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