]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-darwin.c
Changelog.
[thirdparty/cups.git] / cups / tls-darwin.c
CommitLineData
2c85b752 1/*
8072030b 2 * TLS support code for CUPS on macOS.
2c85b752 3 *
a7aabde8
MS
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
2c85b752 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
2c85b752
MS
8 */
9
ebb24a07 10/**** This file is included from tls.c ****/
2c85b752
MS
11
12/*
dafebafd 13 * Include necessary headers...
2c85b752
MS
14 */
15
dafebafd
MS
16#include <spawn.h>
17
b3903a94
MS
18extern 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_
31typedef CF_OPTIONS(uint32_t, SecKeyUsage) {
32 kSecKeyUsageAll = 0x7FFFFFFF
33};
34# endif /* !_SECURITY_VERSION_GREATER_THAN_57610_ */
35extern const void * kSecCSRChallengePassword;
36extern const void * kSecSubjectAltName;
37extern const void * kSecCertificateKeyUsage;
38extern const void * kSecCSRBasicContraintsPathLen;
39extern const void * kSecCertificateExtensions;
40extern const void * kSecCertificateExtensionsEncoded;
41extern const void * kSecOidCommonName;
42extern const void * kSecOidCountryName;
43extern const void * kSecOidStateProvinceName;
44extern const void * kSecOidLocalityName;
45extern const void * kSecOidOrganization;
46extern const void * kSecOidOrganizationalUnit;
47extern SecCertificateRef SecCertificateCreateWithBytes(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length);
48extern bool SecCertificateIsValid(SecCertificateRef certificate, CFAbsoluteTime verifyTime);
49extern CFAbsoluteTime SecCertificateNotValidAfter(SecCertificateRef certificate);
50extern SecCertificateRef SecGenerateSelfSignedCertificate(CFArrayRef subject, CFDictionaryRef parameters, SecKeyRef publicKey, SecKeyRef privateKey);
51extern 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 */
dafebafd
MS
62
63
4daf7e97
MS
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
c0459938
MS
72/*
73 * Local globals...
74 */
75
c0459938
MS
76static int tls_auto_create = 0;
77 /* Auto-create self-signed certs? */
78static char *tls_common_name = NULL;
79 /* Default common name */
fc4bbb58 80#ifdef HAVE_SECKEYCHAINOPEN
afd25c34
MS
81static int tls_cups_keychain = 0;
82 /* Opened the CUPS keychain? */
41e0907c
MS
83static SecKeychainRef tls_keychain = NULL;
84 /* Server cert keychain */
2274d26b
MS
85#else
86static SecIdentityRef tls_selfsigned = NULL;
87 /* Temporary self-signed cert */
fc4bbb58 88#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c
MS
89static char *tls_keypath = NULL;
90 /* Server cert keychain path */
91static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER;
92 /* Mutex for keychain/certs */
8f1fbdec
MS
93static int tls_options = -1,/* Options for TLS connections */
94 tls_min_version = _HTTP_TLS_1_0,
95 tls_max_version = _HTTP_TLS_MAX;
c0459938
MS
96
97
dafebafd
MS
98/*
99 * Local functions...
100 */
2c85b752 101
41e0907c 102static CFArrayRef http_cdsa_copy_server(const char *common_name);
2ece34a9 103static SecCertificateRef http_cdsa_create_credential(http_credential_t *credential);
fc4bbb58 104#ifdef HAVE_SECKEYCHAINOPEN
005f7f1f 105static const char *http_cdsa_default_path(char *buffer, size_t bufsize);
4daf7e97
MS
106static SecKeychainRef http_cdsa_open_keychain(const char *path, char *filename, size_t filesize);
107static SecKeychainRef http_cdsa_open_system_keychain(void);
fc4bbb58 108#endif /* HAVE_SECKEYCHAINOPEN */
41e0907c 109static OSStatus http_cdsa_read(SSLConnectionRef connection, void *data, size_t *dataLength);
88f1e9c8 110static int http_cdsa_set_credentials(http_t *http);
41e0907c 111static OSStatus http_cdsa_write(SSLConnectionRef connection, const void *data, size_t *dataLength);
2c85b752
MS
112
113
3af9ac9e
MS
114/*
115 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
116 *
e1f19878 117 * @since CUPS 2.0/OS 10.10@
3af9ac9e
MS
118 */
119
120int /* O - 1 on success, 0 on failure */
121cupsMakeServerCredentials(
f93b32b6 122 const char *path, /* I - Keychain path or @code NULL@ for default */
3af9ac9e
MS
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{
fc4bbb58 128#if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE)
41e0907c
MS
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 */
558883c6 138 SecCertificateRef cert = NULL; /* Self-signed certificate */
41e0907c
MS
139 CFMutableDictionaryRef keyParams = NULL;
140 /* Key generation parameters */
141
142
172bdf5d
MS
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
fc4bbb58 145 (void)path;
3af9ac9e
MS
146 (void)num_alt_names;
147 (void)alt_names;
148 (void)expiration_date;
149
fc4bbb58
MS
150 if (path)
151 {
152 DEBUG_puts("1cupsMakeServerCredentials: No keychain support compiled in, returning 0.");
153 return (0);
154 }
f93b32b6 155
2274d26b
MS
156 if (tls_selfsigned)
157 {
158 DEBUG_puts("1cupsMakeServerCredentials: Using existing self-signed cert.");
159 return (1);
160 }
161
f93b32b6 162 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
41e0907c 163 if (!cfcommon_name)
2274d26b
MS
164 {
165 DEBUG_puts("1cupsMakeServerCredentials: Unable to create CF string of common name.");
41e0907c 166 goto cleanup;
2274d26b 167 }
41e0907c
MS
168
169 /*
170 * Create a public/private key pair...
171 */
172
f93b32b6 173 keyParams = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
41e0907c 174 if (!keyParams)
2274d26b
MS
175 {
176 DEBUG_puts("1cupsMakeServerCredentials: Unable to create key parameters dictionary.");
41e0907c 177 goto cleanup;
2274d26b 178 }
41e0907c 179
1a7059a0 180 CFDictionaryAddValue(keyParams, kSecAttrKeyType, kSecAttrKeyTypeRSA);
41e0907c 181 CFDictionaryAddValue(keyParams, kSecAttrKeySizeInBits, CFSTR("2048"));
2274d26b 182 CFDictionaryAddValue(keyParams, kSecAttrLabel, cfcommon_name);
41e0907c
MS
183
184 err = SecKeyGeneratePair(keyParams, &publicKey, &privateKey);
185 if (err != noErr)
2274d26b
MS
186 {
187 DEBUG_printf(("1cupsMakeServerCredentials: Unable to generate key pair: %d.", (int)err));
41e0907c 188 goto cleanup;
2274d26b 189 }
41e0907c
MS
190
191 /*
192 * Create a self-signed certificate using the public/private key pair...
193 */
194
195 CFIndex usageInt = kSecKeyUsageAll;
558883c6
MS
196 CFNumberRef usage = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &usageInt);
197 CFIndex lenInt = 0;
198 CFNumberRef len = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &lenInt);
fc4bbb58
MS
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);
41e0907c 202 CFRelease(usage);
558883c6 203 CFRelease(len);
41e0907c
MS
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);
fc4bbb58 215
558883c6 216 cert = SecGenerateSelfSignedCertificate(subject, certParams, publicKey, privateKey);
fc4bbb58 217
41e0907c
MS
218 CFRelease(subject);
219 CFRelease(certParams);
220
221 if (!cert)
2274d26b
MS
222 {
223 DEBUG_puts("1cupsMakeServerCredentials: Unable to create self-signed certificate.");
41e0907c 224 goto cleanup;
2274d26b 225 }
41e0907c
MS
226
227 ident = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
228
229 if (ident)
fc4bbb58 230 {
2274d26b
MS
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 */
fc4bbb58
MS
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);
e34d1ec4 246 /* SecItemAdd consumes itemAttrs... */
fc4bbb58 247
2274d26b
MS
248 CFRelease(ident);
249
fc4bbb58 250 if (err != noErr)
2274d26b
MS
251 {
252 DEBUG_printf(("1cupsMakeServerCredentials: Unable to add identity to keychain: %d.", (int)err));
fc4bbb58 253 goto cleanup;
2274d26b
MS
254 }
255# endif /* 0 */
fc4bbb58 256
41e0907c 257 status = 1;
fc4bbb58 258 }
2274d26b
MS
259 else
260 DEBUG_puts("1cupsMakeServerCredentials: Unable to create identity from cert and keys.");
41e0907c
MS
261
262 /*
263 * Cleanup and return...
264 */
265
266cleanup:
267
268 if (cfcommon_name)
269 CFRelease(cfcommon_name);
270
271 if (keyParams)
272 CFRelease(keyParams);
273
41e0907c
MS
274 if (cert)
275 CFRelease(cert);
276
277 if (publicKey)
278 CFRelease(publicKey);
279
280 if (privateKey)
2274d26b
MS
281 CFRelease(privateKey);
282
283 DEBUG_printf(("1cupsMakeServerCredentials: Returning %d.", status));
41e0907c
MS
284
285 return (status);
286
fc4bbb58 287#else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
41e0907c 288 int pid, /* Process ID of command */
7d58a105
MS
289 status, /* Status of command */
290 i; /* Looping var */
41e0907c 291 char command[1024], /* Command */
724f2615 292 *argv[5], /* Command-line arguments */
7d58a105
MS
293 *envp[1000], /* Environment variables */
294 days[32], /* CERTTOOL_EXPIRATION_DAYS env var */
41e0907c 295 keychain[1024], /* Keychain argument */
f93b32b6
MS
296 infofile[1024], /* Type-in information for cert */
297 filename[1024]; /* Default keychain path */
41e0907c
MS
298 cups_file_t *fp; /* Seed/info file */
299
300
807315e6 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));
172bdf5d 302
41e0907c
MS
303 (void)num_alt_names;
304 (void)alt_names;
41e0907c 305
f93b32b6 306 if (!path)
005f7f1f 307 path = http_cdsa_default_path(filename, sizeof(filename));
f93b32b6 308
41e0907c
MS
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 */
724f2615 329 "r\n" /* Generate RSA key pair */
1ae693e3 330 "2048\n" /* 2048 bit encryption key */
41e0907c
MS
331 "y\n" /* OK (y = yes) */
332 "b\n" /* Usage (b=signing/encryption) */
1ae693e3 333 "2\n" /* Sign with SHA256 */
41e0907c
MS
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
7d58a105
MS
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
41e0907c
MS
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);
f93b32b6
MS
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);
41e0907c 367
7d58a105 368 if (posix_spawn(&pid, command, &actions, NULL, argv, envp))
41e0907c
MS
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);
eb66bc71 386#endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
3af9ac9e
MS
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 *
8072030b 396 * @since CUPS 2.0/macOS 10.10@
3af9ac9e
MS
397 */
398
399int /* O - 1 on success, 0 on failure */
400cupsSetServerCredentials(
f93b32b6 401 const char *path, /* I - Keychain path or @code NULL@ for default */
3af9ac9e
MS
402 const char *common_name, /* I - Default common name for server */
403 int auto_create) /* I - 1 = automatically create self-signed certificates */
404{
a27a134a
MS
405 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
406
eb66bc71 407#ifdef HAVE_SECKEYCHAINOPEN
4daf7e97
MS
408 char filename[1024]; /* Keychain filename */
409 SecKeychainRef keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
c0459938 410
4daf7e97 411 if (!keychain)
c0459938 412 {
4daf7e97 413 DEBUG_puts("1cupsSetServerCredentials: Unable to open keychain.");
c0459938
MS
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
41e0907c
MS
426 if (tls_keypath)
427 _cupsStrFree(tls_keypath);
428
c0459938
MS
429 if (tls_common_name)
430 _cupsStrFree(tls_common_name);
431
432 /*
433 * Save the new keychain...
434 */
435
436 tls_keychain = keychain;
4daf7e97 437 tls_keypath = _cupsStrAlloc(filename);
c0459938
MS
438 tls_auto_create = auto_create;
439 tls_common_name = _cupsStrAlloc(common_name);
440
441 _cupsMutexUnlock(&tls_mutex);
442
a27a134a 443 DEBUG_puts("1cupsSetServerCredentials: Opened keychain, returning 1.");
c0459938 444 return (1);
eb66bc71
MS
445
446#else
fc4bbb58
MS
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);
eb66bc71 457#endif /* HAVE_SECKEYCHAINOPEN */
3af9ac9e
MS
458}
459
2c85b752
MS
460
461/*
462 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
463 * an encrypted connection.
464 *
8072030b 465 * @since CUPS 1.5/macOS 10.7@
2c85b752
MS
466 */
467
468int /* O - Status of call (0 = success) */
469httpCopyCredentials(
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
807315e6 481 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", (void *)http, (void *)credentials));
376d7c69 482
2c85b752
MS
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 {
376d7c69
MS
491 DEBUG_printf(("2httpCopyCredentials: Peer provided %d certificates.", (int)SecTrustGetCertificateCount(peerTrust)));
492
2c85b752
MS
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);
376d7c69
MS
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
88f1e9c8 512 if ((data = SecCertificateCopyData(secCert)) != NULL)
2c85b752 513 {
376d7c69
MS
514 DEBUG_printf(("2httpCopyCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
515
7e86f2f6 516 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
2c85b752
MS
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
533http_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 */
2c85b752
MS
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 {
9653cfdf 554 if ((secCert = http_cdsa_create_credential(credential)) != NULL)
2c85b752 555 {
9653cfdf
MS
556 CFArrayAppendValue(peerCerts, secCert);
557 CFRelease(secCert);
2c85b752
MS
558 }
559 }
560
561 return (peerCerts);
562}
563
564
3af9ac9e 565/*
524c65e6 566 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
3af9ac9e 567 *
8072030b 568 * @since CUPS 2.0/macOS 10.10@
3af9ac9e
MS
569 */
570
524c65e6
MS
571int /* O - 1 if valid, 0 otherwise */
572httpCredentialsAreValidForName(
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 *
8072030b 631 * @since CUPS 2.0/macOS 10.10@
524c65e6
MS
632 */
633
634http_trust_t /* O - Level of trust */
635httpCredentialsGetTrust(
376d7c69
MS
636 cups_array_t *credentials, /* I - Credentials */
637 const char *common_name) /* I - Common name for trust lookup */
3af9ac9e 638{
9653cfdf 639 SecCertificateRef secCert; /* Certificate reference */
524c65e6
MS
640 http_trust_t trust = HTTP_TRUST_OK;
641 /* Trusted? */
376d7c69 642 cups_array_t *tcreds = NULL; /* Trusted credentials */
9653cfdf
MS
643 _cups_globals_t *cg = _cupsGlobals();
644 /* Per-thread globals */
3af9ac9e 645
9653cfdf 646
376d7c69 647 if (!common_name)
7aeb3615
MS
648 {
649 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No common name specified."), 1);
524c65e6 650 return (HTTP_TRUST_UNKNOWN);
7aeb3615 651 }
376d7c69 652
9653cfdf 653 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
7aeb3615
MS
654 {
655 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create credentials from array."), 1);
524c65e6 656 return (HTTP_TRUST_UNKNOWN);
7aeb3615 657 }
9653cfdf 658
3abb875b
MS
659 if (cg->any_root < 0)
660 _cupsSetDefaults();
661
376d7c69 662 /*
88f1e9c8 663 * Look this common name up in the default keychains...
376d7c69
MS
664 */
665
88f1e9c8 666 httpLoadCredentials(NULL, &tcreds, common_name);
376d7c69
MS
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
08d56b1f
MS
683 if (!cg->trust_first)
684 {
685 /*
686 * Do not trust certificates on first use...
687 */
688
7aeb3615
MS
689 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
690
08d56b1f
MS
691 trust = HTTP_TRUST_INVALID;
692 }
7aeb3615 693 else if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds))
376d7c69
MS
694 {
695 /*
7aeb3615 696 * The new credentials are not newly issued...
376d7c69
MS
697 */
698
7aeb3615
MS
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
524c65e6 711 trust = HTTP_TRUST_INVALID;
376d7c69 712 }
524c65e6 713 else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
376d7c69
MS
714 {
715 /*
524c65e6 716 * Save the renewed credentials...
376d7c69
MS
717 */
718
524c65e6
MS
719 trust = HTTP_TRUST_RENEWED;
720
721 httpSaveCredentials(NULL, credentials, common_name);
376d7c69
MS
722 }
723 }
724
725 httpFreeCredentials(tcreds);
726 }
f51f3773 727 else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
7aeb3615
MS
728 {
729 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1);
524c65e6 730 trust = HTTP_TRUST_INVALID;
7aeb3615
MS
731 }
732 else if (!cg->trust_first)
733 {
f8e19681
MS
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 }
7aeb3615 774 }
376d7c69 775
7aeb3615
MS
776 if (trust == HTTP_TRUST_OK && !cg->expired_certs && !SecCertificateIsValid(secCert, CFAbsoluteTimeGetCurrent()))
777 {
778 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials have expired."), 1);
524c65e6 779 trust = HTTP_TRUST_EXPIRED;
7aeb3615
MS
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);
08d56b1f 785 trust = HTTP_TRUST_INVALID;
7aeb3615 786 }
376d7c69 787
9653cfdf
MS
788 CFRelease(secCert);
789
524c65e6 790 return (trust);
3af9ac9e
MS
791}
792
793
794/*
795 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
796 *
8072030b 797 * @since CUPS 2.0/macOS 10.10@
3af9ac9e
MS
798 */
799
800time_t /* O - Expiration date of credentials */
801httpCredentialsGetExpiration(
802 cups_array_t *credentials) /* I - Credentials */
803{
9653cfdf
MS
804 SecCertificateRef secCert; /* Certificate reference */
805 time_t expiration; /* Expiration date */
3af9ac9e 806
9653cfdf
MS
807
808 if ((secCert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
809 return (0);
810
376d7c69 811 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
9653cfdf
MS
812
813 CFRelease(secCert);
814
815 return (expiration);
3af9ac9e
MS
816}
817
818
72d05bc9
MS
819/*
820 * 'httpCredentialsString()' - Return a string representing the credentials.
821 *
8072030b 822 * @since CUPS 2.0/macOS 10.10@
72d05bc9
MS
823 */
824
825size_t /* O - Total size of credentials string */
826httpCredentialsString(
827 cups_array_t *credentials, /* I - Credentials */
828 char *buffer, /* I - Buffer or @code NULL@ */
829 size_t bufsize) /* I - Size of buffer */
830{
376d7c69 831 http_credential_t *first; /* First certificate */
9653cfdf 832 SecCertificateRef secCert; /* Certificate reference */
9653cfdf
MS
833
834
807315e6 835 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", (void *)credentials, (void *)buffer, CUPS_LLCAST bufsize));
376d7c69 836
9653cfdf
MS
837 if (!buffer)
838 return (0);
72d05bc9
MS
839
840 if (buffer && bufsize > 0)
841 *buffer = '\0';
842
376d7c69
MS
843 if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
844 (secCert = http_cdsa_create_credential(first)) != NULL)
9653cfdf 845 {
073b3929
MS
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 */
376d7c69 857 time_t expiration; /* Expiration date of cert */
376d7c69
MS
858 unsigned char md5_digest[16]; /* MD5 result */
859
073b3929 860 if (SecCertificateCopyCommonName(secCert, &cf_string) == noErr)
9653cfdf 861 {
073b3929
MS
862 CFStringGetCString(cf_string, commonName, (CFIndex)sizeof(commonName), kCFStringEncodingUTF8);
863 CFRelease(cf_string);
9653cfdf 864 }
376d7c69 865 else
073b3929
MS
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 }
376d7c69
MS
919
920 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
921
7ec11630 922 cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
376d7c69 923
073b3929 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]);
9653cfdf
MS
925
926 CFRelease(secCert);
927 }
928
376d7c69
MS
929 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
930
9653cfdf 931 return (strlen(buffer));
72d05bc9
MS
932}
933
934
2c85b752
MS
935/*
936 * '_httpFreeCredentials()' - Free internal credentials.
937 */
938
939void
940_httpFreeCredentials(
941 http_tls_credentials_t credentials) /* I - Internal credentials */
942{
943 if (!credentials)
944 return;
945
946 CFRelease(credentials);
947}
948
949
72d05bc9
MS
950/*
951 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
952 *
e1f19878 953 * @since CUPS 2.0/OS 10.10@
72d05bc9
MS
954 */
955
dafebafd 956int /* O - 0 on success, -1 on error */
72d05bc9 957httpLoadCredentials(
f93b32b6 958 const char *path, /* I - Keychain path or @code NULL@ for default */
72d05bc9
MS
959 cups_array_t **credentials, /* IO - Credentials */
960 const char *common_name) /* I - Common name for credentials */
961{
dafebafd 962 OSStatus err; /* Error info */
fc4bbb58 963#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8 964 char filename[1024]; /* Filename for keychain */
4daf7e97
MS
965 SecKeychainRef keychain = NULL,/* Keychain reference */
966 syschain = NULL;/* System keychain */
fc4bbb58
MS
967 CFArrayRef list; /* Keychain list */
968#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8
MS
969 SecCertificateRef cert = NULL; /* Certificate */
970 CFDataRef data; /* Certificate data */
dafebafd
MS
971 SecPolicyRef policy = NULL; /* Policy ref */
972 CFStringRef cfcommon_name = NULL;
973 /* Server name */
974 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
dafebafd
MS
975
976
807315e6 977 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
88f1e9c8
MS
978
979 if (!credentials)
980 return (-1);
981
982 *credentials = NULL;
983
fc4bbb58 984#ifdef HAVE_SECKEYCHAINOPEN
4daf7e97 985 keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
88f1e9c8 986
4daf7e97 987 if (!keychain)
dafebafd
MS
988 goto cleanup;
989
4daf7e97
MS
990 syschain = http_cdsa_open_system_keychain();
991
fc4bbb58
MS
992#else
993 if (path)
994 return (-1);
995#endif /* HAVE_SECKEYCHAINOPEN */
996
dafebafd
MS
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
88f1e9c8 1010 CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
dafebafd
MS
1011 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1012 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1013 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
dafebafd 1014
fc4bbb58 1015#ifdef HAVE_SECKEYCHAINOPEN
4daf7e97
MS
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);
fc4bbb58 1024 CFDictionaryAddValue(query, kSecMatchSearchList, list);
dafebafd 1025 CFRelease(list);
fc4bbb58 1026#endif /* HAVE_SECKEYCHAINOPEN */
dafebafd 1027
88f1e9c8 1028 err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
dafebafd
MS
1029
1030 if (err)
1031 goto cleanup;
1032
88f1e9c8 1033 if (CFGetTypeID(cert) != SecCertificateGetTypeID())
dafebafd
MS
1034 goto cleanup;
1035
88f1e9c8
MS
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 }
dafebafd
MS
1044
1045 cleanup :
1046
fc4bbb58 1047#ifdef HAVE_SECKEYCHAINOPEN
dafebafd
MS
1048 if (keychain)
1049 CFRelease(keychain);
4daf7e97
MS
1050
1051 if (syschain)
1052 CFRelease(syschain);
fc4bbb58 1053#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8
MS
1054 if (cert)
1055 CFRelease(cert);
dafebafd
MS
1056 if (policy)
1057 CFRelease(policy);
1058 if (query)
1059 CFRelease(query);
1060
88f1e9c8
MS
1061 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
1062
1063 return (*credentials ? 0 : -1);
72d05bc9
MS
1064}
1065
1066
72d05bc9
MS
1067/*
1068 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
1069 *
e1f19878 1070 * @since CUPS 2.0/OS 10.10@
72d05bc9
MS
1071 */
1072
1073int /* O - -1 on error, 0 on success */
1074httpSaveCredentials(
f93b32b6 1075 const char *path, /* I - Keychain path or @code NULL@ for default */
72d05bc9
MS
1076 cups_array_t *credentials, /* I - Credentials */
1077 const char *common_name) /* I - Common name for credentials */
1078{
88f1e9c8 1079 int ret = -1; /* Return value */
41e0907c 1080 OSStatus err; /* Error info */
fc4bbb58 1081#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8
MS
1082 char filename[1024]; /* Filename for keychain */
1083 SecKeychainRef keychain = NULL;/* Keychain reference */
fc4bbb58
MS
1084 CFArrayRef list; /* Keychain list */
1085#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8 1086 SecCertificateRef cert = NULL; /* Certificate */
88f1e9c8 1087 CFMutableDictionaryRef attrs = NULL; /* Attributes for add */
41e0907c
MS
1088
1089
807315e6 1090 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
88f1e9c8 1091 if (!credentials)
41e0907c
MS
1092 goto cleanup;
1093
524c65e6
MS
1094 if (!httpCredentialsAreValidForName(credentials, common_name))
1095 {
1096 DEBUG_puts("1httpSaveCredentials: Common name does not match.");
1097 return (-1);
1098 }
1099
88f1e9c8
MS
1100 if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
1101 {
1102 DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
41e0907c 1103 goto cleanup;
88f1e9c8 1104 }
41e0907c 1105
fc4bbb58 1106#ifdef HAVE_SECKEYCHAINOPEN
4daf7e97 1107 keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
2c85b752 1108
4daf7e97 1109 if (!keychain)
88f1e9c8 1110 goto cleanup;
2c85b752 1111
fc4bbb58
MS
1112#else
1113 if (path)
1114 return (-1);
1115#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8 1116
88f1e9c8
MS
1117 if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
1118 {
1119 DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
1120 goto cleanup;
2c85b752
MS
1121 }
1122
88f1e9c8 1123 CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
88f1e9c8 1124 CFDictionaryAddValue(attrs, kSecValueRef, cert);
fc4bbb58
MS
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 }
88f1e9c8 1132 CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
fc4bbb58
MS
1133 CFRelease(list);
1134#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752 1135
88f1e9c8 1136 /* Note: SecItemAdd consumes "attrs"... */
524c65e6 1137 err = SecItemAdd(attrs, NULL);
88f1e9c8 1138 DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
2c85b752 1139
88f1e9c8 1140 cleanup :
2c85b752 1141
fc4bbb58 1142#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8
MS
1143 if (keychain)
1144 CFRelease(keychain);
fc4bbb58 1145#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8
MS
1146 if (cert)
1147 CFRelease(cert);
2c85b752 1148
88f1e9c8 1149 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
2c85b752 1150
88f1e9c8 1151 return (ret);
2c85b752
MS
1152}
1153
1154
1155/*
25731360 1156 * '_httpTLSInitialize()' - Initialize the TLS stack.
2c85b752
MS
1157 */
1158
25731360
MS
1159void
1160_httpTLSInitialize(void)
2c85b752
MS
1161{
1162 /*
1163 * Nothing to do...
1164 */
1165}
1166
1167
1168/*
25731360 1169 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
2c85b752
MS
1170 */
1171
25731360
MS
1172size_t
1173_httpTLSPending(http_t *http) /* I - HTTP connection */
2c85b752
MS
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/*
25731360 1186 * '_httpTLSRead()' - Read from a SSL/TLS connection.
2c85b752
MS
1187 */
1188
25731360
MS
1189int /* O - Bytes read */
1190_httpTLSRead(http_t *http, /* I - HTTP connection */
2c85b752
MS
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
7e86f2f6 1199 error = SSLRead(http->tls, buf, (size_t)len, &processed);
25731360 1200 DEBUG_printf(("6_httpTLSRead: error=%d, processed=%d", (int)error,
2c85b752
MS
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
63aefcd5
MS
1234/*
1235 * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
1236 */
1237
1238void
8f1fbdec
MS
1239_httpTLSSetOptions(int options, /* I - Options */
1240 int min_version, /* I - Minimum TLS version */
1241 int max_version) /* I - Maximum TLS version */
63aefcd5 1242{
02c88e67 1243 if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
8f1fbdec
MS
1244 {
1245 tls_options = options;
1246 tls_min_version = min_version;
1247 tls_max_version = max_version;
1248 }
63aefcd5
MS
1249}
1250
1251
2c85b752 1252/*
25731360 1253 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
2c85b752
MS
1254 */
1255
25731360
MS
1256int /* O - 0 on success, -1 on failure */
1257_httpTLSStart(http_t *http) /* I - HTTP connection */
2c85b752
MS
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
807315e6 1274 DEBUG_printf(("3_httpTLSStart(http=%p)", (void *)http));
b37d45d9
MS
1275
1276 if (tls_options < 0)
1277 {
1278 DEBUG_puts("4_httpTLSStart: Setting defaults.");
1279 _cupsSetDefaults();
8f1fbdec 1280 DEBUG_printf(("4_httpTLSStart: tls_options=%x, tls_min_version=%d, tls_max_version=%d", tls_options, tls_min_version, tls_max_version));
b37d45d9 1281 }
2c85b752 1282
c913d726 1283#ifdef HAVE_SECKEYCHAINOPEN
41e0907c 1284 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
2c85b752 1285 {
25731360 1286 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
41e0907c
MS
1287 http->error = errno = EINVAL;
1288 http->status = HTTP_STATUS_ERROR;
1289 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
2c85b752 1290
41e0907c 1291 return (-1);
2c85b752 1292 }
2274d26b 1293#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752 1294
72d05bc9 1295 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
2c85b752 1296 {
25731360 1297 DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
2c85b752
MS
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);
25731360 1306 DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
2c85b752
MS
1307
1308 if (!error)
1309 {
1310 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
25731360 1311 DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
2c85b752
MS
1312 }
1313
1314 if (!error)
1315 {
1316 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1317 true);
63aefcd5
MS
1318 DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d", (int)error));
1319 }
1320
1321 if (!error)
1322 {
8f1fbdec
MS
1323 static const SSLProtocol protocols[] = /* Min/max protocol versions */
1324 {
1325 kSSLProtocol3,
1326 kTLSProtocol1,
1327 kTLSProtocol11,
1328 kTLSProtocol12,
9f4c8267 1329 kTLSProtocol13
8f1fbdec
MS
1330 };
1331
9f4c8267
MS
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 }
8f1fbdec 1337
9f4c8267 1338 if (!error && tls_max_version < _HTTP_TLS_MAX)
4f272af7 1339 {
8f1fbdec
MS
1340 error = SSLSetProtocolVersionMax(http->tls, protocols[tls_max_version]);
1341 DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMax(%d), error=%d", protocols[tls_max_version], (int)error));
4f272af7 1342 }
63aefcd5
MS
1343 }
1344
ee6226a5 1345# if HAVE_SSLSETENABLEDCIPHERS
63aefcd5
MS
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 :
b37d45d9 1410 DEBUG_printf(("4_httpTLSStart: Excluding insecure cipher suite %d", supported[i]));
63aefcd5
MS
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];
b37d45d9
MS
1425 else
1426 DEBUG_printf(("4_httpTLSStart: Excluding RC4 cipher suite %d", supported[i]));
63aefcd5
MS
1427 break;
1428
ee6226a5
MS
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 :
ee6226a5
MS
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 :
f2e87147
MS
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
bdc4056c
MS
1460// case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 :
1461// case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 :
ee6226a5
MS
1462 case TLS_DH_RSA_WITH_AES_128_GCM_SHA256 :
1463 case TLS_DH_RSA_WITH_AES_256_GCM_SHA384 :
bdc4056c
MS
1464// case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 :
1465// case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 :
ee6226a5
MS
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 :
ee6226a5
MS
1470 if (tls_options & _HTTP_TLS_ALLOW_DH)
1471 enabled[num_enabled ++] = supported[i];
b37d45d9
MS
1472 else
1473 DEBUG_printf(("4_httpTLSStart: Excluding DH/DHE cipher suite %d", supported[i]));
ee6226a5
MS
1474 break;
1475
f2e87147
MS
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 :
4f272af7
MS
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 :
f2e87147
MS
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" */
63aefcd5
MS
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 }
2c85b752 1504 }
ee6226a5 1505#endif /* HAVE_SSLSETENABLEDCIPHERS */
2c85b752 1506
41e0907c 1507 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1508 {
41e0907c
MS
1509 /*
1510 * Client: set client-side credentials, if any...
1511 */
1512
2c85b752
MS
1513 if (cg->client_cert_cb)
1514 {
1515 error = SSLSetSessionOption(http->tls,
1516 kSSLSessionOptionBreakOnCertRequested, true);
25731360 1517 DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, "
2c85b752
MS
1518 "error=%d", (int)error));
1519 }
1520 else
1521 {
88f1e9c8
MS
1522 error = http_cdsa_set_credentials(http);
1523 DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d",
2c85b752
MS
1524 (int)error));
1525 }
1526 }
41e0907c
MS
1527 else if (!error)
1528 {
1529 /*
1530 * Server: find/create a certificate for TLS...
1531 */
1532
378eeedf 1533 if (http->fields[HTTP_FIELD_HOST])
41e0907c
MS
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 {
25731360 1553 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
41e0907c
MS
1554 hostname[0] = '\0';
1555 }
1556 else if (httpAddrLocalhost(&addr))
1557 hostname[0] = '\0';
1558 else
a27a134a
MS
1559 {
1560 httpAddrLookup(&addr, hostname, sizeof(hostname));
25731360 1561 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
a27a134a 1562 }
41e0907c
MS
1563 }
1564
a27a134a
MS
1565 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1566 hostname[0] = '\0'; /* Don't allow numeric addresses */
1567
41e0907c
MS
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 {
25731360 1575 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
41e0907c
MS
1576
1577 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1578 {
25731360 1579 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
41e0907c
MS
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 {
25731360 1592 DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
41e0907c
MS
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
25731360 1602 DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
41e0907c
MS
1603 }
1604
807315e6 1605 DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", (void *)http->tls_credentials));
2c85b752
MS
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
41e0907c 1612 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1613 {
41e0907c
MS
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
2c85b752
MS
1634 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1635
25731360 1636 DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
2c85b752
MS
1637 }
1638
1639 if (!error)
1640 {
a7aabde8
MS
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 */
2c85b752
MS
1663
1664 while (!error && !done)
1665 {
1666 error = SSLHandshake(http->tls);
1667
25731360 1668 DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
2c85b752
MS
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
25731360 1693 DEBUG_printf(("4_httpTLSStart: Server certificate callback "
2c85b752
MS
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 {
7e86f2f6 1715 credential->datalen = (size_t)CFDataGetLength(data);
2c85b752
MS
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
25731360 1736 DEBUG_printf(("4_httpTLSStart: Client certificate callback "
2c85b752
MS
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 }
a7aabde8
MS
1783
1784 /*
1785 * Restore the previous timeout settings...
1786 */
1787
1788 httpSetTimeout(http, old_timeout, old_cb, old_data);
2c85b752
MS
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/*
25731360 1821 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
2c85b752
MS
1822 */
1823
25731360
MS
1824void
1825_httpTLSStop(http_t *http) /* I - HTTP connection */
2c85b752
MS
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/*
25731360 1841 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
2c85b752
MS
1842 */
1843
25731360
MS
1844int /* O - Bytes written */
1845_httpTLSWrite(http_t *http, /* I - HTTP connection */
2c85b752
MS
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
807315e6 1854 DEBUG_printf(("2_httpTLSWrite(http=%p, buf=%p, len=%d)", (void *)http, (void *)buf, len));
2c85b752 1855
7e86f2f6 1856 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
2c85b752
MS
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
25731360 1890 DEBUG_printf(("3_httpTLSWrite: Returning %d.", (int)result));
2c85b752
MS
1891
1892 return ((int)result);
1893}
1894
1895
1896/*
88f1e9c8 1897 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
2c85b752
MS
1898 */
1899
88f1e9c8
MS
1900static CFArrayRef /* O - Array of certificates or NULL */
1901http_cdsa_copy_server(
1902 const char *common_name) /* I - Server's hostname */
2c85b752 1903{
2274d26b 1904#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8 1905 OSStatus err; /* Error info */
88f1e9c8
MS
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 */
4daf7e97 1914 SecKeychainRef syschain = NULL;/* System keychain */
afd25c34 1915 SecKeychainStatus status = 0; /* Keychain status */
2c85b752 1916
2c85b752 1917
2274d26b
MS
1918 DEBUG_printf(("3http_cdsa_copy_server(common_name=\"%s\")", common_name));
1919
88f1e9c8 1920 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
2c85b752 1921
88f1e9c8
MS
1922 policy = SecPolicyCreateSSL(1, cfcommon_name);
1923
88f1e9c8 1924 if (!policy)
2274d26b
MS
1925 {
1926 DEBUG_puts("4http_cdsa_copy_server: Unable to create SSL policy.");
88f1e9c8 1927 goto cleanup;
2274d26b 1928 }
88f1e9c8
MS
1929
1930 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
2274d26b
MS
1931 {
1932 DEBUG_puts("4http_cdsa_copy_server: Unable to create query dictionary.");
88f1e9c8 1933 goto cleanup;
2274d26b 1934 }
88f1e9c8 1935
f93b32b6
MS
1936 _cupsMutexLock(&tls_mutex);
1937
afd25c34
MS
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
88f1e9c8
MS
1943 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
1944 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1945 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1946 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
88f1e9c8 1947
4daf7e97
MS
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
fc4bbb58 1959 CFDictionaryAddValue(query, kSecMatchSearchList, list);
88f1e9c8
MS
1960 CFRelease(list);
1961
1962 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
1963
f93b32b6
MS
1964 _cupsMutexUnlock(&tls_mutex);
1965
2274d26b
MS
1966 if (err != noErr)
1967 {
1968 DEBUG_printf(("4http_cdsa_copy_server: SecItemCopyMatching failed with status %d.", (int)err));
88f1e9c8 1969 goto cleanup;
2274d26b 1970 }
88f1e9c8
MS
1971
1972 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
2274d26b
MS
1973 {
1974 DEBUG_puts("4http_cdsa_copy_server: Search returned something that is not an identity.");
88f1e9c8 1975 goto cleanup;
2274d26b 1976 }
88f1e9c8
MS
1977
1978 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
2274d26b
MS
1979 {
1980 DEBUG_puts("4http_cdsa_copy_server: Unable to create array of certificates.");
88f1e9c8 1981 goto cleanup;
2274d26b 1982 }
88f1e9c8
MS
1983
1984 cleanup :
1985
4daf7e97
MS
1986 if (syschain)
1987 CFRelease(syschain);
88f1e9c8
MS
1988 if (identity)
1989 CFRelease(identity);
88f1e9c8
MS
1990 if (policy)
1991 CFRelease(policy);
2274d26b
MS
1992 if (cfcommon_name)
1993 CFRelease(cfcommon_name);
88f1e9c8
MS
1994 if (query)
1995 CFRelease(query);
1996
2274d26b
MS
1997 DEBUG_printf(("4http_cdsa_copy_server: Returning %p.", (void *)certificates));
1998
88f1e9c8 1999 return (certificates);
2274d26b
MS
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 */
2c85b752
MS
2007}
2008
2009
2ece34a9
MS
2010/*
2011 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
2012 */
2013
2014static SecCertificateRef /* O - Certificate */
2015http_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
fc4bbb58 2025#ifdef HAVE_SECKEYCHAINOPEN
005f7f1f
MS
2026/*
2027 * 'http_cdsa_default_path()' - Get the default keychain path.
2028 */
2029
2030static const char * /* O - Keychain path */
2031http_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
4daf7e97
MS
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
005f7f1f 2043 if (getuid() && home)
4daf7e97 2044 snprintf(buffer, bufsize, "%s/.cups/ssl.keychain", home);
005f7f1f 2045 else
4daf7e97 2046 strlcpy(buffer, "/etc/cups/ssl.keychain", bufsize);
005f7f1f
MS
2047
2048 DEBUG_printf(("1http_cdsa_default_path: Using default path \"%s\".", buffer));
2049
2050 return (buffer);
2051}
4daf7e97
MS
2052
2053
2054/*
2055 * 'http_cdsa_open_keychain()' - Open (or create) a keychain.
2056 */
2057
2058static SecKeychainRef /* O - Keychain or NULL */
2059http_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)
afd25c34 2075 {
4daf7e97 2076 path = http_cdsa_default_path(filename, filesize);
afd25c34
MS
2077 tls_cups_keychain = 1;
2078 }
4daf7e97 2079 else
afd25c34 2080 {
4daf7e97 2081 strlcpy(filename, path, filesize);
afd25c34
MS
2082 tls_cups_keychain = 0;
2083 }
4daf7e97
MS
2084
2085 /*
2086 * Save the interaction setting and disable while we open the keychain...
2087 */
2088
2089 SecKeychainGetUserInteractionAllowed(&interaction);
2090 SecKeychainSetUserInteractionAllowed(FALSE);
2091
afd25c34 2092 if (access(path, R_OK) && tls_cups_keychain)
4daf7e97
MS
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
afd25c34 2111 if (err == noErr && !(status & kSecUnlockStateStatus) && tls_cups_keychain)
4daf7e97
MS
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
2149static SecKeychainRef
2150http_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}
fc4bbb58 2201#endif /* HAVE_SECKEYCHAINOPEN */
005f7f1f
MS
2202
2203
2c85b752 2204/*
88f1e9c8 2205 * 'http_cdsa_read()' - Read function for the CDSA library.
2c85b752
MS
2206 */
2207
88f1e9c8
MS
2208static OSStatus /* O - -1 on error, 0 on success */
2209http_cdsa_read(
2210 SSLConnectionRef connection, /* I - SSL/TLS connection */
2211 void *data, /* I - Data buffer */
2212 size_t *dataLength) /* IO - Number of bytes */
2c85b752 2213{
88f1e9c8
MS
2214 OSStatus result; /* Return value */
2215 ssize_t bytes; /* Number of bytes read */
2216 http_t *http; /* HTTP connection */
2c85b752 2217
2c85b752 2218
88f1e9c8 2219 http = (http_t *)connection;
2c85b752 2220
a7aabde8 2221 if (!http->blocking || http->timeout_value > 0.0)
2c85b752
MS
2222 {
2223 /*
88f1e9c8 2224 * Make sure we have data before we read...
2c85b752
MS
2225 */
2226
88f1e9c8
MS
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 }
2c85b752
MS
2235 }
2236
88f1e9c8 2237 do
2c85b752 2238 {
88f1e9c8 2239 bytes = recv(http->fd, data, *dataLength, 0);
2c85b752 2240 }
88f1e9c8 2241 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2c85b752 2242
88f1e9c8
MS
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;
2c85b752 2255
88f1e9c8
MS
2256 if (bytes == 0)
2257 result = errSSLClosedGraceful;
2258 else if (errno == EAGAIN)
2259 result = errSSLWouldBlock;
2260 else
2261 result = errSSLClosedAbort;
2262 }
2c85b752 2263
88f1e9c8
MS
2264 return (result);
2265}
2c85b752 2266
2c85b752 2267
88f1e9c8
MS
2268/*
2269 * 'http_cdsa_set_credentials()' - Set the TLS credentials.
2270 */
2c85b752 2271
88f1e9c8
MS
2272static int /* O - Status of connection */
2273http_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 */
2c85b752 2279
2c85b752 2280
807315e6 2281 DEBUG_printf(("7http_tls_set_credentials(%p)", (void *)http));
2c85b752 2282
88f1e9c8
MS
2283 /*
2284 * Prefer connection specific credentials...
2285 */
2c85b752 2286
88f1e9c8
MS
2287 if ((credentials = http->tls_credentials) == NULL)
2288 credentials = cg->tls_credentials;
2c85b752 2289
88f1e9c8
MS
2290 if (credentials)
2291 {
2292 error = SSLSetCertificate(http->tls, credentials);
2293 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
2294 (int)error));
2c85b752 2295 }
88f1e9c8
MS
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
2307static OSStatus /* O - -1 on error, 0 on success */
2308http_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 */
2c85b752 2316
2c85b752 2317
88f1e9c8
MS
2318 http = (http_t *)connection;
2319
2320 do
2c85b752 2321 {
88f1e9c8
MS
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;
2c85b752
MS
2334 }
2335 else
88f1e9c8
MS
2336 {
2337 *dataLength = 0;
2c85b752 2338
88f1e9c8
MS
2339 if (errno == EAGAIN)
2340 result = errSSLWouldBlock;
2341 else
2342 result = errSSLClosedAbort;
2343 }
2344
2345 return (result);
2c85b752 2346}