4 * TLS support code for CUPS on OS X.
6 * Copyright 2007-2013 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
15 * This file is subject to the Apple OS-Developed Software exception.
18 /**** This file is included from http.c ****/
21 * Include necessary headers...
26 extern char **environ
;
33 #ifdef HAVE_SECKEYCHAINOPEN
34 static int tls_auto_create
= 0;
35 /* Auto-create self-signed certs? */
36 static char *tls_common_name
= NULL
;
37 /* Default common name */
38 static SecKeychainRef tls_keychain
= NULL
;
39 /* Server cert keychain */
40 static char *tls_keypath
= NULL
;
41 /* Server cert keychain path */
42 static _cups_mutex_t tls_mutex
= _CUPS_MUTEX_INITIALIZER
;
43 /* Mutex for keychain/certs */
44 #endif /* HAVE_SECKEYCHAINOPEN */
51 #ifdef HAVE_SECKEYCHAINOPEN
52 static CFArrayRef
http_cdsa_copy_server(const char *common_name
);
53 #endif /* HAVE_SECKEYCHAINOPEN */
54 static OSStatus
http_cdsa_read(SSLConnectionRef connection
, void *data
, size_t *dataLength
);
55 static OSStatus
http_cdsa_write(SSLConnectionRef connection
, const void *data
, size_t *dataLength
);
59 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
64 int /* O - 1 on success, 0 on failure */
65 cupsMakeServerCredentials(
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 */
72 #if defined(HAVE_SECGENERATESELFSIGNEDCERTIFICATE) && defined(HAVE_SECKEYCHAINOPEN)
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
,
82 CFMutableDictionaryRef keyParams
= NULL
;
83 /* Key generation parameters */
88 (void)expiration_date
;
90 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
,
91 kCFStringEncodingUTF8
);
96 * Create a public/private key pair...
99 keyParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
100 &kCFTypeDictionaryKeyCallBacks
,
101 &kCFTypeDictionaryValueCallBacks
);
105 CFDictionaryAddValue(keyParams
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
106 CFDictionaryAddValue(keyParams
, kSecAttrKeySizeInBits
, CFSTR("2048"));
107 CFDictionaryAddValue(keyParams
, kSecAttrLabel
,
108 CFSTR("CUPS Self-Signed Certificate"));
110 err
= SecKeyGeneratePair(keyParams
, &publicKey
, &privateKey
);
115 * Create a self-signed certificate using the public/private key pair...
118 CFIndex usageInt
= kSecKeyUsageAll
;
119 CFNumberRef usage
= CFNumberCreate(alloc
, kCFNumberCFIndexType
, &usageInt
);
120 CFDictionaryRef certParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
121 kSecCSRBasicContraintsPathLen
, CFINT(0), kSecSubjectAltName
, cfcommon_name
, kSecCertificateKeyUsage
, usage
, NULL
, NULL
);
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];
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
);
133 CFArrayRef subject
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, 2, NULL
);
134 SecCertificateRef cert
= SecGenerateSelfSignedCertificate(subject
, certParams
, publicKey
, privateKey
);
136 CFRelease(certParams
);
141 ident
= SecIdentityCreate(kCFAllocatorDefault
, cert
, privateKey
);
147 * Cleanup and return...
153 CFRelease(cfcommon_name
);
156 CFRelease(keyParams
);
165 CFRelease(publicKey
);
168 CFRelease(publicKey
);
172 #else /* !(HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN) */
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 */
184 (void)expiration_date
;
187 * Run the "certtool" command to generate a self-signed certificate...
190 if (!cupsFileFind("certtool", getenv("PATH"), 1, command
, sizeof(command
)))
194 * Create a file with the certificate information fields...
196 * Note: This assumes that the default questions are asked by the certtool
200 if ((fp
= cupsTempFile2(infofile
, sizeof(infofile
))) == NULL
)
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) */
222 snprintf(keychain
, sizeof(keychain
), "k=%s", path
);
224 argv
[0] = "certtool";
229 posix_spawn_file_actions_t actions
; /* File actions */
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);
235 if (posix_spawn(&pid
, command
, &actions
, NULL
, argv
, environ
))
241 posix_spawn_file_actions_destroy(&actions
);
245 while (waitpid(pid
, &status
, 0) < 0)
253 #endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE && HAVE_SECKEYCHAINOPEN */
258 * 'cupsSetServerCredentials()' - Set the default server credentials.
260 * Note: The server credentials are used by all threads in the running process.
261 * This function is threadsafe.
266 int /* O - 1 on success, 0 on failure */
267 cupsSetServerCredentials(
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 */
272 #ifdef HAVE_SECKEYCHAINOPEN
273 SecKeychainRef keychain
= NULL
;/* Temporary keychain */
276 if (SecKeychainOpen(path
, &keychain
) != noErr
)
278 /* TODO: Set cups last error string */
282 _cupsMutexLock(&tls_mutex
);
285 * Close any keychain that is currently open...
289 CFRelease(tls_keychain
);
292 _cupsStrFree(tls_keypath
);
295 _cupsStrFree(tls_common_name
);
298 * Save the new keychain...
301 tls_keychain
= keychain
;
302 tls_keypath
= _cupsStrAlloc(path
);
303 tls_auto_create
= auto_create
;
304 tls_common_name
= _cupsStrAlloc(common_name
);
306 _cupsMutexUnlock(&tls_mutex
);
312 #endif /* HAVE_SECKEYCHAINOPEN */
317 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
318 * an encrypted connection.
320 * @since CUPS 1.5/OS X 10.7@
323 int /* O - Status of call (0 = success) */
325 http_t
*http
, /* I - Connection to server */
326 cups_array_t
**credentials
) /* O - Array of credentials */
328 OSStatus error
; /* Error code */
329 SecTrustRef peerTrust
; /* Peer trust reference */
330 CFIndex count
; /* Number of credentials */
331 SecCertificateRef secCert
; /* Certificate reference */
332 CFDataRef data
; /* Certificate data */
333 int i
; /* Looping var */
339 if (!http
|| !http
->tls
|| !credentials
)
342 if (!(error
= SSLCopyPeerTrust(http
->tls
, &peerTrust
)) && peerTrust
)
344 if ((*credentials
= cupsArrayNew(NULL
, NULL
)) != NULL
)
346 count
= SecTrustGetCertificateCount(peerTrust
);
348 for (i
= 0; i
< count
; i
++)
350 secCert
= SecTrustGetCertificateAtIndex(peerTrust
, i
);
351 if ((data
= SecCertificateCopyData(secCert
)))
353 httpAddCredential(*credentials
, CFDataGetBytePtr(data
),
354 CFDataGetLength(data
));
360 CFRelease(peerTrust
);
368 * '_httpCreateCredentials()' - Create credentials in the internal format.
371 http_tls_credentials_t
/* O - Internal credentials */
372 _httpCreateCredentials(
373 cups_array_t
*credentials
) /* I - Array of credentials */
375 CFMutableArrayRef peerCerts
; /* Peer credentials reference */
376 SecCertificateRef secCert
; /* Certificate reference */
377 CFDataRef data
; /* Credential data reference */
378 http_credential_t
*credential
; /* Credential data */
384 if ((peerCerts
= CFArrayCreateMutable(kCFAllocatorDefault
,
385 cupsArrayCount(credentials
),
386 &kCFTypeArrayCallBacks
)) == NULL
)
389 for (credential
= (http_credential_t
*)cupsArrayFirst(credentials
);
391 credential
= (http_credential_t
*)cupsArrayNext(credentials
))
393 if ((data
= CFDataCreate(kCFAllocatorDefault
, credential
->data
,
394 credential
->datalen
)))
396 if ((secCert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
))
399 CFArrayAppendValue(peerCerts
, secCert
);
412 * 'httpCredentialsAreTrusted()' - Return whether the credentials are trusted.
417 int /* O - 1 if trusted, 0 if not/unknown */
418 httpCredentialsAreTrusted(
419 cups_array_t
*credentials
) /* I - Credentials */
428 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
433 time_t /* O - Expiration date of credentials */
434 httpCredentialsGetExpiration(
435 cups_array_t
*credentials
) /* I - Credentials */
444 * 'httpCredentialsIsValidName()' - Return whether the credentials are valid for the given name.
449 int /* O - 1 if valid, 0 otherwise */
450 httpCredentialsIsValidName(
451 cups_array_t
*credentials
, /* I - Credentials */
452 const char *common_name
) /* I - Name to check */
462 * 'httpCredentialsString()' - Return a string representing the credentials.
467 size_t /* O - Total size of credentials string */
468 httpCredentialsString(
469 cups_array_t
*credentials
, /* I - Credentials */
470 char *buffer
, /* I - Buffer or @code NULL@ */
471 size_t bufsize
) /* I - Size of buffer */
475 if (buffer
&& bufsize
> 0)
483 * '_httpFreeCredentials()' - Free internal credentials.
487 _httpFreeCredentials(
488 http_tls_credentials_t credentials
) /* I - Internal credentials */
493 CFRelease(credentials
);
498 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
503 int /* O - 0 on success, -1 on error */
505 const char *path
, /* I - Keychain/PKCS#12 path */
506 cups_array_t
**credentials
, /* IO - Credentials */
507 const char *common_name
) /* I - Common name for credentials */
516 OSStatus err
; /* Error info */
517 SecKeychainRef keychain
= NULL
;/* Keychain reference */
518 SecIdentitySearchRef search
= NULL
; /* Search reference */
519 SecIdentityRef identity
= NULL
;/* Identity */
520 CFArrayRef certificates
= NULL
;
521 /* Certificate array */
522 SecPolicyRef policy
= NULL
; /* Policy ref */
523 CFStringRef cfcommon_name
= NULL
;
525 CFMutableDictionaryRef query
= NULL
; /* Query qualifiers */
526 CFArrayRef list
= NULL
; /* Keychain list */
529 if ((err
= SecKeychainOpen(path
, &keychain
)))
532 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
, kCFStringEncodingUTF8
);
534 policy
= SecPolicyCreateSSL(1, cfcommon_name
);
537 CFRelease(cfcommon_name
);
542 if (!(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
)))
545 list
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&keychain
, 1,
546 &kCFTypeArrayCallBacks
);
548 CFDictionaryAddValue(query
, kSecClass
, kSecClassIdentity
);
549 CFDictionaryAddValue(query
, kSecMatchPolicy
, policy
);
550 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
551 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
552 CFDictionaryAddValue(query
, kSecMatchSearchList
, list
);
556 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
561 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
564 if ((certificates
= CFArrayCreate(NULL
, (const void **)&identity
, 1, &kCFTypeArrayCallBacks
)) == NULL
)
581 return (certificates
);
588 * 'cupsMakeCredentials()' - Create self-signed credentials for the given
594 int /* O - 0 on success, -1 on error */
596 const char *path
, /* I - Keychain/PKCS#12 path */
597 cups_array_t
**credentials
, /* O - Credentials */
598 const char *common_name
) /* I - Common name for X.509 cert */
600 # ifdef HAVE_SECGENERATESELFSIGNEDCERTIFICATE
601 int status
= -1; /* Return status */
602 OSStatus err
; /* Error code (if any) */
603 CFStringRef cfcommon_name
= NULL
;
604 /* CF string for server name */
605 SecIdentityRef ident
= NULL
; /* Identity */
606 SecKeyRef publicKey
= NULL
,
610 CFMutableDictionaryRef keyParams
= NULL
;
611 /* Key generation parameters */
617 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, servername
,
618 kCFStringEncodingUTF8
);
623 * Create a public/private key pair...
626 keyParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
627 &kCFTypeDictionaryKeyCallBacks
,
628 &kCFTypeDictionaryValueCallBacks
);
632 CFDictionaryAddValue(keyParams
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
633 CFDictionaryAddValue(keyParams
, kSecAttrKeySizeInBits
, CFSTR("2048"));
634 CFDictionaryAddValue(keyParams
, kSecAttrLabel
,
635 CFSTR("CUPS Self-Signed Certificate"));
637 err
= SecKeyGeneratePair(keyParams
, &publicKey
, &privateKey
);
642 * Create a self-signed certificate using the public/private key pair...
645 CFIndex usageInt
= kSecKeyUsageAll
;
646 CFNumberRef usage
= CFNumberCreate(alloc
, kCFNumberCFIndexType
, &usageInt
);
647 CFDictionaryRef certParams
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
648 kSecCSRBasicContraintsPathLen
, CFINT(0), kSecSubjectAltName
, cfcommon_name
, kSecCertificateKeyUsage
, usage
, NULL
, NULL
);
651 const void *ca_o
[] = { kSecOidOrganization
, CFSTR("") };
652 const void *ca_cn
[] = { kSecOidCommonName
, cfcommon_name
};
653 CFArrayRef ca_o_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_o
, 2, NULL
);
654 CFArrayRef ca_cn_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_cn
, 2, NULL
);
655 const void *ca_dn_array
[2];
657 ca_dn_array
[0] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_o_dn
, 1, NULL
);
658 ca_dn_array
[1] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_cn_dn
, 1, NULL
);
660 CFArrayRef subject
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, 2, NULL
);
661 SecCertificateRef cert
= SecGenerateSelfSignedCertificate(subject
, certParams
, publicKey
, privateKey
);
663 CFRelease(certParams
);
668 ident
= SecIdentityCreate(kCFAllocatorDefault
, cert
, privateKey
);
674 * Cleanup and return...
680 CFRelease(cfcommon_name
);
683 CFRelease(keyParams
);
692 CFRelease(publicKey
);
695 CFRelease(publicKey
);
699 # else /* !HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
700 int pid
, /* Process ID of command */
701 status
; /* Status of command */
702 char command
[1024], /* Command */
703 *argv
[4], /* Command-line arguments */
704 keychain
[1024], /* Keychain argument */
705 infofile
[1024]; /* Type-in information for cert */
706 cups_file_t
*fp
; /* Seed/info file */
710 * Run the "certtool" command to generate a self-signed certificate...
713 if (!cupsFileFind("certtool", getenv("PATH"), 1, command
, sizeof(command
)))
717 * Create a file with the certificate information fields...
719 * Note: This assumes that the default questions are asked by the certtool
723 if ((fp
= cupsTempFile2(infofile
, sizeof(infofile
))) == NULL
)
727 "%s\n" /* Enter key and certificate label */
728 "r\n" /* Generate RSA key pair */
729 "2048\n" /* Key size in bits */
730 "y\n" /* OK (y = yes) */
731 "b\n" /* Usage (b=signing/encryption) */
732 "s\n" /* Sign with SHA1 */
733 "y\n" /* OK (y = yes) */
734 "%s\n" /* Common name */
735 "\n" /* Country (default) */
736 "\n" /* Organization (default) */
737 "\n" /* Organizational unit (default) */
738 "\n" /* State/Province (default) */
739 "%s\n" /* Email address */
740 "y\n", /* OK (y = yes) */
741 common_name
, common_name
, "");
744 snprintf(keychain
, sizeof(keychain
), "k=%s", path
);
746 argv
[0] = "certtool";
751 posix_spawn_file_actions_t actions
; /* File actions */
753 posix_spawn_file_actions_init(&actions
);
754 posix_spawn_file_actions_addclose(&actions
, 0);
755 posix_spawn_file_actions_addopen(&actions
, 0, infofile
, O_RDONLY
, 0);
757 if (posix_spawn(&pid
, command
, &actions
, NULL
, argv
, environ
))
763 posix_spawn_file_actions_destroy(&actions
);
767 while (waitpid(pid
, &status
, 0) < 0)
777 # endif /* HAVE_SECGENERATESELFSIGNEDCERTIFICATE */
779 return (httpLoadCredentials(path
, credentials
, common_name
));
785 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
790 int /* O - -1 on error, 0 on success */
792 const char *path
, /* I - Keychain/PKCS#12 path */
793 cups_array_t
*credentials
, /* I - Credentials */
794 const char *common_name
) /* I - Common name for credentials */
804 #ifdef HAVE_SECKEYCHAINOPEN
806 * 'http_cdsa_copy_server()' - Find and copy server credentials from the keychain.
809 static CFArrayRef
/* O - Array of certificates or NULL */
810 http_cdsa_copy_server(
811 const char *common_name
) /* I - Server's hostname */
813 OSStatus err
; /* Error info */
814 SecIdentitySearchRef search
= NULL
; /* Search reference */
815 SecIdentityRef identity
= NULL
;/* Identity */
816 CFArrayRef certificates
= NULL
;
817 /* Certificate array */
818 SecPolicyRef policy
= NULL
; /* Policy ref */
819 CFStringRef cfcommon_name
= NULL
;
821 CFMutableDictionaryRef query
= NULL
; /* Query qualifiers */
822 CFArrayRef list
= NULL
; /* Keychain list */
825 cfcommon_name
= CFStringCreateWithCString(kCFAllocatorDefault
, common_name
, kCFStringEncodingUTF8
);
827 policy
= SecPolicyCreateSSL(1, cfcommon_name
);
830 CFRelease(cfcommon_name
);
835 if (!(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
)))
838 list
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&tls_keychain
, 1, &kCFTypeArrayCallBacks
);
840 CFDictionaryAddValue(query
, kSecClass
, kSecClassIdentity
);
841 CFDictionaryAddValue(query
, kSecMatchPolicy
, policy
);
842 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
843 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitOne
);
844 CFDictionaryAddValue(query
, kSecMatchSearchList
, list
);
848 err
= SecItemCopyMatching(query
, (CFTypeRef
*)&identity
);
853 if (CFGetTypeID(identity
) != SecIdentityGetTypeID())
856 if ((certificates
= CFArrayCreate(NULL
, (const void **)&identity
, 1, &kCFTypeArrayCallBacks
)) == NULL
)
871 return (certificates
);
873 #endif /* HAVE_SECKEYCHAINOPEN */
877 * 'http_cdsa_read()' - Read function for the CDSA library.
880 static OSStatus
/* O - -1 on error, 0 on success */
882 SSLConnectionRef connection
, /* I - SSL/TLS connection */
883 void *data
, /* I - Data buffer */
884 size_t *dataLength
) /* IO - Number of bytes */
886 OSStatus result
; /* Return value */
887 ssize_t bytes
; /* Number of bytes read */
888 http_t
*http
; /* HTTP connection */
891 http
= (http_t
*)connection
;
896 * Make sure we have data before we read...
899 while (!_httpWait(http
, http
->wait_value
, 0))
901 if (http
->timeout_cb
&& (*http
->timeout_cb
)(http
, http
->timeout_data
))
904 http
->error
= ETIMEDOUT
;
911 bytes
= recv(http
->fd
, data
, *dataLength
, 0);
913 while (bytes
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
915 if (bytes
== *dataLength
)
922 result
= errSSLWouldBlock
;
929 result
= errSSLClosedGraceful
;
930 else if (errno
== EAGAIN
)
931 result
= errSSLWouldBlock
;
933 result
= errSSLClosedAbort
;
941 * 'http_cdsa_write()' - Write function for the CDSA library.
944 static OSStatus
/* O - -1 on error, 0 on success */
946 SSLConnectionRef connection
, /* I - SSL/TLS connection */
947 const void *data
, /* I - Data buffer */
948 size_t *dataLength
) /* IO - Number of bytes */
950 OSStatus result
; /* Return value */
951 ssize_t bytes
; /* Number of bytes read */
952 http_t
*http
; /* HTTP connection */
955 http
= (http_t
*)connection
;
959 bytes
= write(http
->fd
, data
, *dataLength
);
961 while (bytes
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
963 if (bytes
== *dataLength
)
970 result
= errSSLWouldBlock
;
977 result
= errSSLWouldBlock
;
979 result
= errSSLClosedAbort
;
987 * 'http_tls_initialize()' - Initialize the TLS stack.
991 http_tls_initialize(void)
1000 * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
1004 http_tls_pending(http_t
*http
) /* I - HTTP connection */
1006 size_t bytes
; /* Bytes that are available */
1009 if (!SSLGetBufferedReadSize(http
->tls
, &bytes
))
1017 * 'http_tls_read()' - Read from a SSL/TLS connection.
1020 static int /* O - Bytes read */
1021 http_tls_read(http_t
*http
, /* I - HTTP connection */
1022 char *buf
, /* I - Buffer to store data */
1023 int len
) /* I - Length of buffer */
1025 int result
; /* Return value */
1026 OSStatus error
; /* Error info */
1027 size_t processed
; /* Number of bytes processed */
1030 error
= SSLRead(http
->tls
, buf
, len
, &processed
);
1031 DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error
,
1036 result
= (int)processed
;
1039 case errSSLWouldBlock
:
1041 result
= (int)processed
;
1049 case errSSLClosedGraceful
:
1052 result
= (int)processed
;
1066 * 'http_tls_set_credentials()' - Set the TLS credentials.
1069 static int /* O - Status of connection */
1070 http_tls_set_credentials(http_t
*http
) /* I - HTTP connection */
1072 _cups_globals_t
*cg
= _cupsGlobals(); /* Pointer to library globals */
1073 OSStatus error
= 0; /* Error code */
1074 http_tls_credentials_t credentials
= NULL
;
1075 /* TLS credentials */
1078 DEBUG_printf(("7http_tls_set_credentials(%p)", http
));
1081 * Prefer connection specific credentials...
1084 if ((credentials
= http
->tls_credentials
) == NULL
)
1085 credentials
= cg
->tls_credentials
;
1089 error
= SSLSetCertificate(http
->tls
, credentials
);
1090 DEBUG_printf(("4http_tls_set_credentials: SSLSetCertificate, error=%d",
1094 DEBUG_puts("4http_tls_set_credentials: No credentials to set.");
1101 * 'http_tls_start()' - Set up SSL/TLS support on a connection.
1104 static int /* O - 0 on success, -1 on failure */
1105 http_tls_start(http_t
*http
) /* I - HTTP connection */
1107 char hostname
[256], /* Hostname */
1108 *hostptr
; /* Pointer into hostname */
1109 _cups_globals_t
*cg
= _cupsGlobals();
1110 /* Pointer to library globals */
1111 OSStatus error
; /* Error code */
1112 const char *message
= NULL
;/* Error message */
1113 cups_array_t
*credentials
; /* Credentials array */
1114 cups_array_t
*names
; /* CUPS distinguished names */
1115 CFArrayRef dn_array
; /* CF distinguished names array */
1116 CFIndex count
; /* Number of credentials */
1117 CFDataRef data
; /* Certificate data */
1118 int i
; /* Looping var */
1119 http_credential_t
*credential
; /* Credential data */
1122 DEBUG_printf(("7http_tls_start(http=%p)", http
));
1124 if (http
->mode
== _HTTP_MODE_SERVER
&& !tls_keychain
)
1126 DEBUG_puts("4http_tls_start: cupsSetServerCredentials not called.");
1127 http
->error
= errno
= EINVAL
;
1128 http
->status
= HTTP_STATUS_ERROR
;
1129 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Server credentials not set."), 1);
1134 if ((http
->tls
= SSLCreateContext(kCFAllocatorDefault
, http
->mode
== _HTTP_MODE_CLIENT
? kSSLClientSide
: kSSLServerSide
, kSSLStreamType
)) == NULL
)
1136 DEBUG_puts("4http_tls_start: SSLCreateContext failed.");
1137 http
->error
= errno
= ENOMEM
;
1138 http
->status
= HTTP_STATUS_ERROR
;
1139 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
1144 error
= SSLSetConnection(http
->tls
, http
);
1145 DEBUG_printf(("4http_tls_start: SSLSetConnection, error=%d", (int)error
));
1149 error
= SSLSetIOFuncs(http
->tls
, http_cdsa_read
, http_cdsa_write
);
1150 DEBUG_printf(("4http_tls_start: SSLSetIOFuncs, error=%d", (int)error
));
1155 error
= SSLSetSessionOption(http
->tls
, kSSLSessionOptionBreakOnServerAuth
,
1157 DEBUG_printf(("4http_tls_start: SSLSetSessionOption, error=%d",
1161 if (!error
&& http
->mode
== _HTTP_MODE_CLIENT
)
1164 * Client: set client-side credentials, if any...
1167 if (cg
->client_cert_cb
)
1169 error
= SSLSetSessionOption(http
->tls
,
1170 kSSLSessionOptionBreakOnCertRequested
, true);
1171 DEBUG_printf(("4http_tls_start: kSSLSessionOptionBreakOnCertRequested, "
1172 "error=%d", (int)error
));
1176 error
= http_tls_set_credentials(http
);
1177 DEBUG_printf(("4http_tls_start: http_tls_set_credentials, error=%d",
1184 * Server: find/create a certificate for TLS...
1187 if (http
->fields
[HTTP_FIELD_HOST
][0])
1190 * Use hostname for TLS upgrade...
1193 strlcpy(hostname
, http
->fields
[HTTP_FIELD_HOST
], sizeof(hostname
));
1198 * Resolve hostname from connection address...
1201 http_addr_t addr
; /* Connection address */
1202 socklen_t addrlen
; /* Length of address */
1204 addrlen
= sizeof(addr
);
1205 if (getsockname(http
->fd
, (struct sockaddr
*)&addr
, &addrlen
))
1207 DEBUG_printf(("4http_tls_start: Unable to get socket address: %s", strerror(errno
)));
1210 else if (httpAddrLocalhost(&addr
))
1213 httpAddrString(&addr
, hostname
, sizeof(hostname
));
1216 #ifdef HAVE_SECKEYCHAINOPEN
1218 http
->tls_credentials
= http_cdsa_copy_server(hostname
);
1219 else if (tls_common_name
)
1220 http
->tls_credentials
= http_cdsa_copy_server(tls_common_name
);
1222 if (!http
->tls_credentials
&& tls_auto_create
&& (hostname
[0] || tls_common_name
))
1224 DEBUG_printf(("4http_tls_start: Auto-create credentials for \"%s\".", hostname
[0] ? hostname
: tls_common_name
));
1226 if (!cupsMakeServerCredentials(tls_keypath
, hostname
[0] ? hostname
: tls_common_name
, 0, NULL
, time(NULL
) + 365 * 86400))
1228 DEBUG_puts("4http_tls_start: cupsMakeServerCredentials failed.");
1229 http
->error
= errno
= EINVAL
;
1230 http
->status
= HTTP_STATUS_ERROR
;
1231 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unable to create server credentials."), 1);
1236 http
->tls_credentials
= http_cdsa_copy_server(hostname
[0] ? hostname
: tls_common_name
);
1238 #endif /* HAVE_SECKEYCHAINOPEN */
1240 if (!http
->tls_credentials
)
1242 DEBUG_puts("4http_tls_start: Unable to find server credentials.");
1243 http
->error
= errno
= EINVAL
;
1244 http
->status
= HTTP_STATUS_ERROR
;
1245 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, _("Unable to find server credentials."), 1);
1250 error
= SSLSetCertificate(http
->tls
, http
->tls_credentials
);
1252 DEBUG_printf(("4http_tls_start: SSLSetCertificate, error=%d", (int)error
));
1255 DEBUG_printf(("4http_tls_start: tls_credentials=%p", http
->tls_credentials
));
1258 * Let the server know which hostname/domain we are trying to connect to
1259 * in case it wants to serve up a certificate with a matching common name.
1262 if (!error
&& http
->mode
== _HTTP_MODE_CLIENT
)
1265 * Client: get the hostname to use for TLS...
1268 if (httpAddrLocalhost(http
->hostaddr
))
1270 strlcpy(hostname
, "localhost", sizeof(hostname
));
1275 * Otherwise make sure the hostname we have does not end in a trailing dot.
1278 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
1279 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
1284 error
= SSLSetPeerDomainName(http
->tls
, hostname
, strlen(hostname
));
1286 DEBUG_printf(("4http_tls_start: SSLSetPeerDomainName, error=%d", (int)error
));
1291 int done
= 0; /* Are we done yet? */
1293 while (!error
&& !done
)
1295 error
= SSLHandshake(http
->tls
);
1297 DEBUG_printf(("4http_tls_start: SSLHandshake returned %d.", (int)error
));
1305 case errSSLWouldBlock
:
1306 error
= noErr
; /* Force a retry */
1307 usleep(1000); /* in 1 millisecond */
1310 case errSSLServerAuthCompleted
:
1312 if (cg
->server_cert_cb
)
1314 error
= httpCopyCredentials(http
, &credentials
);
1317 error
= (cg
->server_cert_cb
)(http
, http
->tls
, credentials
,
1318 cg
->server_cert_data
);
1319 httpFreeCredentials(credentials
);
1322 DEBUG_printf(("4http_tls_start: Server certificate callback "
1323 "returned %d.", (int)error
));
1327 case errSSLClientCertRequested
:
1330 if (cg
->client_cert_cb
)
1333 if (!(error
= SSLCopyDistinguishedNames(http
->tls
, &dn_array
)) &&
1336 if ((names
= cupsArrayNew(NULL
, NULL
)) != NULL
)
1338 for (i
= 0, count
= CFArrayGetCount(dn_array
); i
< count
; i
++)
1340 data
= (CFDataRef
)CFArrayGetValueAtIndex(dn_array
, i
);
1342 if ((credential
= malloc(sizeof(*credential
))) != NULL
)
1344 credential
->datalen
= CFDataGetLength(data
);
1345 if ((credential
->data
= malloc(credential
->datalen
)))
1347 memcpy((void *)credential
->data
, CFDataGetBytePtr(data
),
1348 credential
->datalen
);
1349 cupsArrayAdd(names
, credential
);
1357 CFRelease(dn_array
);
1362 error
= (cg
->client_cert_cb
)(http
, http
->tls
, names
,
1363 cg
->client_cert_data
);
1365 DEBUG_printf(("4http_tls_start: Client certificate callback "
1366 "returned %d.", (int)error
));
1369 httpFreeCredentials(names
);
1373 case errSSLUnknownRootCert
:
1374 message
= _("Unable to establish a secure connection to host "
1375 "(untrusted certificate).");
1378 case errSSLNoRootCert
:
1379 message
= _("Unable to establish a secure connection to host "
1380 "(self-signed certificate).");
1383 case errSSLCertExpired
:
1384 message
= _("Unable to establish a secure connection to host "
1385 "(expired certificate).");
1388 case errSSLCertNotYetValid
:
1389 message
= _("Unable to establish a secure connection to host "
1390 "(certificate not yet valid).");
1393 case errSSLHostNameMismatch
:
1394 message
= _("Unable to establish a secure connection to host "
1395 "(host name mismatch).");
1398 case errSSLXCertChainInvalid
:
1399 message
= _("Unable to establish a secure connection to host "
1400 "(certificate chain invalid).");
1403 case errSSLConnectionRefused
:
1404 message
= _("Unable to establish a secure connection to host "
1405 "(peer dropped connection before responding).");
1416 http
->error
= error
;
1417 http
->status
= HTTP_STATUS_ERROR
;
1418 errno
= ECONNREFUSED
;
1420 CFRelease(http
->tls
);
1424 * If an error string wasn't set by the callbacks use a generic one...
1428 #ifdef HAVE_CSSMERRORSTRING
1429 message
= cssmErrorString(error
);
1431 message
= _("Unable to establish a secure connection to host.");
1432 #endif /* HAVE_CSSMERRORSTRING */
1434 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
, message
, 1);
1444 * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
1448 http_tls_stop(http_t
*http
) /* I - HTTP connection */
1450 while (SSLClose(http
->tls
) == errSSLWouldBlock
)
1453 CFRelease(http
->tls
);
1455 if (http
->tls_credentials
)
1456 CFRelease(http
->tls_credentials
);
1459 http
->tls_credentials
= NULL
;
1464 * 'http_tls_write()' - Write to a SSL/TLS connection.
1467 static int /* O - Bytes written */
1468 http_tls_write(http_t
*http
, /* I - HTTP connection */
1469 const char *buf
, /* I - Buffer holding data */
1470 int len
) /* I - Length of buffer */
1472 ssize_t result
; /* Return value */
1473 OSStatus error
; /* Error info */
1474 size_t processed
; /* Number of bytes processed */
1477 DEBUG_printf(("2http_tls_write(http=%p, buf=%p, len=%d)", http
, buf
, len
));
1479 error
= SSLWrite(http
->tls
, buf
, len
, &processed
);
1484 result
= (int)processed
;
1487 case errSSLWouldBlock
:
1490 result
= (int)processed
;
1499 case errSSLClosedGraceful
:
1503 result
= (int)processed
;
1513 DEBUG_printf(("3http_tls_write: Returning %d.", (int)result
));
1515 return ((int)result
);
1521 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
1524 int /* O - 1 on success, 0 on error */
1525 cupsdEndTLS(cupsd_client_t
*con
) /* I - Client connection */
1527 while (SSLClose(con
->http
.tls
) == errSSLWouldBlock
)
1530 CFRelease(con
->http
.tls
);
1531 con
->http
.tls
= NULL
;
1533 if (con
->http
.tls_credentials
)
1534 CFRelease(con
->http
.tls_credentials
);
1541 * 'cupsdStartTLS()' - Start a secure session with the client.
1544 int /* O - 1 on success, 0 on error */
1545 cupsdStartTLS(cupsd_client_t
*con
) /* I - Client connection */
1547 OSStatus error
= 0; /* Error code */
1548 SecTrustRef peerTrust
; /* Peer certificates */
1551 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Client %d] Encrypting connection.",
1554 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
1556 if (!con
->http
.tls_credentials
)
1559 * No keychain (yet), make a self-signed certificate...
1562 if (make_certificate(con
))
1563 con
->http
.tls_credentials
= copy_cdsa_certificate(con
);
1566 if (!con
->http
.tls_credentials
)
1568 cupsdLogMessage(CUPSD_LOG_ERROR
,
1569 "Could not find signing key in keychain \"%s\"",
1571 error
= errSSLBadConfiguration
;
1575 con
->http
.tls
= SSLCreateContext(kCFAllocatorDefault
, kSSLServerSide
,
1579 error
= SSLSetIOFuncs(con
->http
.tls
, http_cdsa_read
, http_cdsa_write
);
1582 error
= SSLSetConnection(con
->http
.tls
, HTTP(con
));
1585 error
= SSLSetCertificate(con
->http
.tls
, con
->http
.tls_credentials
);
1590 * Perform SSL/TLS handshake
1593 while ((error
= SSLHandshake(con
->http
.tls
)) == errSSLWouldBlock
)
1599 cupsdLogMessage(CUPSD_LOG_ERROR
,
1600 "Unable to encrypt connection from %s - %s (%d)",
1601 con
->http
.hostname
, cssmErrorString(error
), (int)error
);
1603 con
->http
.error
= error
;
1604 con
->http
.status
= HTTP_ERROR
;
1608 CFRelease(con
->http
.tls
);
1609 con
->http
.tls
= NULL
;
1612 if (con
->http
.tls_credentials
)
1614 CFRelease(con
->http
.tls_credentials
);
1615 con
->http
.tls_credentials
= NULL
;
1621 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Connection from %s now encrypted.",
1622 con
->http
.hostname
);
1624 if (!SSLCopyPeerTrust(con
->http
.tls
, &peerTrust
) && peerTrust
)
1626 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received %d peer certificates.",
1627 (int)SecTrustGetCertificateCount(peerTrust
));
1628 CFRelease(peerTrust
);
1631 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Received NO peer certificates.");