]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/tls-openssl.c
94c9e71e64bfe9b3da06fa101056ea985ab2c97a
[thirdparty/cups.git] / scheduler / tls-openssl.c
1 /*
2 * "$Id$"
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
27 static int make_certificate(cupsd_client_t *con);
28
29
30 /*
31 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
32 */
33
34 int /* O - 1 on success, 0 on error */
35 cupsdEndTLS(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
76 int /* O - 1 on success, 0 on error */
77 cupsdStartTLS(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
144 static int /* O - 1 on success, 0 on failure */
145 make_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$".
353 */