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