summaryrefslogtreecommitdiff
path: root/scheduler/tls-openssl.c
diff options
context:
space:
mode:
authorDidier Raboud <odyx@debian.org>2012-10-25 21:07:57 +0200
committerDidier Raboud <odyx@debian.org>2012-10-25 21:07:57 +0200
commit81ab83f382660bc7980ae954725c4ebf28764b03 (patch)
tree523268f698a63a8fd44f3491d94d140266b2403b /scheduler/tls-openssl.c
parenta75966e33dbc3e3e096338fd332f515cb313b58a (diff)
downloadcups-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.c353
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 $".
+ */