]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-darwin.c
Remove macOS version check.
[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
588c2205 852# if !TARGET_OS_IOS
073b3929 853 CFDictionaryRef cf_dict; /* Dictionary for certificate */
588c2205
MS
854# endif /* !TARGET_OS_IOS */
855 CFStringRef cf_string; /* CF string */
073b3929
MS
856 char commonName[256],/* Common name associated with cert */
857 issuer[256], /* Issuer name */
858 sigalg[256]; /* Signature algorithm */
376d7c69 859 time_t expiration; /* Expiration date of cert */
376d7c69
MS
860 unsigned char md5_digest[16]; /* MD5 result */
861
073b3929 862 if (SecCertificateCopyCommonName(secCert, &cf_string) == noErr)
9653cfdf 863 {
073b3929
MS
864 CFStringGetCString(cf_string, commonName, (CFIndex)sizeof(commonName), kCFStringEncodingUTF8);
865 CFRelease(cf_string);
9653cfdf 866 }
376d7c69 867 else
073b3929
MS
868 {
869 strlcpy(commonName, "unknown", sizeof(commonName));
870 }
871
872 strlcpy(issuer, "unknown", sizeof(issuer));
873 strlcpy(sigalg, "UnknownSignature", sizeof(sigalg));
874
588c2205 875# if !TARGET_OS_IOS
073b3929
MS
876 if ((cf_dict = SecCertificateCopyValues(secCert, NULL, NULL)) != NULL)
877 {
878 CFDictionaryRef cf_issuer = CFDictionaryGetValue(cf_dict, kSecOIDX509V1IssuerName);
879 CFDictionaryRef cf_sigalg = CFDictionaryGetValue(cf_dict, kSecOIDX509V1SignatureAlgorithm);
880
881 if (cf_issuer)
882 {
883 CFArrayRef cf_values = CFDictionaryGetValue(cf_issuer, kSecPropertyKeyValue);
884 CFIndex i, count = CFArrayGetCount(cf_values);
885 CFDictionaryRef cf_value;
886
887 for (i = 0; i < count; i ++)
888 {
889 cf_value = CFArrayGetValueAtIndex(cf_values, i);
890
891 if (!CFStringCompare(CFDictionaryGetValue(cf_value, kSecPropertyKeyLabel), kSecOIDOrganizationName, kCFCompareCaseInsensitive))
892 CFStringGetCString(CFDictionaryGetValue(cf_value, kSecPropertyKeyValue), issuer, (CFIndex)sizeof(issuer), kCFStringEncodingUTF8);
893 }
894 }
895
896 if (cf_sigalg)
897 {
898 CFArrayRef cf_values = CFDictionaryGetValue(cf_sigalg, kSecPropertyKeyValue);
899 CFIndex i, count = CFArrayGetCount(cf_values);
900 CFDictionaryRef cf_value;
901
902 for (i = 0; i < count; i ++)
903 {
904 cf_value = CFArrayGetValueAtIndex(cf_values, i);
905
906 if (!CFStringCompare(CFDictionaryGetValue(cf_value, kSecPropertyKeyLabel), CFSTR("Algorithm"), kCFCompareCaseInsensitive))
907 {
908 CFStringRef cf_algorithm = CFDictionaryGetValue(cf_value, kSecPropertyKeyValue);
909
910 if (!CFStringCompare(cf_algorithm, CFSTR("1.2.840.113549.1.1.5"), kCFCompareCaseInsensitive))
911 strlcpy(sigalg, "SHA1WithRSAEncryption", sizeof(sigalg));
912 else if (!CFStringCompare(cf_algorithm, CFSTR("1.2.840.113549.1.1.11"), kCFCompareCaseInsensitive))
913 strlcpy(sigalg, "SHA256WithRSAEncryption", sizeof(sigalg));
914 else if (!CFStringCompare(cf_algorithm, CFSTR("1.2.840.113549.1.1.4"), kCFCompareCaseInsensitive))
915 strlcpy(sigalg, "MD5WithRSAEncryption", sizeof(sigalg));
916 }
917 }
918 }
919
920 CFRelease(cf_dict);
921 }
588c2205 922# endif /* !TARGET_OS_IOS */
376d7c69
MS
923
924 expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
925
7ec11630 926 cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
376d7c69 927
073b3929 928 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
929
930 CFRelease(secCert);
931 }
932
376d7c69
MS
933 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
934
9653cfdf 935 return (strlen(buffer));
72d05bc9
MS
936}
937
938
2c85b752
MS
939/*
940 * '_httpFreeCredentials()' - Free internal credentials.
941 */
942
943void
944_httpFreeCredentials(
945 http_tls_credentials_t credentials) /* I - Internal credentials */
946{
947 if (!credentials)
948 return;
949
950 CFRelease(credentials);
951}
952
953
72d05bc9
MS
954/*
955 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
956 *
e1f19878 957 * @since CUPS 2.0/OS 10.10@
72d05bc9
MS
958 */
959
dafebafd 960int /* O - 0 on success, -1 on error */
72d05bc9 961httpLoadCredentials(
f93b32b6 962 const char *path, /* I - Keychain path or @code NULL@ for default */
72d05bc9
MS
963 cups_array_t **credentials, /* IO - Credentials */
964 const char *common_name) /* I - Common name for credentials */
965{
dafebafd 966 OSStatus err; /* Error info */
fc4bbb58 967#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8 968 char filename[1024]; /* Filename for keychain */
4daf7e97
MS
969 SecKeychainRef keychain = NULL,/* Keychain reference */
970 syschain = NULL;/* System keychain */
fc4bbb58
MS
971 CFArrayRef list; /* Keychain list */
972#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8
MS
973 SecCertificateRef cert = NULL; /* Certificate */
974 CFDataRef data; /* Certificate data */
dafebafd
MS
975 SecPolicyRef policy = NULL; /* Policy ref */
976 CFStringRef cfcommon_name = NULL;
977 /* Server name */
978 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
dafebafd
MS
979
980
807315e6 981 DEBUG_printf(("httpLoadCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
88f1e9c8
MS
982
983 if (!credentials)
984 return (-1);
985
986 *credentials = NULL;
987
fc4bbb58 988#ifdef HAVE_SECKEYCHAINOPEN
4daf7e97 989 keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
88f1e9c8 990
4daf7e97 991 if (!keychain)
dafebafd
MS
992 goto cleanup;
993
4daf7e97
MS
994 syschain = http_cdsa_open_system_keychain();
995
fc4bbb58
MS
996#else
997 if (path)
998 return (-1);
999#endif /* HAVE_SECKEYCHAINOPEN */
1000
dafebafd
MS
1001 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
1002
1003 policy = SecPolicyCreateSSL(1, cfcommon_name);
1004
1005 if (cfcommon_name)
1006 CFRelease(cfcommon_name);
1007
1008 if (!policy)
1009 goto cleanup;
1010
1011 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
1012 goto cleanup;
1013
88f1e9c8 1014 CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
dafebafd
MS
1015 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1016 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1017 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
dafebafd 1018
fc4bbb58 1019#ifdef HAVE_SECKEYCHAINOPEN
4daf7e97
MS
1020 if (syschain)
1021 {
1022 const void *values[2] = { syschain, keychain };
1023
1024 list = CFArrayCreate(kCFAllocatorDefault, (const void **)values, 2, &kCFTypeArrayCallBacks);
1025 }
1026 else
1027 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
fc4bbb58 1028 CFDictionaryAddValue(query, kSecMatchSearchList, list);
dafebafd 1029 CFRelease(list);
fc4bbb58 1030#endif /* HAVE_SECKEYCHAINOPEN */
dafebafd 1031
88f1e9c8 1032 err = SecItemCopyMatching(query, (CFTypeRef *)&cert);
dafebafd
MS
1033
1034 if (err)
1035 goto cleanup;
1036
88f1e9c8 1037 if (CFGetTypeID(cert) != SecCertificateGetTypeID())
dafebafd
MS
1038 goto cleanup;
1039
88f1e9c8
MS
1040 if ((data = SecCertificateCopyData(cert)) != NULL)
1041 {
1042 DEBUG_printf(("1httpLoadCredentials: Adding %d byte certificate blob.", (int)CFDataGetLength(data)));
1043
1044 *credentials = cupsArrayNew(NULL, NULL);
1045 httpAddCredential(*credentials, CFDataGetBytePtr(data), (size_t)CFDataGetLength(data));
1046 CFRelease(data);
1047 }
dafebafd
MS
1048
1049 cleanup :
1050
fc4bbb58 1051#ifdef HAVE_SECKEYCHAINOPEN
dafebafd
MS
1052 if (keychain)
1053 CFRelease(keychain);
4daf7e97
MS
1054
1055 if (syschain)
1056 CFRelease(syschain);
fc4bbb58 1057#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8
MS
1058 if (cert)
1059 CFRelease(cert);
dafebafd
MS
1060 if (policy)
1061 CFRelease(policy);
1062 if (query)
1063 CFRelease(query);
1064
88f1e9c8
MS
1065 DEBUG_printf(("1httpLoadCredentials: Returning %d.", *credentials ? 0 : -1));
1066
1067 return (*credentials ? 0 : -1);
72d05bc9
MS
1068}
1069
1070
72d05bc9
MS
1071/*
1072 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
1073 *
e1f19878 1074 * @since CUPS 2.0/OS 10.10@
72d05bc9
MS
1075 */
1076
1077int /* O - -1 on error, 0 on success */
1078httpSaveCredentials(
f93b32b6 1079 const char *path, /* I - Keychain path or @code NULL@ for default */
72d05bc9
MS
1080 cups_array_t *credentials, /* I - Credentials */
1081 const char *common_name) /* I - Common name for credentials */
1082{
88f1e9c8 1083 int ret = -1; /* Return value */
41e0907c 1084 OSStatus err; /* Error info */
fc4bbb58 1085#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8
MS
1086 char filename[1024]; /* Filename for keychain */
1087 SecKeychainRef keychain = NULL;/* Keychain reference */
fc4bbb58
MS
1088 CFArrayRef list; /* Keychain list */
1089#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8 1090 SecCertificateRef cert = NULL; /* Certificate */
88f1e9c8 1091 CFMutableDictionaryRef attrs = NULL; /* Attributes for add */
41e0907c
MS
1092
1093
807315e6 1094 DEBUG_printf(("httpSaveCredentials(path=\"%s\", credentials=%p, common_name=\"%s\")", path, (void *)credentials, common_name));
88f1e9c8 1095 if (!credentials)
41e0907c
MS
1096 goto cleanup;
1097
524c65e6
MS
1098 if (!httpCredentialsAreValidForName(credentials, common_name))
1099 {
1100 DEBUG_puts("1httpSaveCredentials: Common name does not match.");
1101 return (-1);
1102 }
1103
88f1e9c8
MS
1104 if ((cert = http_cdsa_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
1105 {
1106 DEBUG_puts("1httpSaveCredentials: Unable to create certificate.");
41e0907c 1107 goto cleanup;
88f1e9c8 1108 }
41e0907c 1109
fc4bbb58 1110#ifdef HAVE_SECKEYCHAINOPEN
4daf7e97 1111 keychain = http_cdsa_open_keychain(path, filename, sizeof(filename));
2c85b752 1112
4daf7e97 1113 if (!keychain)
88f1e9c8 1114 goto cleanup;
2c85b752 1115
fc4bbb58
MS
1116#else
1117 if (path)
1118 return (-1);
1119#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8 1120
88f1e9c8
MS
1121 if ((attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
1122 {
1123 DEBUG_puts("1httpSaveCredentials: Unable to create dictionary.");
1124 goto cleanup;
2c85b752
MS
1125 }
1126
88f1e9c8 1127 CFDictionaryAddValue(attrs, kSecClass, kSecClassCertificate);
88f1e9c8 1128 CFDictionaryAddValue(attrs, kSecValueRef, cert);
fc4bbb58
MS
1129
1130#ifdef HAVE_SECKEYCHAINOPEN
1131 if ((list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks)) == NULL)
1132 {
1133 DEBUG_puts("1httpSaveCredentials: Unable to create list of keychains.");
1134 goto cleanup;
1135 }
88f1e9c8 1136 CFDictionaryAddValue(attrs, kSecMatchSearchList, list);
fc4bbb58
MS
1137 CFRelease(list);
1138#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752 1139
88f1e9c8 1140 /* Note: SecItemAdd consumes "attrs"... */
524c65e6 1141 err = SecItemAdd(attrs, NULL);
88f1e9c8 1142 DEBUG_printf(("1httpSaveCredentials: SecItemAdd returned %d.", (int)err));
2c85b752 1143
88f1e9c8 1144 cleanup :
2c85b752 1145
fc4bbb58 1146#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8
MS
1147 if (keychain)
1148 CFRelease(keychain);
fc4bbb58 1149#endif /* HAVE_SECKEYCHAINOPEN */
88f1e9c8
MS
1150 if (cert)
1151 CFRelease(cert);
2c85b752 1152
88f1e9c8 1153 DEBUG_printf(("1httpSaveCredentials: Returning %d.", ret));
2c85b752 1154
88f1e9c8 1155 return (ret);
2c85b752
MS
1156}
1157
1158
1159/*
25731360 1160 * '_httpTLSInitialize()' - Initialize the TLS stack.
2c85b752
MS
1161 */
1162
25731360
MS
1163void
1164_httpTLSInitialize(void)
2c85b752
MS
1165{
1166 /*
1167 * Nothing to do...
1168 */
1169}
1170
1171
1172/*
25731360 1173 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
2c85b752
MS
1174 */
1175
25731360
MS
1176size_t
1177_httpTLSPending(http_t *http) /* I - HTTP connection */
2c85b752
MS
1178{
1179 size_t bytes; /* Bytes that are available */
1180
1181
1182 if (!SSLGetBufferedReadSize(http->tls, &bytes))
1183 return (bytes);
1184
1185 return (0);
1186}
1187
1188
1189/*
25731360 1190 * '_httpTLSRead()' - Read from a SSL/TLS connection.
2c85b752
MS
1191 */
1192
25731360
MS
1193int /* O - Bytes read */
1194_httpTLSRead(http_t *http, /* I - HTTP connection */
2c85b752
MS
1195 char *buf, /* I - Buffer to store data */
1196 int len) /* I - Length of buffer */
1197{
1198 int result; /* Return value */
1199 OSStatus error; /* Error info */
1200 size_t processed; /* Number of bytes processed */
1201
1202
7e86f2f6 1203 error = SSLRead(http->tls, buf, (size_t)len, &processed);
25731360 1204 DEBUG_printf(("6_httpTLSRead: error=%d, processed=%d", (int)error,
2c85b752
MS
1205 (int)processed));
1206 switch (error)
1207 {
1208 case 0 :
1209 result = (int)processed;
1210 break;
1211
1212 case errSSLWouldBlock :
1213 if (processed)
1214 result = (int)processed;
1215 else
1216 {
1217 result = -1;
1218 errno = EINTR;
1219 }
1220 break;
1221
1222 case errSSLClosedGraceful :
1223 default :
1224 if (processed)
1225 result = (int)processed;
1226 else
1227 {
1228 result = -1;
1229 errno = EPIPE;
1230 }
1231 break;
1232 }
1233
1234 return (result);
1235}
1236
1237
63aefcd5
MS
1238/*
1239 * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
1240 */
1241
1242void
8f1fbdec
MS
1243_httpTLSSetOptions(int options, /* I - Options */
1244 int min_version, /* I - Minimum TLS version */
1245 int max_version) /* I - Maximum TLS version */
63aefcd5 1246{
02c88e67 1247 if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
8f1fbdec
MS
1248 {
1249 tls_options = options;
1250 tls_min_version = min_version;
1251 tls_max_version = max_version;
1252 }
63aefcd5
MS
1253}
1254
1255
2c85b752 1256/*
25731360 1257 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
2c85b752
MS
1258 */
1259
25731360
MS
1260int /* O - 0 on success, -1 on failure */
1261_httpTLSStart(http_t *http) /* I - HTTP connection */
2c85b752
MS
1262{
1263 char hostname[256], /* Hostname */
1264 *hostptr; /* Pointer into hostname */
1265 _cups_globals_t *cg = _cupsGlobals();
1266 /* Pointer to library globals */
1267 OSStatus error; /* Error code */
1268 const char *message = NULL;/* Error message */
1269 cups_array_t *credentials; /* Credentials array */
1270 cups_array_t *names; /* CUPS distinguished names */
1271 CFArrayRef dn_array; /* CF distinguished names array */
1272 CFIndex count; /* Number of credentials */
1273 CFDataRef data; /* Certificate data */
1274 int i; /* Looping var */
1275 http_credential_t *credential; /* Credential data */
1276
1277
807315e6 1278 DEBUG_printf(("3_httpTLSStart(http=%p)", (void *)http));
b37d45d9
MS
1279
1280 if (tls_options < 0)
1281 {
1282 DEBUG_puts("4_httpTLSStart: Setting defaults.");
1283 _cupsSetDefaults();
8f1fbdec 1284 DEBUG_printf(("4_httpTLSStart: tls_options=%x, tls_min_version=%d, tls_max_version=%d", tls_options, tls_min_version, tls_max_version));
b37d45d9 1285 }
2c85b752 1286
c913d726 1287#ifdef HAVE_SECKEYCHAINOPEN
41e0907c 1288 if (http->mode == _HTTP_MODE_SERVER && !tls_keychain)
2c85b752 1289 {
25731360 1290 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
41e0907c
MS
1291 http->error = errno = EINVAL;
1292 http->status = HTTP_STATUS_ERROR;
1293 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
2c85b752 1294
41e0907c 1295 return (-1);
2c85b752 1296 }
2274d26b 1297#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752 1298
72d05bc9 1299 if ((http->tls = SSLCreateContext(kCFAllocatorDefault, http->mode == _HTTP_MODE_CLIENT ? kSSLClientSide : kSSLServerSide, kSSLStreamType)) == NULL)
2c85b752 1300 {
25731360 1301 DEBUG_puts("4_httpTLSStart: SSLCreateContext failed.");
2c85b752
MS
1302 http->error = errno = ENOMEM;
1303 http->status = HTTP_STATUS_ERROR;
1304 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1305
1306 return (-1);
1307 }
1308
1309 error = SSLSetConnection(http->tls, http);
25731360 1310 DEBUG_printf(("4_httpTLSStart: SSLSetConnection, error=%d", (int)error));
2c85b752
MS
1311
1312 if (!error)
1313 {
1314 error = SSLSetIOFuncs(http->tls, http_cdsa_read, http_cdsa_write);
25731360 1315 DEBUG_printf(("4_httpTLSStart: SSLSetIOFuncs, error=%d", (int)error));
2c85b752
MS
1316 }
1317
1318 if (!error)
1319 {
1320 error = SSLSetSessionOption(http->tls, kSSLSessionOptionBreakOnServerAuth,
1321 true);
63aefcd5
MS
1322 DEBUG_printf(("4_httpTLSStart: SSLSetSessionOption, error=%d", (int)error));
1323 }
1324
1325 if (!error)
1326 {
8f1fbdec
MS
1327 static const SSLProtocol protocols[] = /* Min/max protocol versions */
1328 {
1329 kSSLProtocol3,
1330 kTLSProtocol1,
1331 kTLSProtocol11,
1332 kTLSProtocol12,
9f4c8267 1333 kTLSProtocol13
8f1fbdec
MS
1334 };
1335
9f4c8267
MS
1336 if (tls_min_version < _HTTP_TLS_MAX)
1337 {
1338 error = SSLSetProtocolVersionMin(http->tls, protocols[tls_min_version]);
1339 DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", protocols[tls_min_version], (int)error));
1340 }
8f1fbdec 1341
9f4c8267 1342 if (!error && tls_max_version < _HTTP_TLS_MAX)
4f272af7 1343 {
8f1fbdec
MS
1344 error = SSLSetProtocolVersionMax(http->tls, protocols[tls_max_version]);
1345 DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMax(%d), error=%d", protocols[tls_max_version], (int)error));
4f272af7 1346 }
63aefcd5
MS
1347 }
1348
ee6226a5 1349# if HAVE_SSLSETENABLEDCIPHERS
63aefcd5
MS
1350 if (!error)
1351 {
1352 SSLCipherSuite supported[100]; /* Supported cipher suites */
1353 size_t num_supported; /* Number of supported cipher suites */
1354 SSLCipherSuite enabled[100]; /* Cipher suites to enable */
1355 size_t num_enabled; /* Number of cipher suites to enable */
1356
1357 num_supported = sizeof(supported) / sizeof(supported[0]);
1358 error = SSLGetSupportedCiphers(http->tls, supported, &num_supported);
1359
1360 if (!error)
1361 {
1362 DEBUG_printf(("4_httpTLSStart: %d cipher suites supported.", (int)num_supported));
1363
1364 for (i = 0, num_enabled = 0; i < (int)num_supported && num_enabled < (sizeof(enabled) / sizeof(enabled[0])); i ++)
1365 {
1366 switch (supported[i])
1367 {
1368 /* Obviously insecure cipher suites that we never want to use */
1369 case SSL_NULL_WITH_NULL_NULL :
1370 case SSL_RSA_WITH_NULL_MD5 :
1371 case SSL_RSA_WITH_NULL_SHA :
1372 case SSL_RSA_EXPORT_WITH_RC4_40_MD5 :
1373 case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 :
1374 case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA :
1375 case SSL_RSA_WITH_DES_CBC_SHA :
1376 case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA :
1377 case SSL_DH_DSS_WITH_DES_CBC_SHA :
1378 case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA :
1379 case SSL_DH_RSA_WITH_DES_CBC_SHA :
1380 case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA :
1381 case SSL_DHE_DSS_WITH_DES_CBC_SHA :
1382 case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA :
1383 case SSL_DHE_RSA_WITH_DES_CBC_SHA :
1384 case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 :
1385 case SSL_DH_anon_WITH_RC4_128_MD5 :
1386 case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA :
1387 case SSL_DH_anon_WITH_DES_CBC_SHA :
1388 case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA :
1389 case SSL_FORTEZZA_DMS_WITH_NULL_SHA :
1390 case TLS_DH_anon_WITH_AES_128_CBC_SHA :
1391 case TLS_DH_anon_WITH_AES_256_CBC_SHA :
1392 case TLS_ECDH_ECDSA_WITH_NULL_SHA :
1393 case TLS_ECDHE_RSA_WITH_NULL_SHA :
1394 case TLS_ECDH_anon_WITH_NULL_SHA :
1395 case TLS_ECDH_anon_WITH_RC4_128_SHA :
1396 case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA :
1397 case TLS_ECDH_anon_WITH_AES_128_CBC_SHA :
1398 case TLS_ECDH_anon_WITH_AES_256_CBC_SHA :
1399 case TLS_RSA_WITH_NULL_SHA256 :
1400 case TLS_DH_anon_WITH_AES_128_CBC_SHA256 :
1401 case TLS_DH_anon_WITH_AES_256_CBC_SHA256 :
1402 case TLS_PSK_WITH_NULL_SHA :
1403 case TLS_DHE_PSK_WITH_NULL_SHA :
1404 case TLS_RSA_PSK_WITH_NULL_SHA :
1405 case TLS_DH_anon_WITH_AES_128_GCM_SHA256 :
1406 case TLS_DH_anon_WITH_AES_256_GCM_SHA384 :
1407 case TLS_PSK_WITH_NULL_SHA256 :
1408 case TLS_PSK_WITH_NULL_SHA384 :
1409 case TLS_DHE_PSK_WITH_NULL_SHA256 :
1410 case TLS_DHE_PSK_WITH_NULL_SHA384 :
1411 case TLS_RSA_PSK_WITH_NULL_SHA256 :
1412 case TLS_RSA_PSK_WITH_NULL_SHA384 :
1413 case SSL_RSA_WITH_DES_CBC_MD5 :
b37d45d9 1414 DEBUG_printf(("4_httpTLSStart: Excluding insecure cipher suite %d", supported[i]));
63aefcd5
MS
1415 break;
1416
1417 /* RC4 cipher suites that should only be used as a last resort */
1418 case SSL_RSA_WITH_RC4_128_MD5 :
1419 case SSL_RSA_WITH_RC4_128_SHA :
1420 case TLS_ECDH_ECDSA_WITH_RC4_128_SHA :
1421 case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA :
1422 case TLS_ECDH_RSA_WITH_RC4_128_SHA :
1423 case TLS_ECDHE_RSA_WITH_RC4_128_SHA :
1424 case TLS_PSK_WITH_RC4_128_SHA :
1425 case TLS_DHE_PSK_WITH_RC4_128_SHA :
1426 case TLS_RSA_PSK_WITH_RC4_128_SHA :
1427 if (tls_options & _HTTP_TLS_ALLOW_RC4)
1428 enabled[num_enabled ++] = supported[i];
b37d45d9
MS
1429 else
1430 DEBUG_printf(("4_httpTLSStart: Excluding RC4 cipher suite %d", supported[i]));
63aefcd5
MS
1431 break;
1432
ee6226a5
MS
1433 /* DH/DHE cipher suites that are problematic with parameters < 1024 bits */
1434 case TLS_DH_DSS_WITH_AES_128_CBC_SHA :
1435 case TLS_DH_RSA_WITH_AES_128_CBC_SHA :
1436 case TLS_DHE_DSS_WITH_AES_128_CBC_SHA :
1437 case TLS_DHE_RSA_WITH_AES_128_CBC_SHA :
1438 case TLS_DH_DSS_WITH_AES_256_CBC_SHA :
1439 case TLS_DH_RSA_WITH_AES_256_CBC_SHA :
1440 case TLS_DHE_DSS_WITH_AES_256_CBC_SHA :
1441 case TLS_DHE_RSA_WITH_AES_256_CBC_SHA :
1442 case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA :
1443 case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA :
ee6226a5
MS
1444 case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA :
1445 case TLS_DH_DSS_WITH_AES_128_CBC_SHA256 :
1446 case TLS_DH_RSA_WITH_AES_128_CBC_SHA256 :
1447 case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 :
1448 case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 :
1449 case TLS_DH_DSS_WITH_AES_256_CBC_SHA256 :
1450 case TLS_DH_RSA_WITH_AES_256_CBC_SHA256 :
1451 case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 :
1452 case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 :
1453 case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA :
1454 case TLS_DHE_PSK_WITH_AES_128_CBC_SHA :
1455 case TLS_DHE_PSK_WITH_AES_256_CBC_SHA :
f2e87147
MS
1456 case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
1457 case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
1458 if (tls_options & _HTTP_TLS_DENY_CBC)
1459 {
1460 DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
1461 break;
1462 }
1463
bdc4056c
MS
1464// case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 :
1465// case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 :
ee6226a5
MS
1466 case TLS_DH_RSA_WITH_AES_128_GCM_SHA256 :
1467 case TLS_DH_RSA_WITH_AES_256_GCM_SHA384 :
bdc4056c
MS
1468// case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 :
1469// case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 :
ee6226a5
MS
1470 case TLS_DH_DSS_WITH_AES_128_GCM_SHA256 :
1471 case TLS_DH_DSS_WITH_AES_256_GCM_SHA384 :
1472 case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 :
1473 case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 :
ee6226a5
MS
1474 if (tls_options & _HTTP_TLS_ALLOW_DH)
1475 enabled[num_enabled ++] = supported[i];
b37d45d9
MS
1476 else
1477 DEBUG_printf(("4_httpTLSStart: Excluding DH/DHE cipher suite %d", supported[i]));
ee6226a5
MS
1478 break;
1479
f2e87147
MS
1480 case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA :
1481 case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 :
1482 case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 :
1483 case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 :
1484 case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 :
1485 case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 :
1486 case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 :
1487 case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 :
1488 case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 :
4f272af7
MS
1489 case TLS_RSA_WITH_3DES_EDE_CBC_SHA :
1490 case TLS_RSA_WITH_AES_128_CBC_SHA :
1491 case TLS_RSA_WITH_AES_256_CBC_SHA :
f2e87147
MS
1492 if (tls_options & _HTTP_TLS_DENY_CBC)
1493 {
1494 DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
1495 break;
1496 }
1497
1498 /* Anything else we'll assume is "secure" */
63aefcd5
MS
1499 default :
1500 enabled[num_enabled ++] = supported[i];
1501 break;
1502 }
1503 }
1504
1505 DEBUG_printf(("4_httpTLSStart: %d cipher suites enabled.", (int)num_enabled));
1506 error = SSLSetEnabledCiphers(http->tls, enabled, num_enabled);
1507 }
2c85b752 1508 }
ee6226a5 1509#endif /* HAVE_SSLSETENABLEDCIPHERS */
2c85b752 1510
41e0907c 1511 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1512 {
41e0907c
MS
1513 /*
1514 * Client: set client-side credentials, if any...
1515 */
1516
2c85b752
MS
1517 if (cg->client_cert_cb)
1518 {
1519 error = SSLSetSessionOption(http->tls,
1520 kSSLSessionOptionBreakOnCertRequested, true);
25731360 1521 DEBUG_printf(("4_httpTLSStart: kSSLSessionOptionBreakOnCertRequested, "
2c85b752
MS
1522 "error=%d", (int)error));
1523 }
1524 else
1525 {
88f1e9c8
MS
1526 error = http_cdsa_set_credentials(http);
1527 DEBUG_printf(("4_httpTLSStart: http_cdsa_set_credentials, error=%d",
2c85b752
MS
1528 (int)error));
1529 }
1530 }
41e0907c
MS
1531 else if (!error)
1532 {
1533 /*
1534 * Server: find/create a certificate for TLS...
1535 */
1536
378eeedf 1537 if (http->fields[HTTP_FIELD_HOST])
41e0907c
MS
1538 {
1539 /*
1540 * Use hostname for TLS upgrade...
1541 */
1542
1543 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1544 }
1545 else
1546 {
1547 /*
1548 * Resolve hostname from connection address...
1549 */
1550
1551 http_addr_t addr; /* Connection address */
1552 socklen_t addrlen; /* Length of address */
1553
1554 addrlen = sizeof(addr);
1555 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1556 {
25731360 1557 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
41e0907c
MS
1558 hostname[0] = '\0';
1559 }
1560 else if (httpAddrLocalhost(&addr))
1561 hostname[0] = '\0';
1562 else
a27a134a
MS
1563 {
1564 httpAddrLookup(&addr, hostname, sizeof(hostname));
25731360 1565 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
a27a134a 1566 }
41e0907c
MS
1567 }
1568
a27a134a
MS
1569 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1570 hostname[0] = '\0'; /* Don't allow numeric addresses */
1571
41e0907c
MS
1572 if (hostname[0])
1573 http->tls_credentials = http_cdsa_copy_server(hostname);
1574 else if (tls_common_name)
1575 http->tls_credentials = http_cdsa_copy_server(tls_common_name);
1576
1577 if (!http->tls_credentials && tls_auto_create && (hostname[0] || tls_common_name))
1578 {
25731360 1579 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
41e0907c
MS
1580
1581 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1582 {
25731360 1583 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
41e0907c
MS
1584 http->error = errno = EINVAL;
1585 http->status = HTTP_STATUS_ERROR;
1586 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1587
1588 return (-1);
1589 }
1590
1591 http->tls_credentials = http_cdsa_copy_server(hostname[0] ? hostname : tls_common_name);
1592 }
1593
1594 if (!http->tls_credentials)
1595 {
25731360 1596 DEBUG_puts("4_httpTLSStart: Unable to find server credentials.");
41e0907c
MS
1597 http->error = errno = EINVAL;
1598 http->status = HTTP_STATUS_ERROR;
1599 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to find server credentials."), 1);
1600
1601 return (-1);
1602 }
1603
1604 error = SSLSetCertificate(http->tls, http->tls_credentials);
1605
25731360 1606 DEBUG_printf(("4_httpTLSStart: SSLSetCertificate, error=%d", (int)error));
41e0907c
MS
1607 }
1608
807315e6 1609 DEBUG_printf(("4_httpTLSStart: tls_credentials=%p", (void *)http->tls_credentials));
2c85b752
MS
1610
1611 /*
1612 * Let the server know which hostname/domain we are trying to connect to
1613 * in case it wants to serve up a certificate with a matching common name.
1614 */
1615
41e0907c 1616 if (!error && http->mode == _HTTP_MODE_CLIENT)
2c85b752 1617 {
41e0907c
MS
1618 /*
1619 * Client: get the hostname to use for TLS...
1620 */
1621
1622 if (httpAddrLocalhost(http->hostaddr))
1623 {
1624 strlcpy(hostname, "localhost", sizeof(hostname));
1625 }
1626 else
1627 {
1628 /*
1629 * Otherwise make sure the hostname we have does not end in a trailing dot.
1630 */
1631
1632 strlcpy(hostname, http->hostname, sizeof(hostname));
1633 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1634 *hostptr == '.')
1635 *hostptr = '\0';
1636 }
1637
2c85b752
MS
1638 error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
1639
25731360 1640 DEBUG_printf(("4_httpTLSStart: SSLSetPeerDomainName, error=%d", (int)error));
2c85b752
MS
1641 }
1642
1643 if (!error)
1644 {
a7aabde8
MS
1645 int done = 0; /* Are we done yet? */
1646 double old_timeout; /* Old timeout value */
1647 http_timeout_cb_t old_cb; /* Old timeout callback */
1648 void *old_data; /* Old timeout data */
1649
1650 /*
1651 * Enforce a minimum timeout of 10 seconds for the TLS handshake...
1652 */
1653
1654 old_timeout = http->timeout_value;
1655 old_cb = http->timeout_cb;
1656 old_data = http->timeout_data;
1657
1658 if (!old_cb || old_timeout < 10.0)
1659 {
1660 DEBUG_puts("4_httpTLSStart: Setting timeout to 10 seconds.");
1661 httpSetTimeout(http, 10.0, NULL, NULL);
1662 }
1663
1664 /*
1665 * Do the TLS handshake...
1666 */
2c85b752
MS
1667
1668 while (!error && !done)
1669 {
1670 error = SSLHandshake(http->tls);
1671
25731360 1672 DEBUG_printf(("4_httpTLSStart: SSLHandshake returned %d.", (int)error));
2c85b752
MS
1673
1674 switch (error)
1675 {
1676 case noErr :
1677 done = 1;
1678 break;
1679
1680 case errSSLWouldBlock :
1681 error = noErr; /* Force a retry */
1682 usleep(1000); /* in 1 millisecond */
1683 break;
1684
1685 case errSSLServerAuthCompleted :
1686 error = 0;
1687 if (cg->server_cert_cb)
1688 {
1689 error = httpCopyCredentials(http, &credentials);
1690 if (!error)
1691 {
1692 error = (cg->server_cert_cb)(http, http->tls, credentials,
1693 cg->server_cert_data);
1694 httpFreeCredentials(credentials);
1695 }
1696
25731360 1697 DEBUG_printf(("4_httpTLSStart: Server certificate callback "
2c85b752
MS
1698 "returned %d.", (int)error));
1699 }
1700 break;
1701
1702 case errSSLClientCertRequested :
1703 error = 0;
1704
1705 if (cg->client_cert_cb)
1706 {
1707 names = NULL;
1708 if (!(error = SSLCopyDistinguishedNames(http->tls, &dn_array)) &&
1709 dn_array)
1710 {
1711 if ((names = cupsArrayNew(NULL, NULL)) != NULL)
1712 {
1713 for (i = 0, count = CFArrayGetCount(dn_array); i < count; i++)
1714 {
1715 data = (CFDataRef)CFArrayGetValueAtIndex(dn_array, i);
1716
1717 if ((credential = malloc(sizeof(*credential))) != NULL)
1718 {
7e86f2f6 1719 credential->datalen = (size_t)CFDataGetLength(data);
2c85b752
MS
1720 if ((credential->data = malloc(credential->datalen)))
1721 {
1722 memcpy((void *)credential->data, CFDataGetBytePtr(data),
1723 credential->datalen);
1724 cupsArrayAdd(names, credential);
1725 }
1726 else
1727 free(credential);
1728 }
1729 }
1730 }
1731
1732 CFRelease(dn_array);
1733 }
1734
1735 if (!error)
1736 {
1737 error = (cg->client_cert_cb)(http, http->tls, names,
1738 cg->client_cert_data);
1739
25731360 1740 DEBUG_printf(("4_httpTLSStart: Client certificate callback "
2c85b752
MS
1741 "returned %d.", (int)error));
1742 }
1743
1744 httpFreeCredentials(names);
1745 }
1746 break;
1747
1748 case errSSLUnknownRootCert :
1749 message = _("Unable to establish a secure connection to host "
1750 "(untrusted certificate).");
1751 break;
1752
1753 case errSSLNoRootCert :
1754 message = _("Unable to establish a secure connection to host "
1755 "(self-signed certificate).");
1756 break;
1757
1758 case errSSLCertExpired :
1759 message = _("Unable to establish a secure connection to host "
1760 "(expired certificate).");
1761 break;
1762
1763 case errSSLCertNotYetValid :
1764 message = _("Unable to establish a secure connection to host "
1765 "(certificate not yet valid).");
1766 break;
1767
1768 case errSSLHostNameMismatch :
1769 message = _("Unable to establish a secure connection to host "
1770 "(host name mismatch).");
1771 break;
1772
1773 case errSSLXCertChainInvalid :
1774 message = _("Unable to establish a secure connection to host "
1775 "(certificate chain invalid).");
1776 break;
1777
1778 case errSSLConnectionRefused :
1779 message = _("Unable to establish a secure connection to host "
1780 "(peer dropped connection before responding).");
1781 break;
1782
1783 default :
1784 break;
1785 }
1786 }
a7aabde8
MS
1787
1788 /*
1789 * Restore the previous timeout settings...
1790 */
1791
1792 httpSetTimeout(http, old_timeout, old_cb, old_data);
2c85b752
MS
1793 }
1794
1795 if (error)
1796 {
1797 http->error = error;
1798 http->status = HTTP_STATUS_ERROR;
1799 errno = ECONNREFUSED;
1800
1801 CFRelease(http->tls);
1802 http->tls = NULL;
1803
1804 /*
1805 * If an error string wasn't set by the callbacks use a generic one...
1806 */
1807
1808 if (!message)
1809#ifdef HAVE_CSSMERRORSTRING
1810 message = cssmErrorString(error);
1811#else
1812 message = _("Unable to establish a secure connection to host.");
1813#endif /* HAVE_CSSMERRORSTRING */
1814
1815 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
1816
1817 return (-1);
1818 }
1819
1820 return (0);
1821}
1822
1823
1824/*
25731360 1825 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
2c85b752
MS
1826 */
1827
25731360
MS
1828void
1829_httpTLSStop(http_t *http) /* I - HTTP connection */
2c85b752
MS
1830{
1831 while (SSLClose(http->tls) == errSSLWouldBlock)
1832 usleep(1000);
1833
1834 CFRelease(http->tls);
1835
1836 if (http->tls_credentials)
1837 CFRelease(http->tls_credentials);
1838
1839 http->tls = NULL;
1840 http->tls_credentials = NULL;
1841}
1842
1843
1844/*
25731360 1845 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
2c85b752
MS
1846 */
1847
25731360
MS
1848int /* O - Bytes written */
1849_httpTLSWrite(http_t *http, /* I - HTTP connection */
2c85b752
MS
1850 const char *buf, /* I - Buffer holding data */
1851 int len) /* I - Length of buffer */
1852{
1853 ssize_t result; /* Return value */
1854 OSStatus error; /* Error info */
1855 size_t processed; /* Number of bytes processed */
1856
1857
807315e6 1858 DEBUG_printf(("2_httpTLSWrite(http=%p, buf=%p, len=%d)", (void *)http, (void *)buf, len));
2c85b752 1859
7e86f2f6 1860 error = SSLWrite(http->tls, buf, (size_t)len, &processed);
2c85b752
MS
1861
1862 switch (error)
1863 {
1864 case 0 :
1865 result = (int)processed;
1866 break;
1867
1868 case errSSLWouldBlock :
1869 if (processed)
1870 {
1871 result = (int)processed;
1872 }
1873 else
1874 {
1875 result = -1;
1876 errno = EINTR;
1877 }
1878 break;
1879
1880 case errSSLClosedGraceful :
1881 default :
1882 if (processed)
1883 {
1884 result = (int)processed;
1885 }
1886 else
1887 {
1888 result = -1;
1889 errno = EPIPE;
1890 }
1891 break;
1892 }
1893
25731360 1894 DEBUG_printf(("3_httpTLSWrite: Returning %d.", (int)result));
2c85b752
MS
1895
1896 return ((int)result);
1897}
1898
1899
1900/*
88f1e9c8 1901 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
2c85b752
MS
1902 */
1903
88f1e9c8
MS
1904static CFArrayRef /* O - Array of certificates or NULL */
1905http_cdsa_copy_server(
1906 const char *common_name) /* I - Server's hostname */
2c85b752 1907{
2274d26b 1908#ifdef HAVE_SECKEYCHAINOPEN
88f1e9c8 1909 OSStatus err; /* Error info */
88f1e9c8
MS
1910 SecIdentityRef identity = NULL;/* Identity */
1911 CFArrayRef certificates = NULL;
1912 /* Certificate array */
1913 SecPolicyRef policy = NULL; /* Policy ref */
1914 CFStringRef cfcommon_name = NULL;
1915 /* Server name */
1916 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
1917 CFArrayRef list = NULL; /* Keychain list */
4daf7e97 1918 SecKeychainRef syschain = NULL;/* System keychain */
afd25c34 1919 SecKeychainStatus status = 0; /* Keychain status */
2c85b752 1920
2c85b752 1921
2274d26b
MS
1922 DEBUG_printf(("3http_cdsa_copy_server(common_name=\"%s\")", common_name));
1923
88f1e9c8 1924 cfcommon_name = CFStringCreateWithCString(kCFAllocatorDefault, common_name, kCFStringEncodingUTF8);
2c85b752 1925
88f1e9c8
MS
1926 policy = SecPolicyCreateSSL(1, cfcommon_name);
1927
88f1e9c8 1928 if (!policy)
2274d26b
MS
1929 {
1930 DEBUG_puts("4http_cdsa_copy_server: Unable to create SSL policy.");
88f1e9c8 1931 goto cleanup;
2274d26b 1932 }
88f1e9c8
MS
1933
1934 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
2274d26b
MS
1935 {
1936 DEBUG_puts("4http_cdsa_copy_server: Unable to create query dictionary.");
88f1e9c8 1937 goto cleanup;
2274d26b 1938 }
88f1e9c8 1939
f93b32b6
MS
1940 _cupsMutexLock(&tls_mutex);
1941
afd25c34
MS
1942 err = SecKeychainGetStatus(tls_keychain, &status);
1943
1944 if (err == noErr && !(status & kSecUnlockStateStatus) && tls_cups_keychain)
1945 SecKeychainUnlock(tls_keychain, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, TRUE);
1946
88f1e9c8
MS
1947 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
1948 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
1949 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
1950 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
88f1e9c8 1951
4daf7e97
MS
1952 syschain = http_cdsa_open_system_keychain();
1953
1954 if (syschain)
1955 {
1956 const void *values[2] = { syschain, tls_keychain };
1957
1958 list = CFArrayCreate(kCFAllocatorDefault, (const void **)values, 2, &kCFTypeArrayCallBacks);
1959 }
1960 else
1961 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&tls_keychain, 1, &kCFTypeArrayCallBacks);
1962
fc4bbb58 1963 CFDictionaryAddValue(query, kSecMatchSearchList, list);
88f1e9c8
MS
1964 CFRelease(list);
1965
1966 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
1967
f93b32b6
MS
1968 _cupsMutexUnlock(&tls_mutex);
1969
2274d26b
MS
1970 if (err != noErr)
1971 {
1972 DEBUG_printf(("4http_cdsa_copy_server: SecItemCopyMatching failed with status %d.", (int)err));
88f1e9c8 1973 goto cleanup;
2274d26b 1974 }
88f1e9c8
MS
1975
1976 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
2274d26b
MS
1977 {
1978 DEBUG_puts("4http_cdsa_copy_server: Search returned something that is not an identity.");
88f1e9c8 1979 goto cleanup;
2274d26b 1980 }
88f1e9c8
MS
1981
1982 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL)
2274d26b
MS
1983 {
1984 DEBUG_puts("4http_cdsa_copy_server: Unable to create array of certificates.");
88f1e9c8 1985 goto cleanup;
2274d26b 1986 }
88f1e9c8
MS
1987
1988 cleanup :
1989
4daf7e97
MS
1990 if (syschain)
1991 CFRelease(syschain);
88f1e9c8
MS
1992 if (identity)
1993 CFRelease(identity);
88f1e9c8
MS
1994 if (policy)
1995 CFRelease(policy);
2274d26b
MS
1996 if (cfcommon_name)
1997 CFRelease(cfcommon_name);
88f1e9c8
MS
1998 if (query)
1999 CFRelease(query);
2000
2274d26b
MS
2001 DEBUG_printf(("4http_cdsa_copy_server: Returning %p.", (void *)certificates));
2002
88f1e9c8 2003 return (certificates);
2274d26b
MS
2004#else
2005
588c2205
MS
2006 (void)common_name;
2007
2274d26b
MS
2008 if (!tls_selfsigned)
2009 return (NULL);
2010
2011 return (CFArrayCreate(NULL, (const void **)&tls_selfsigned, 1, &kCFTypeArrayCallBacks));
2012#endif /* HAVE_SECKEYCHAINOPEN */
2c85b752
MS
2013}
2014
2015
2ece34a9
MS
2016/*
2017 * 'http_cdsa_create_credential()' - Create a single credential in the internal format.
2018 */
2019
2020static SecCertificateRef /* O - Certificate */
2021http_cdsa_create_credential(
2022 http_credential_t *credential) /* I - Credential */
2023{
2024 if (!credential)
2025 return (NULL);
2026
2027 return (SecCertificateCreateWithBytes(kCFAllocatorDefault, credential->data, (CFIndex)credential->datalen));
2028}
2029
2030
fc4bbb58 2031#ifdef HAVE_SECKEYCHAINOPEN
005f7f1f
MS
2032/*
2033 * 'http_cdsa_default_path()' - Get the default keychain path.
2034 */
2035
2036static const char * /* O - Keychain path */
2037http_cdsa_default_path(char *buffer, /* I - Path buffer */
2038 size_t bufsize) /* I - Size of buffer */
2039{
2040 const char *home = getenv("HOME"); /* HOME environment variable */
2041
2042
4daf7e97
MS
2043 /*
2044 * Determine the default keychain path. Note that the login and system
2045 * keychains are no longer accessible to user applications starting in macOS
2046 * 10.11.4 (!), so we need to create our own keychain just for CUPS.
2047 */
2048
005f7f1f 2049 if (getuid() && home)
4daf7e97 2050 snprintf(buffer, bufsize, "%s/.cups/ssl.keychain", home);
005f7f1f 2051 else
4daf7e97 2052 strlcpy(buffer, "/etc/cups/ssl.keychain", bufsize);
005f7f1f
MS
2053
2054 DEBUG_printf(("1http_cdsa_default_path: Using default path \"%s\".", buffer));
2055
2056 return (buffer);
2057}
4daf7e97
MS
2058
2059
2060/*
2061 * 'http_cdsa_open_keychain()' - Open (or create) a keychain.
2062 */
2063
2064static SecKeychainRef /* O - Keychain or NULL */
2065http_cdsa_open_keychain(
2066 const char *path, /* I - Path to keychain */
2067 char *filename, /* I - Keychain filename */
2068 size_t filesize) /* I - Size of filename buffer */
2069{
2070 SecKeychainRef keychain = NULL;/* Temporary keychain */
2071 OSStatus err; /* Error code */
2072 Boolean interaction; /* Interaction allowed? */
2073 SecKeychainStatus status = 0; /* Keychain status */
2074
2075
2076 /*
2077 * Get the keychain filename...
2078 */
2079
2080 if (!path)
afd25c34 2081 {
4daf7e97 2082 path = http_cdsa_default_path(filename, filesize);
afd25c34
MS
2083 tls_cups_keychain = 1;
2084 }
4daf7e97 2085 else
afd25c34 2086 {
4daf7e97 2087 strlcpy(filename, path, filesize);
afd25c34
MS
2088 tls_cups_keychain = 0;
2089 }
4daf7e97
MS
2090
2091 /*
2092 * Save the interaction setting and disable while we open the keychain...
2093 */
2094
2095 SecKeychainGetUserInteractionAllowed(&interaction);
2096 SecKeychainSetUserInteractionAllowed(FALSE);
2097
afd25c34 2098 if (access(path, R_OK) && tls_cups_keychain)
4daf7e97
MS
2099 {
2100 /*
2101 * Create a new keychain at the given path...
2102 */
2103
2104 err = SecKeychainCreate(path, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, FALSE, NULL, &keychain);
2105 }
2106 else
2107 {
2108 /*
2109 * Open the existing keychain and unlock as needed...
2110 */
2111
2112 err = SecKeychainOpen(path, &keychain);
2113
2114 if (err == noErr)
2115 err = SecKeychainGetStatus(keychain, &status);
2116
afd25c34 2117 if (err == noErr && !(status & kSecUnlockStateStatus) && tls_cups_keychain)
4daf7e97
MS
2118 err = SecKeychainUnlock(keychain, _CUPS_CDSA_PASSLEN, _CUPS_CDSA_PASSWORD, TRUE);
2119 }
2120
2121 /*
2122 * Restore interaction setting...
2123 */
2124
2125 SecKeychainSetUserInteractionAllowed(interaction);
2126
2127 /*
2128 * Release the keychain if we had any errors...
2129 */
2130
2131 if (err != noErr)
2132 {
2133 /* TODO: Set cups last error string */
2134 DEBUG_printf(("4http_cdsa_open_keychain: Unable to open keychain (%d), returning NULL.", (int)err));
2135
2136 if (keychain)
2137 {
2138 CFRelease(keychain);
2139 keychain = NULL;
2140 }
2141 }
2142
2143 /*
2144 * Return the keychain or NULL...
2145 */
2146
2147 return (keychain);
2148}
2149
2150
2151/*
2152 * 'http_cdsa_open_system_keychain()' - Open the System keychain.
2153 */
2154
2155static SecKeychainRef
2156http_cdsa_open_system_keychain(void)
2157{
2158 SecKeychainRef keychain = NULL;/* Temporary keychain */
2159 OSStatus err; /* Error code */
2160 Boolean interaction; /* Interaction allowed? */
2161 SecKeychainStatus status = 0; /* Keychain status */
2162
2163
2164 /*
2165 * Save the interaction setting and disable while we open the keychain...
2166 */
2167
2168 SecKeychainGetUserInteractionAllowed(&interaction);
2169 SecKeychainSetUserInteractionAllowed(TRUE);
2170
2171 err = SecKeychainOpen("/Library/Keychains/System.keychain", &keychain);
2172
2173 if (err == noErr)
2174 err = SecKeychainGetStatus(keychain, &status);
2175
2176 if (err == noErr && !(status & kSecUnlockStateStatus))
2177 err = errSecInteractionNotAllowed;
2178
2179 /*
2180 * Restore interaction setting...
2181 */
2182
2183 SecKeychainSetUserInteractionAllowed(interaction);
2184
2185 /*
2186 * Release the keychain if we had any errors...
2187 */
2188
2189 if (err != noErr)
2190 {
2191 /* TODO: Set cups last error string */
2192 DEBUG_printf(("4http_cdsa_open_system_keychain: Unable to open keychain (%d), returning NULL.", (int)err));
2193
2194 if (keychain)
2195 {
2196 CFRelease(keychain);
2197 keychain = NULL;
2198 }
2199 }
2200
2201 /*
2202 * Return the keychain or NULL...
2203 */
2204
2205 return (keychain);
2206}
fc4bbb58 2207#endif /* HAVE_SECKEYCHAINOPEN */
005f7f1f
MS
2208
2209
2c85b752 2210/*
88f1e9c8 2211 * 'http_cdsa_read()' - Read function for the CDSA library.
2c85b752
MS
2212 */
2213
88f1e9c8
MS
2214static OSStatus /* O - -1 on error, 0 on success */
2215http_cdsa_read(
2216 SSLConnectionRef connection, /* I - SSL/TLS connection */
2217 void *data, /* I - Data buffer */
2218 size_t *dataLength) /* IO - Number of bytes */
2c85b752 2219{
88f1e9c8
MS
2220 OSStatus result; /* Return value */
2221 ssize_t bytes; /* Number of bytes read */
2222 http_t *http; /* HTTP connection */
2c85b752 2223
2c85b752 2224
88f1e9c8 2225 http = (http_t *)connection;
2c85b752 2226
a7aabde8 2227 if (!http->blocking || http->timeout_value > 0.0)
2c85b752
MS
2228 {
2229 /*
88f1e9c8 2230 * Make sure we have data before we read...
2c85b752
MS
2231 */
2232
88f1e9c8
MS
2233 while (!_httpWait(http, http->wait_value, 0))
2234 {
2235 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
2236 continue;
2237
2238 http->error = ETIMEDOUT;
2239 return (-1);
2240 }
2c85b752
MS
2241 }
2242
88f1e9c8 2243 do
2c85b752 2244 {
88f1e9c8 2245 bytes = recv(http->fd, data, *dataLength, 0);
2c85b752 2246 }
88f1e9c8 2247 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2c85b752 2248
88f1e9c8
MS
2249 if ((size_t)bytes == *dataLength)
2250 {
2251 result = 0;
2252 }
2253 else if (bytes > 0)
2254 {
2255 *dataLength = (size_t)bytes;
2256 result = errSSLWouldBlock;
2257 }
2258 else
2259 {
2260 *dataLength = 0;
2c85b752 2261
88f1e9c8
MS
2262 if (bytes == 0)
2263 result = errSSLClosedGraceful;
2264 else if (errno == EAGAIN)
2265 result = errSSLWouldBlock;
2266 else
2267 result = errSSLClosedAbort;
2268 }
2c85b752 2269
88f1e9c8
MS
2270 return (result);
2271}
2c85b752 2272
2c85b752 2273
88f1e9c8
MS
2274/*
2275 * 'http_cdsa_set_credentials()' - Set the TLS credentials.
2276 */
2c85b752 2277
88f1e9c8
MS
2278static int /* O - Status of connection */
2279http_cdsa_set_credentials(http_t *http) /* I - HTTP connection */
2280{
2281 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
2282 OSStatus error = 0; /* Error code */
2283 http_tls_credentials_t credentials = NULL;
2284 /* TLS credentials */
2c85b752 2285
2c85b752 2286
807315e6 2287 DEBUG_printf(("7http_tls_set_credentials(%p)", (void *)http));
2c85b752 2288
88f1e9c8
MS
2289 /*
2290 * Prefer connection specific credentials...
2291 */
2c85b752 2292
88f1e9c8
MS
2293 if ((credentials = http->tls_credentials) == NULL)
2294 credentials = cg->tls_credentials;
2c85b752 2295
88f1e9c8
MS
2296 if (credentials)
2297 {
2298 error = SSLSetCertificate(http->tls, credentials);
2299 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
2300 (int)error));
2c85b752 2301 }
88f1e9c8
MS
2302 else
2303 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
2304
2305 return (error);
2306}
2307
2308
2309/*
2310 * 'http_cdsa_write()' - Write function for the CDSA library.
2311 */
2312
2313static OSStatus /* O - -1 on error, 0 on success */
2314http_cdsa_write(
2315 SSLConnectionRef connection, /* I - SSL/TLS connection */
2316 const void *data, /* I - Data buffer */
2317 size_t *dataLength) /* IO - Number of bytes */
2318{
2319 OSStatus result; /* Return value */
2320 ssize_t bytes; /* Number of bytes read */
2321 http_t *http; /* HTTP connection */
2c85b752 2322
2c85b752 2323
88f1e9c8
MS
2324 http = (http_t *)connection;
2325
2326 do
2c85b752 2327 {
88f1e9c8
MS
2328 bytes = write(http->fd, data, *dataLength);
2329 }
2330 while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
2331
2332 if ((size_t)bytes == *dataLength)
2333 {
2334 result = 0;
2335 }
2336 else if (bytes >= 0)
2337 {
2338 *dataLength = (size_t)bytes;
2339 result = errSSLWouldBlock;
2c85b752
MS
2340 }
2341 else
88f1e9c8
MS
2342 {
2343 *dataLength = 0;
2c85b752 2344
88f1e9c8
MS
2345 if (errno == EAGAIN)
2346 result = errSSLWouldBlock;
2347 else
2348 result = errSSLClosedAbort;
2349 }
2350
2351 return (result);
2c85b752 2352}