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