]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/tls-gnutls.c
0f5b11881654c04d2478cbb871d32b4377f824a9
[thirdparty/cups.git] / cups / tls-gnutls.c
1 /*
2 * "$Id$"
3 *
4 * TLS support code for CUPS using GNU TLS.
5 *
6 * Copyright 2007-2013 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 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18
19 /*
20 * Local functions...
21 */
22
23 //static int make_certificate(cupsd_client_t *con);
24 static ssize_t http_gnutls_read(gnutls_transport_ptr_t ptr, void *data, size_t length);
25 static ssize_t http_gnutls_write(gnutls_transport_ptr_t ptr, const void *data, size_t length);
26
27
28 /*
29 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
30 * an encrypted connection.
31 *
32 * @since CUPS 1.5/OS X 10.7@
33 */
34
35 int /* O - Status of call (0 = success) */
36 httpCopyCredentials(
37 http_t *http, /* I - Connection to server */
38 cups_array_t **credentials) /* O - Array of credentials */
39 {
40 if (credentials)
41 *credentials = NULL;
42
43 if (!http || !http->tls || !credentials)
44 return (-1);
45
46 return (0);
47 }
48
49
50 /*
51 * '_httpCreateCredentials()' - Create credentials in the internal format.
52 */
53
54 http_tls_credentials_t /* O - Internal credentials */
55 _httpCreateCredentials(
56 cups_array_t *credentials) /* I - Array of credentials */
57 {
58 (void)credentials;
59
60 return (NULL);
61 }
62
63
64 /*
65 * '_httpFreeCredentials()' - Free internal credentials.
66 */
67
68 void
69 _httpFreeCredentials(
70 http_tls_credentials_t credentials) /* I - Internal credentials */
71 {
72 (void)credentials;
73 }
74
75
76 /*
77 * 'http_gnutls_read()' - Read function for the GNU TLS library.
78 */
79
80 static ssize_t /* O - Number of bytes read or -1 on error */
81 http_gnutls_read(
82 gnutls_transport_ptr_t ptr, /* I - Connection to server */
83 void *data, /* I - Buffer */
84 size_t length) /* I - Number of bytes to read */
85 {
86 http_t *http; /* HTTP connection */
87 ssize_t bytes; /* Bytes read */
88
89
90 DEBUG_printf(("6http_gnutls_read(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
91
92 http = (http_t *)ptr;
93
94 if (!http->blocking)
95 {
96 /*
97 * Make sure we have data before we read...
98 */
99
100 while (!_httpWait(http, http->wait_value, 0))
101 {
102 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
103 continue;
104
105 http->error = ETIMEDOUT;
106 return (-1);
107 }
108 }
109
110 bytes = recv(http->fd, data, length, 0);
111 DEBUG_printf(("6http_gnutls_read: bytes=%d", (int)bytes));
112 return (bytes);
113 }
114
115
116 /*
117 * 'http_gnutls_write()' - Write function for the GNU TLS library.
118 */
119
120 static ssize_t /* O - Number of bytes written or -1 on error */
121 http_gnutls_write(
122 gnutls_transport_ptr_t ptr, /* I - Connection to server */
123 const void *data, /* I - Data buffer */
124 size_t length) /* I - Number of bytes to write */
125 {
126 ssize_t bytes; /* Bytes written */
127
128
129 DEBUG_printf(("6http_gnutls_write(ptr=%p, data=%p, length=%d)", ptr, data,
130 (int)length));
131 #ifdef DEBUG
132 http_debug_hex("http_gnutls_write", data, (int)length);
133 #endif /* DEBUG */
134
135 bytes = send(((http_t *)ptr)->fd, data, length, 0);
136 DEBUG_printf(("http_gnutls_write: bytes=%d", (int)bytes));
137
138 return (bytes);
139 }
140
141
142 /*
143 * 'http_tls_initialize()' - Initialize the TLS stack.
144 */
145
146 static void
147 http_tls_initialize(void)
148 {
149 /*
150 * Initialize GNU TLS...
151 */
152
153 gnutls_global_init();
154 }
155
156
157 /*
158 * 'http_tls_pending()' - Return the number of pending TLS-encrypted bytes.
159 */
160
161 static size_t
162 http_tls_pending(http_t *http) /* I - HTTP connection */
163 {
164 return (gnutls_record_check_pending(http->tls));
165 }
166
167
168 /*
169 * 'http_tls_read()' - Read from a SSL/TLS connection.
170 */
171
172 static int /* O - Bytes read */
173 http_tls_read(http_t *http, /* I - Connection to server */
174 char *buf, /* I - Buffer to store data */
175 int len) /* I - Length of buffer */
176 {
177 ssize_t result; /* Return value */
178
179
180 result = gnutls_record_recv(http->tls, buf, len);
181
182 if (result < 0 && !errno)
183 {
184 /*
185 * Convert GNU TLS error to errno value...
186 */
187
188 switch (result)
189 {
190 case GNUTLS_E_INTERRUPTED :
191 errno = EINTR;
192 break;
193
194 case GNUTLS_E_AGAIN :
195 errno = EAGAIN;
196 break;
197
198 default :
199 errno = EPIPE;
200 break;
201 }
202
203 result = -1;
204 }
205
206 return ((int)result);
207 }
208
209
210 /*
211 * 'http_tls_set_credentials()' - Set the TLS credentials.
212 */
213
214 static int /* O - Status of connection */
215 http_tls_set_credentials(http_t *http) /* I - Connection to server */
216 {
217 (void)http;
218
219 return (0);
220 }
221
222
223 /*
224 * 'http_tls_start()' - Set up SSL/TLS support on a connection.
225 */
226
227 static int /* O - 0 on success, -1 on failure */
228 http_tls_start(http_t *http) /* I - Connection to server */
229 {
230 char hostname[256], /* Hostname */
231 *hostptr; /* Pointer into hostname */
232 int status; /* Status of handshake */
233 gnutls_certificate_client_credentials *credentials;
234 /* TLS credentials */
235
236
237 DEBUG_printf(("7http_setup_ssl(http=%p)", http));
238
239 /*
240 * Get the hostname to use for SSL...
241 */
242
243 if (httpAddrLocalhost(http->hostaddr))
244 {
245 strlcpy(hostname, "localhost", sizeof(hostname));
246 }
247 else
248 {
249 /*
250 * Otherwise make sure the hostname we have does not end in a trailing dot.
251 */
252
253 strlcpy(hostname, http->hostname, sizeof(hostname));
254 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
255 *hostptr == '.')
256 *hostptr = '\0';
257 }
258
259 credentials = (gnutls_certificate_client_credentials *)
260 malloc(sizeof(gnutls_certificate_client_credentials));
261 if (credentials == NULL)
262 {
263 DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
264 strerror(errno)));
265 http->error = errno;
266 http->status = HTTP_STATUS_ERROR;
267 _cupsSetHTTPError(HTTP_STATUS_ERROR);
268
269 return (-1);
270 }
271
272 gnutls_certificate_allocate_credentials(credentials);
273
274 gnutls_init(&http->tls, GNUTLS_CLIENT);
275 gnutls_set_default_priority(http->tls);
276 gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
277 strlen(hostname));
278 gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
279 gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr_t)http);
280 gnutls_transport_set_pull_function(http->tls, http_gnutls_read);
281 gnutls_transport_set_push_function(http->tls, http_gnutls_write);
282
283 while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
284 {
285 DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
286 status, gnutls_strerror(status)));
287
288 if (gnutls_error_is_fatal(status))
289 {
290 http->error = EIO;
291 http->status = HTTP_STATUS_ERROR;
292
293 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
294
295 gnutls_deinit(http->tls);
296 gnutls_certificate_free_credentials(*credentials);
297 free(credentials);
298 http->tls = NULL;
299
300 return (-1);
301 }
302 }
303
304 http->tls_credentials = credentials;
305
306 // TODO: Put this in the right place; no-op for now, this to get things to compile
307 http_tls_set_credentials(http);
308
309 return (0);
310 }
311
312
313 /*
314 * 'http_tls_stop()' - Shut down SSL/TLS on a connection.
315 */
316
317 static void
318 http_tls_stop(http_t *http) /* I - Connection to server */
319 {
320 gnutls_certificate_client_credentials *credentials;
321 /* TLS credentials */
322
323 credentials = (gnutls_certificate_client_credentials *)(http->tls_credentials);
324
325 gnutls_bye(http->tls, GNUTLS_SHUT_RDWR);
326 gnutls_deinit(http->tls);
327 gnutls_certificate_free_credentials(*credentials);
328 free(credentials);
329 }
330
331
332 /*
333 * 'http_tls_write()' - Write to a SSL/TLS connection.
334 */
335
336 static int /* O - Bytes written */
337 http_tls_write(http_t *http, /* I - Connection to server */
338 const char *buf, /* I - Buffer holding data */
339 int len) /* I - Length of buffer */
340 {
341 ssize_t result; /* Return value */
342
343
344 DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
345
346 result = gnutls_record_send(http->tls, buf, len);
347
348 if (result < 0 && !errno)
349 {
350 /*
351 * Convert GNU TLS error to errno value...
352 */
353
354 switch (result)
355 {
356 case GNUTLS_E_INTERRUPTED :
357 errno = EINTR;
358 break;
359
360 case GNUTLS_E_AGAIN :
361 errno = EAGAIN;
362 break;
363
364 default :
365 errno = EPIPE;
366 break;
367 }
368
369 result = -1;
370 }
371
372 DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
373
374 return ((int)result);
375 }
376
377
378 #if 0
379 /*
380 * 'cupsdEndTLS()' - Shutdown a secure session with the client.
381 */
382
383 int /* O - 1 on success, 0 on error */
384 cupsdEndTLS(cupsd_client_t *con) /* I - Client connection */
385 {
386 int error; /* Error code */
387 gnutls_certificate_server_credentials *credentials;
388 /* TLS credentials */
389
390
391 credentials = (gnutls_certificate_server_credentials *)
392 (con->http.tls_credentials);
393
394 error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
395 switch (error)
396 {
397 case GNUTLS_E_SUCCESS:
398 cupsdLogMessage(CUPSD_LOG_DEBUG,
399 "SSL shutdown successful!");
400 break;
401 default:
402 cupsdLogMessage(CUPSD_LOG_ERROR,
403 "SSL shutdown failed: %s", gnutls_strerror(error));
404 break;
405 }
406
407 gnutls_deinit(con->http.tls);
408 con->http.tls = NULL;
409
410 gnutls_certificate_free_credentials(*credentials);
411 free(credentials);
412
413 return (1);
414 }
415
416
417 /*
418 * 'cupsdStartTLS()' - Start a secure session with the client.
419 */
420
421 int /* O - 1 on success, 0 on error */
422 cupsdStartTLS(cupsd_client_t *con) /* I - Client connection */
423 {
424 int status; /* Error code */
425 gnutls_certificate_server_credentials *credentials;
426 /* TLS credentials */
427
428
429 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Encrypting connection.",
430 con->http.fd);
431
432 /*
433 * Verify that we have a certificate...
434 */
435
436 if (access(ServerKey, 0) || access(ServerCertificate, 0))
437 {
438 /*
439 * Nope, make a self-signed certificate...
440 */
441
442 if (!make_certificate(con))
443 return (0);
444 }
445
446 /*
447 * Create the SSL object and perform the SSL handshake...
448 */
449
450 credentials = (gnutls_certificate_server_credentials *)
451 malloc(sizeof(gnutls_certificate_server_credentials));
452 if (credentials == NULL)
453 {
454 cupsdLogMessage(CUPSD_LOG_ERROR,
455 "Unable to encrypt connection from %s - %s",
456 con->http.hostname, strerror(errno));
457
458 return (0);
459 }
460
461 gnutls_certificate_allocate_credentials(credentials);
462 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
463 ServerKey, GNUTLS_X509_FMT_PEM);
464
465 gnutls_init(&con->http.tls, GNUTLS_SERVER);
466 gnutls_set_default_priority(con->http.tls);
467
468 gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
469 gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr_t)HTTP(con));
470 gnutls_transport_set_pull_function(con->http.tls, http_gnutls_read);
471 gnutls_transport_set_push_function(con->http.tls, http_gnutls_write);
472
473 while ((status = gnutls_handshake(con->http.tls)) != GNUTLS_E_SUCCESS)
474 {
475 if (gnutls_error_is_fatal(status))
476 {
477 cupsdLogMessage(CUPSD_LOG_ERROR,
478 "Unable to encrypt connection from %s - %s",
479 con->http.hostname, gnutls_strerror(status));
480
481 gnutls_deinit(con->http.tls);
482 gnutls_certificate_free_credentials(*credentials);
483 con->http.tls = NULL;
484 free(credentials);
485 return (0);
486 }
487 }
488
489 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
490 con->http.hostname);
491
492 con->http.tls_credentials = credentials;
493 return (1);
494 }
495
496
497 /*
498 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
499 */
500
501 static int /* O - 1 on success, 0 on failure */
502 make_certificate(cupsd_client_t *con) /* I - Client connection */
503 {
504 gnutls_x509_crt crt; /* Self-signed certificate */
505 gnutls_x509_privkey key; /* Encryption key */
506 cups_lang_t *language; /* Default language info */
507 cups_file_t *fp; /* Key/cert file */
508 unsigned char buffer[8192]; /* Buffer for x509 data */
509 size_t bytes; /* Number of bytes of data */
510 unsigned char serial[4]; /* Serial number buffer */
511 time_t curtime; /* Current time */
512 int result; /* Result of GNU TLS calls */
513
514
515 /*
516 * Create the encryption key...
517 */
518
519 cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
520
521 gnutls_x509_privkey_init(&key);
522 gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
523
524 /*
525 * Save it...
526 */
527
528 bytes = sizeof(buffer);
529
530 if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
531 buffer, &bytes)) < 0)
532 {
533 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
534 gnutls_strerror(result));
535 gnutls_x509_privkey_deinit(key);
536 return (0);
537 }
538 else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
539 {
540 cupsFileWrite(fp, (char *)buffer, bytes);
541 cupsFileClose(fp);
542
543 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
544 ServerKey);
545 }
546 else
547 {
548 cupsdLogMessage(CUPSD_LOG_ERROR,
549 "Unable to create SSL server key file \"%s\" - %s",
550 ServerKey, strerror(errno));
551 gnutls_x509_privkey_deinit(key);
552 return (0);
553 }
554
555 /*
556 * Create the self-signed certificate...
557 */
558
559 cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
560
561 language = cupsLangDefault();
562 curtime = time(NULL);
563 serial[0] = curtime >> 24;
564 serial[1] = curtime >> 16;
565 serial[2] = curtime >> 8;
566 serial[3] = curtime;
567
568 gnutls_x509_crt_init(&crt);
569 if (strlen(language->language) == 5)
570 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
571 language->language + 3, 2);
572 else
573 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
574 "US", 2);
575 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
576 ServerName, strlen(ServerName));
577 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
578 ServerName, strlen(ServerName));
579 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
580 0, "Unknown", 7);
581 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
582 "Unknown", 7);
583 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
584 "Unknown", 7);
585 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
586 ServerAdmin, strlen(ServerAdmin));
587 gnutls_x509_crt_set_key(crt, key);
588 gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
589 gnutls_x509_crt_set_activation_time(crt, curtime);
590 gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
591 gnutls_x509_crt_set_ca_status(crt, 0);
592 gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
593 ServerName);
594 gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
595 gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
596 gnutls_x509_crt_set_version(crt, 3);
597
598 bytes = sizeof(buffer);
599 if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
600 gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
601
602 gnutls_x509_crt_sign(crt, crt, key);
603
604 /*
605 * Save it...
606 */
607
608 bytes = sizeof(buffer);
609 if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
610 buffer, &bytes)) < 0)
611 cupsdLogMessage(CUPSD_LOG_ERROR,
612 "Unable to export SSL server certificate - %s",
613 gnutls_strerror(result));
614 else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
615 {
616 cupsFileWrite(fp, (char *)buffer, bytes);
617 cupsFileClose(fp);
618
619 cupsdLogMessage(CUPSD_LOG_INFO,
620 "Created SSL server certificate file \"%s\"...",
621 ServerCertificate);
622 }
623 else
624 cupsdLogMessage(CUPSD_LOG_ERROR,
625 "Unable to create SSL server certificate file \"%s\" - %s",
626 ServerCertificate, strerror(errno));
627
628 /*
629 * Cleanup...
630 */
631
632 gnutls_x509_crt_deinit(crt);
633 gnutls_x509_privkey_deinit(key);
634
635 return (1);
636 }
637 #endif /* 0 */
638
639
640 /*
641 * End of "$Id$".
642 */