diff options
author | Didier Raboud <odyx@debian.org> | 2012-10-25 21:07:57 +0200 |
---|---|---|
committer | Didier Raboud <odyx@debian.org> | 2012-10-25 21:07:57 +0200 |
commit | 81ab83f382660bc7980ae954725c4ebf28764b03 (patch) | |
tree | 523268f698a63a8fd44f3491d94d140266b2403b /scheduler/tls-openssl.c | |
parent | a75966e33dbc3e3e096338fd332f515cb313b58a (diff) | |
download | cups-upstream/1.6.0.tar.gz |
Imported Upstream version 1.6.0upstream/1.6.0
Diffstat (limited to 'scheduler/tls-openssl.c')
-rw-r--r-- | scheduler/tls-openssl.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/scheduler/tls-openssl.c b/scheduler/tls-openssl.c new file mode 100644 index 00000000..f19d28ae --- /dev/null +++ b/scheduler/tls-openssl.c @@ -0,0 +1,353 @@ +/* + * "$Id: tls-openssl.c 10374 2012-03-22 20:30:20Z mike $" + * + * TLS support code for the CUPS scheduler using OpenSSL. + * + * Copyright 2007-2012 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. + * + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". + * + * Contents: + * + * cupsdEndTLS() - Shutdown a secure session with the client. + * cupsdStartTLS() - Start a secure session with the client. + * make_certificate() - Make a self-signed SSL/TLS certificate. + */ + + +/* + * Local functions... + */ + +static int make_certificate(cupsd_client_t *con); + + +/* + * 'cupsdEndTLS()' - Shutdown a secure session with the client. + */ + +int /* O - 1 on success, 0 on error */ +cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */ +{ + SSL_CTX *context; /* Context for encryption */ + unsigned long error; /* Error code */ + int status; /* Return status */ + + + context = SSL_get_SSL_CTX(con->http.tls); + + switch (SSL_shutdown(con->http.tls)) + { + case 1 : + cupsdLogMessage(CUPSD_LOG_DEBUG, + "SSL shutdown successful!"); + status = 1; + break; + + case -1 : + cupsdLogMessage(CUPSD_LOG_ERROR, + "Fatal error during SSL shutdown!"); + + default : + while ((error = ERR_get_error()) != 0) + cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s", + ERR_error_string(error, NULL)); + status = 0; + break; + } + + SSL_CTX_free(context); + SSL_free(con->http.tls); + con->http.tls = NULL; + + return (status); +} + + +/* + * 'cupsdStartTLS()' - Start a secure session with the client. + */ + +int /* O - 1 on success, 0 on error */ +cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */ +{ + SSL_CTX *context; /* Context for encryption */ + BIO *bio; /* BIO data */ + unsigned long error; /* Error code */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.", + con->http.fd); + + /* + * Verify that we have a certificate... + */ + + if (access(ServerKey, 0) || access(ServerCertificate, 0)) + { + /* + * Nope, make a self-signed certificate... + */ + + if (!make_certificate(con)) + return (0); + } + + /* + * Create the SSL context and accept the connection... + */ + + context = SSL_CTX_new(SSLv23_server_method()); + + SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */ + if (SSLOptions & CUPSD_SSL_NOEMPTY) + SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM); + SSL_CTX_use_certificate_chain_file(context, ServerCertificate); + + bio = BIO_new(_httpBIOMethods()); + BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con)); + + con->http.tls = SSL_new(context); + SSL_set_bio(con->http.tls, bio, bio); + + if (SSL_accept(con->http.tls) != 1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s.", + con->http.hostname); + + while ((error = ERR_get_error()) != 0) + cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL)); + + SSL_CTX_free(context); + SSL_free(con->http.tls); + con->http.tls = NULL; + return (0); + } + + cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.", + con->http.hostname); + + return (1); +} + + +/* + * 'make_certificate()' - Make a self-signed SSL/TLS certificate. + */ + +static int /* O - 1 on success, 0 on failure */ +make_certificate(cupsd_client_t *con) /* I - Client connection */ +{ +#ifdef HAVE_WAITPID + int pid, /* Process ID of command */ + status; /* Status of command */ + char command[1024], /* Command */ + *argv[12], /* Command-line arguments */ + *envp[MAX_ENV + 1], /* Environment variables */ + infofile[1024], /* Type-in information for cert */ + seedfile[1024]; /* Random number seed file */ + int envc, /* Number of environment variables */ + bytes; /* Bytes written */ + cups_file_t *fp; /* Seed/info file */ + int infofd; /* Info file descriptor */ + + + /* + * Run the "openssl" command to seed the random number generator and + * generate a self-signed certificate that is good for 10 years: + * + * openssl rand -rand seedfile 1 + * + * openssl req -new -x509 -keyout ServerKey \ + * -out ServerCertificate -days 3650 -nodes + * + * The seeding step is crucial in ensuring that the openssl command + * does not block on systems without sufficient entropy... + */ + + if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command))) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "No SSL certificate and openssl command not found!"); + return (0); + } + + if (access("/dev/urandom", 0)) + { + /* + * If the system doesn't provide /dev/urandom, then any random source + * will probably be blocking-style, so generate some random data to + * use as a seed for the certificate. Note that we have already + * seeded the random number generator in cupsdInitCerts()... + */ + + cupsdLogMessage(CUPSD_LOG_INFO, + "Seeding the random number generator..."); + + /* + * Write the seed file... + */ + + if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s", + seedfile, strerror(errno)); + return (0); + } + + for (bytes = 0; bytes < 262144; bytes ++) + cupsFilePutChar(fp, CUPS_RAND()); + + cupsFileClose(fp); + + /* + * Run the openssl command to seed its random number generator... + */ + + argv[0] = "openssl"; + argv[1] = "rand"; + argv[2] = "-rand"; + argv[3] = seedfile; + argv[4] = "1"; + argv[5] = NULL; + + envc = cupsdLoadEnv(envp, MAX_ENV); + envp[envc] = NULL; + + if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL, + NULL, &pid)) + { + unlink(seedfile); + return (0); + } + + while (waitpid(pid, &status, 0) < 0) + if (errno != EINTR) + { + status = 1; + break; + } + + cupsdFinishProcess(pid, command, sizeof(command), NULL); + + /* + * Remove the seed file, as it is no longer needed... + */ + + unlink(seedfile); + + if (status) + { + if (WIFEXITED(status)) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to seed random number generator - " + "the openssl command stopped with status %d!", + WEXITSTATUS(status)); + else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to seed random number generator - " + "the openssl command crashed on signal %d!", + WTERMSIG(status)); + + return (0); + } + } + + /* + * Create a file with the certificate information fields... + * + * Note: This assumes that the default questions are asked by the openssl + * command... + */ + + if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to create certificate information file %s - %s", + infofile, strerror(errno)); + return (0); + } + + cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n", + ServerName, ServerName, ServerAdmin); + cupsFileClose(fp); + + cupsdLogMessage(CUPSD_LOG_INFO, + "Generating SSL server key and certificate..."); + + argv[0] = "openssl"; + argv[1] = "req"; + argv[2] = "-new"; + argv[3] = "-x509"; + argv[4] = "-keyout"; + argv[5] = ServerKey; + argv[6] = "-out"; + argv[7] = ServerCertificate; + argv[8] = "-days"; + argv[9] = "3650"; + argv[10] = "-nodes"; + argv[11] = NULL; + + cupsdLoadEnv(envp, MAX_ENV); + + infofd = open(infofile, O_RDONLY); + + if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL, + NULL, &pid)) + { + close(infofd); + unlink(infofile); + return (0); + } + + close(infofd); + unlink(infofile); + + while (waitpid(pid, &status, 0) < 0) + if (errno != EINTR) + { + status = 1; + break; + } + + cupsdFinishProcess(pid, command, sizeof(command), NULL); + + if (status) + { + if (WIFEXITED(status)) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to create SSL server key and certificate - " + "the openssl command stopped with status %d!", + WEXITSTATUS(status)); + else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to create SSL server key and certificate - " + "the openssl command crashed on signal %d!", + WTERMSIG(status)); + } + else + { + cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...", + ServerKey); + cupsdLogMessage(CUPSD_LOG_INFO, + "Created SSL server certificate file \"%s\"...", + ServerCertificate); + } + + return (!status); + +#else + return (0); +#endif /* HAVE_WAITPID */ +} + + +/* + * End of "$Id: tls-openssl.c 10374 2012-03-22 20:30:20Z mike $". + */ |