1/* 2 * "$Id: tls-openssl.c 3757 2012-03-30 06:13:47Z msweet $" 3 * 4 * TLS support code for the CUPS scheduler using OpenSSL. 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 * make_certificate() - Make a self-signed SSL/TLS certificate. 20 */ 21 22 23/* 24 * Local functions... 25 */ 26 27static int make_certificate(cupsd_client_t *con); 28 29 30/* 31 * 'cupsdEndTLS()' - Shutdown a secure session with the client. 32 */ 33 34int /* O - 1 on success, 0 on error */ 35cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */ 36{ 37 SSL_CTX *context; /* Context for encryption */ 38 unsigned long error; /* Error code */ 39 int status; /* Return status */ 40 41 42 context = SSL_get_SSL_CTX(con->http.tls); 43 44 switch (SSL_shutdown(con->http.tls)) 45 { 46 case 1 : 47 cupsdLogMessage(CUPSD_LOG_DEBUG, 48 "SSL shutdown successful!"); 49 status = 1; 50 break; 51 52 case -1 : 53 cupsdLogMessage(CUPSD_LOG_ERROR, 54 "Fatal error during SSL shutdown!"); 55 56 default : 57 while ((error = ERR_get_error()) != 0) 58 cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s", 59 ERR_error_string(error, NULL)); 60 status = 0; 61 break; 62 } 63 64 SSL_CTX_free(context); 65 SSL_free(con->http.tls); 66 con->http.tls = NULL; 67 68 return (status); 69} 70 71 72/* 73 * 'cupsdStartTLS()' - Start a secure session with the client. 74 */ 75 76int /* O - 1 on success, 0 on error */ 77cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */ 78{ 79 SSL_CTX *context; /* Context for encryption */ 80 BIO *bio; /* BIO data */ 81 unsigned long error; /* Error code */ 82 83 84 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.", 85 con->http.fd); 86 87 /* 88 * Verify that we have a certificate... 89 */ 90 91 if (access(ServerKey, 0) || access(ServerCertificate, 0)) 92 { 93 /* 94 * Nope, make a self-signed certificate... 95 */ 96 97 if (!make_certificate(con)) 98 return (0); 99 } 100 101 /* 102 * Create the SSL context and accept the connection... 103 */ 104 105 context = SSL_CTX_new(SSLv23_server_method()); 106 107 SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */ 108 if (SSLOptions & CUPSD_SSL_NOEMPTY) 109 SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); 110 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM); 111 SSL_CTX_use_certificate_chain_file(context, ServerCertificate); 112 113 bio = BIO_new(_httpBIOMethods()); 114 BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con)); 115 116 con->http.tls = SSL_new(context); 117 SSL_set_bio(con->http.tls, bio, bio); 118 119 if (SSL_accept(con->http.tls) != 1) 120 { 121 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s.", 122 con->http.hostname); 123 124 while ((error = ERR_get_error()) != 0) 125 cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL)); 126 127 SSL_CTX_free(context); 128 SSL_free(con->http.tls); 129 con->http.tls = NULL; 130 return (0); 131 } 132 133 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.", 134 con->http.hostname); 135 136 return (1); 137} 138 139 140/* 141 * 'make_certificate()' - Make a self-signed SSL/TLS certificate. 142 */ 143 144static int /* O - 1 on success, 0 on failure */ 145make_certificate(cupsd_client_t *con) /* I - Client connection */ 146{ 147#ifdef HAVE_WAITPID 148 int pid, /* Process ID of command */ 149 status; /* Status of command */ 150 char command[1024], /* Command */ 151 *argv[12], /* Command-line arguments */ 152 *envp[MAX_ENV + 1], /* Environment variables */ 153 infofile[1024], /* Type-in information for cert */ 154 seedfile[1024]; /* Random number seed file */ 155 int envc, /* Number of environment variables */ 156 bytes; /* Bytes written */ 157 cups_file_t *fp; /* Seed/info file */ 158 int infofd; /* Info file descriptor */ 159 160 161 /* 162 * Run the "openssl" command to seed the random number generator and 163 * generate a self-signed certificate that is good for 10 years: 164 * 165 * openssl rand -rand seedfile 1 166 * 167 * openssl req -new -x509 -keyout ServerKey \ 168 * -out ServerCertificate -days 3650 -nodes 169 * 170 * The seeding step is crucial in ensuring that the openssl command 171 * does not block on systems without sufficient entropy... 172 */ 173 174 if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command))) 175 { 176 cupsdLogMessage(CUPSD_LOG_ERROR, 177 "No SSL certificate and openssl command not found!"); 178 return (0); 179 } 180 181 if (access("/dev/urandom", 0)) 182 { 183 /* 184 * If the system doesn't provide /dev/urandom, then any random source 185 * will probably be blocking-style, so generate some random data to 186 * use as a seed for the certificate. Note that we have already 187 * seeded the random number generator in cupsdInitCerts()... 188 */ 189 190 cupsdLogMessage(CUPSD_LOG_INFO, 191 "Seeding the random number generator..."); 192 193 /* 194 * Write the seed file... 195 */ 196 197 if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL) 198 { 199 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s", 200 seedfile, strerror(errno)); 201 return (0); 202 } 203 204 for (bytes = 0; bytes < 262144; bytes ++) 205 cupsFilePutChar(fp, CUPS_RAND()); 206 207 cupsFileClose(fp); 208 209 /* 210 * Run the openssl command to seed its random number generator... 211 */ 212 213 argv[0] = "openssl"; 214 argv[1] = "rand"; 215 argv[2] = "-rand"; 216 argv[3] = seedfile; 217 argv[4] = "1"; 218 argv[5] = NULL; 219 220 envc = cupsdLoadEnv(envp, MAX_ENV); 221 envp[envc] = NULL; 222 223 if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL, 224 NULL, &pid)) 225 { 226 unlink(seedfile); 227 return (0); 228 } 229 230 while (waitpid(pid, &status, 0) < 0) 231 if (errno != EINTR) 232 { 233 status = 1; 234 break; 235 } 236 237 cupsdFinishProcess(pid, command, sizeof(command), NULL); 238 239 /* 240 * Remove the seed file, as it is no longer needed... 241 */ 242 243 unlink(seedfile); 244 245 if (status) 246 { 247 if (WIFEXITED(status)) 248 cupsdLogMessage(CUPSD_LOG_ERROR, 249 "Unable to seed random number generator - " 250 "the openssl command stopped with status %d!", 251 WEXITSTATUS(status)); 252 else 253 cupsdLogMessage(CUPSD_LOG_ERROR, 254 "Unable to seed random number generator - " 255 "the openssl command crashed on signal %d!", 256 WTERMSIG(status)); 257 258 return (0); 259 } 260 } 261 262 /* 263 * Create a file with the certificate information fields... 264 * 265 * Note: This assumes that the default questions are asked by the openssl 266 * command... 267 */ 268 269 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) 270 { 271 cupsdLogMessage(CUPSD_LOG_ERROR, 272 "Unable to create certificate information file %s - %s", 273 infofile, strerror(errno)); 274 return (0); 275 } 276 277 cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n", 278 ServerName, ServerName, ServerAdmin); 279 cupsFileClose(fp); 280 281 cupsdLogMessage(CUPSD_LOG_INFO, 282 "Generating SSL server key and certificate..."); 283 284 argv[0] = "openssl"; 285 argv[1] = "req"; 286 argv[2] = "-new"; 287 argv[3] = "-x509"; 288 argv[4] = "-keyout"; 289 argv[5] = ServerKey; 290 argv[6] = "-out"; 291 argv[7] = ServerCertificate; 292 argv[8] = "-days"; 293 argv[9] = "3650"; 294 argv[10] = "-nodes"; 295 argv[11] = NULL; 296 297 cupsdLoadEnv(envp, MAX_ENV); 298 299 infofd = open(infofile, O_RDONLY); 300 301 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL, 302 NULL, &pid)) 303 { 304 close(infofd); 305 unlink(infofile); 306 return (0); 307 } 308 309 close(infofd); 310 unlink(infofile); 311 312 while (waitpid(pid, &status, 0) < 0) 313 if (errno != EINTR) 314 { 315 status = 1; 316 break; 317 } 318 319 cupsdFinishProcess(pid, command, sizeof(command), NULL); 320 321 if (status) 322 { 323 if (WIFEXITED(status)) 324 cupsdLogMessage(CUPSD_LOG_ERROR, 325 "Unable to create SSL server key and certificate - " 326 "the openssl command stopped with status %d!", 327 WEXITSTATUS(status)); 328 else 329 cupsdLogMessage(CUPSD_LOG_ERROR, 330 "Unable to create SSL server key and certificate - " 331 "the openssl command crashed on signal %d!", 332 WTERMSIG(status)); 333 } 334 else 335 { 336 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...", 337 ServerKey); 338 cupsdLogMessage(CUPSD_LOG_INFO, 339 "Created SSL server certificate file \"%s\"...", 340 ServerCertificate); 341 } 342 343 return (!status); 344 345#else 346 return (0); 347#endif /* HAVE_WAITPID */ 348} 349 350 351/* 352 * End of "$Id: tls-openssl.c 3757 2012-03-30 06:13:47Z msweet $". 353 */ 354