1/* 2 * "$Id: tls-darwin.c 4057 2012-12-07 21:34:43Z msweet $" 3 * 4 * TLS support code for the CUPS scheduler on OS X. 5 * 6 * Copyright 2007-2012 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 * Contents: 16 * 17 * cupsdEndTLS() - Shutdown a secure session with the client. 18 * cupsdStartTLS() - Start a secure session with the client. 19 * copy_cdsa_certificate() - Copy a SSL/TLS certificate from the System 20 * keychain. 21 * make_certificate() - Make a self-signed SSL/TLS certificate. 22 */ 23 24 25/* 26 * Local functions... 27 */ 28 29static CFArrayRef copy_cdsa_certificate(cupsd_client_t *con); 30static int make_certificate(cupsd_client_t *con); 31 32 33/* 34 * 'cupsdEndTLS()' - Shutdown a secure session with the client. 35 */ 36 37int /* O - 1 on success, 0 on error */ 38cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */ 39{ 40 while (SSLClose(con->http.tls) == errSSLWouldBlock) 41 usleep(1000); 42 43 CFRelease(con->http.tls); 44 con->http.tls = NULL; 45 46 if (con->http.tls_credentials) 47 CFRelease(con->http.tls_credentials); 48 49 return (1); 50} 51 52 53/* 54 * 'cupsdStartTLS()' - Start a secure session with the client. 55 */ 56 57int /* O - 1 on success, 0 on error */ 58cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */ 59{ 60 OSStatus error = 0; /* Error code */ 61 SecTrustRef peerTrust; /* Peer certificates */ 62 63 64 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.", 65 con->http.fd); 66 67 con->http.tls_credentials = copy_cdsa_certificate(con); 68 69 if (!con->http.tls_credentials) 70 { 71 /* 72 * No keychain (yet), make a self-signed certificate... 73 */ 74 75 if (make_certificate(con)) 76 con->http.tls_credentials = copy_cdsa_certificate(con); 77 } 78 79 if (!con->http.tls_credentials) 80 { 81 cupsdLogMessage(CUPSD_LOG_ERROR, 82 "Could not find signing key in keychain \"%s\"", 83 ServerCertificate); 84 error = errSSLBadConfiguration; 85 } 86 87 if (!error) 88 con->http.tls = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, 89 kSSLStreamType); 90 91 if (!error) 92 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA); 93 94 if (!error) 95 error = SSLSetConnection(con->http.tls, HTTP(con)); 96 97 if (!error) 98 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials); 99 100 if (!error) 101 { 102 /* 103 * Perform SSL/TLS handshake 104 */ 105 106 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock) 107 usleep(1000); 108 } 109 110 if (error) 111 { 112 cupsdLogMessage(CUPSD_LOG_ERROR, 113 "Unable to encrypt connection from %s - %s (%d)", 114 con->http.hostname, cssmErrorString(error), (int)error); 115 116 con->http.error = error; 117 con->http.status = HTTP_ERROR; 118 119 if (con->http.tls) 120 { 121 CFRelease(con->http.tls); 122 con->http.tls = NULL; 123 } 124 125 if (con->http.tls_credentials) 126 { 127 CFRelease(con->http.tls_credentials); 128 con->http.tls_credentials = NULL; 129 } 130 131 return (0); 132 } 133 134 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.", 135 con->http.hostname); 136 137 if (!SSLCopyPeerTrust(con->http.tls, &peerTrust) && peerTrust) 138 { 139 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!", 140 (int)SecTrustGetCertificateCount(peerTrust)); 141 CFRelease(peerTrust); 142 } 143 else 144 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!"); 145 146 return (1); 147} 148 149 150/* 151 * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System 152 * keychain. 153 */ 154 155static CFArrayRef /* O - Array of certificates */ 156copy_cdsa_certificate( 157 cupsd_client_t *con) /* I - Client connection */ 158{ 159 OSStatus err; /* Error info */ 160 SecKeychainRef keychain = NULL;/* Keychain reference */ 161 SecIdentitySearchRef search = NULL; /* Search reference */ 162 SecIdentityRef identity = NULL;/* Identity */ 163 CFArrayRef certificates = NULL; 164 /* Certificate array */ 165 SecPolicyRef policy = NULL; /* Policy ref */ 166 CFStringRef servername = NULL; 167 /* Server name */ 168 CFMutableDictionaryRef query = NULL; /* Query qualifiers */ 169 CFArrayRef list = NULL; /* Keychain list */ 170# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 171 char localname[1024];/* Local hostname */ 172# endif /* HAVE_DNSSD || HAVE_AVAHI */ 173 174 175 cupsdLogMessage(CUPSD_LOG_DEBUG, 176 "copy_cdsa_certificate: Looking for certs for \"%s\"...", 177 con->servername); 178 179 if ((err = SecKeychainOpen(ServerCertificate, &keychain))) 180 { 181 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)", 182 ServerCertificate, cssmErrorString(err), (int)err); 183 goto cleanup; 184 } 185 186 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername, 187 kCFStringEncodingUTF8); 188 189 policy = SecPolicyCreateSSL(1, servername); 190 191 if (servername) 192 CFRelease(servername); 193 194 if (!policy) 195 { 196 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference"); 197 goto cleanup; 198 } 199 200 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 201 &kCFTypeDictionaryKeyCallBacks, 202 &kCFTypeDictionaryValueCallBacks))) 203 { 204 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary"); 205 goto cleanup; 206 } 207 208 list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, 209 &kCFTypeArrayCallBacks); 210 211 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity); 212 CFDictionaryAddValue(query, kSecMatchPolicy, policy); 213 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); 214 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne); 215 CFDictionaryAddValue(query, kSecMatchSearchList, list); 216 217 CFRelease(list); 218 219 err = SecItemCopyMatching(query, (CFTypeRef *)&identity); 220 221# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 222 if (err && DNSSDHostName) 223 { 224 /* 225 * Search for the connection server name failed; try the DNS-SD .local 226 * hostname instead... 227 */ 228 229 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName); 230 231 cupsdLogMessage(CUPSD_LOG_DEBUG, 232 "copy_cdsa_certificate: Looking for certs for \"%s\"...", 233 localname); 234 235 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname, 236 kCFStringEncodingUTF8); 237 238 CFRelease(policy); 239 240 policy = SecPolicyCreateSSL(1, servername); 241 242 if (servername) 243 CFRelease(servername); 244 245 if (!policy) 246 { 247 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference"); 248 goto cleanup; 249 } 250 251 CFDictionarySetValue(query, kSecMatchPolicy, policy); 252 253 err = SecItemCopyMatching(query, (CFTypeRef *)&identity); 254 } 255# endif /* HAVE_DNSSD || HAVE_AVAHI */ 256 257 if (err) 258 { 259 cupsdLogMessage(CUPSD_LOG_DEBUG, 260 "Cannot find signing key in keychain \"%s\": %s (%d)", 261 ServerCertificate, cssmErrorString(err), (int)err); 262 goto cleanup; 263 } 264 265 if (CFGetTypeID(identity) != SecIdentityGetTypeID()) 266 { 267 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!"); 268 goto cleanup; 269 } 270 271 if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 272 1, &kCFTypeArrayCallBacks)) == NULL) 273 { 274 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array"); 275 goto cleanup; 276 } 277 278 cleanup : 279 280 if (keychain) 281 CFRelease(keychain); 282 if (search) 283 CFRelease(search); 284 if (identity) 285 CFRelease(identity); 286 287 if (policy) 288 CFRelease(policy); 289 if (query) 290 CFRelease(query); 291 292 return (certificates); 293} 294 295 296/* 297 * 'make_certificate()' - Make a self-signed SSL/TLS certificate. 298 */ 299 300static int /* O - 1 on success, 0 on failure */ 301make_certificate(cupsd_client_t *con) /* I - Client connection */ 302{ 303 int pid, /* Process ID of command */ 304 status; /* Status of command */ 305 char command[1024], /* Command */ 306 *argv[4], /* Command-line arguments */ 307 *envp[MAX_ENV + 1], /* Environment variables */ 308 keychain[1024], /* Keychain argument */ 309 infofile[1024], /* Type-in information for cert */ 310# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 311 localname[1024], /* Local hostname */ 312# endif /* HAVE_DNSSD || HAVE_AVAHI */ 313 *servername; /* Name of server in cert */ 314 cups_file_t *fp; /* Seed/info file */ 315 int infofd; /* Info file descriptor */ 316 317 318# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 319 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName) 320 { 321 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName); 322 servername = localname; 323 } 324 else 325# endif /* HAVE_DNSSD || HAVE_AVAHI */ 326 servername = con->servername; 327 328 /* 329 * Run the "certtool" command to generate a self-signed certificate... 330 */ 331 332 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command))) 333 { 334 cupsdLogMessage(CUPSD_LOG_ERROR, 335 "No SSL certificate and certtool command not found!"); 336 return (0); 337 } 338 339 /* 340 * Create a file with the certificate information fields... 341 * 342 * Note: This assumes that the default questions are asked by the certtool 343 * command... 344 */ 345 346 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) 347 { 348 cupsdLogMessage(CUPSD_LOG_ERROR, 349 "Unable to create certificate information file %s - %s", 350 infofile, strerror(errno)); 351 return (0); 352 } 353 354 cupsFilePrintf(fp, 355 "%s\n" /* Enter key and certificate label */ 356 "r\n" /* Generate RSA key pair */ 357 "2048\n" /* Key size in bits */ 358 "y\n" /* OK (y = yes) */ 359 "b\n" /* Usage (b=signing/encryption) */ 360 "s\n" /* Sign with SHA1 */ 361 "y\n" /* OK (y = yes) */ 362 "%s\n" /* Common name */ 363 "\n" /* Country (default) */ 364 "\n" /* Organization (default) */ 365 "\n" /* Organizational unit (default) */ 366 "\n" /* State/Province (default) */ 367 "%s\n" /* Email address */ 368 "y\n", /* OK (y = yes) */ 369 servername, servername, ServerAdmin); 370 cupsFileClose(fp); 371 372 cupsdLogMessage(CUPSD_LOG_INFO, 373 "Generating SSL server key and certificate..."); 374 375 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate); 376 377 argv[0] = "certtool"; 378 argv[1] = "c"; 379 argv[2] = keychain; 380 argv[3] = NULL; 381 382 cupsdLoadEnv(envp, MAX_ENV); 383 384 infofd = open(infofile, O_RDONLY); 385 386 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL, 387 NULL, &pid)) 388 { 389 close(infofd); 390 unlink(infofile); 391 return (0); 392 } 393 394 close(infofd); 395 unlink(infofile); 396 397 while (waitpid(pid, &status, 0) < 0) 398 if (errno != EINTR) 399 { 400 status = 1; 401 break; 402 } 403 404 cupsdFinishProcess(pid, command, sizeof(command), NULL); 405 406 if (status) 407 { 408 if (WIFEXITED(status)) 409 cupsdLogMessage(CUPSD_LOG_ERROR, 410 "Unable to create SSL server key and certificate - " 411 "the certtool command stopped with status %d!", 412 WEXITSTATUS(status)); 413 else 414 cupsdLogMessage(CUPSD_LOG_ERROR, 415 "Unable to create SSL server key and certificate - " 416 "the certtool command crashed on signal %d!", 417 WTERMSIG(status)); 418 } 419 else 420 { 421 cupsdLogMessage(CUPSD_LOG_INFO, 422 "Created SSL server certificate file \"%s\"...", 423 ServerCertificate); 424 } 425 426 return (!status); 427} 428 429 430/* 431 * End of "$Id: tls-darwin.c 4057 2012-12-07 21:34:43Z msweet $". 432 */ 433