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