Version 8.2.1.
[libpwmd.git] / src / tls.c
blob7b8d4f60e505acebaac0b12d8ec43f87dd0f8baf
1 /*
2 Copyright (C) 2012-2018 Ben Kibbey <bjk@luxsci.net>
4 This file is part of libpwmd.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <sys/types.h>
31 #include <fcntl.h>
33 #ifdef HAVE_STRINGS_H
34 #include <strings.h>
35 #endif
37 #ifndef __MINGW32__
38 #include <sys/socket.h>
39 #endif
41 #include "types.h"
42 #include "misc.h"
44 #define DEFAULT_TLS_PRIORITY "SECURE256:SECURE192:SECURE128:" \
45 "-VERS-SSL3.0:-VERS-TLS1.0"
46 #define WAIT_INTERVAL 50000
47 #define TEST_TIMEOUT(pwm, timeout, ret, start, rc) \
48 do { \
49 rc = 0; \
50 if (ret == GNUTLS_E_AGAIN || pwm->cancel) \
51 { \
52 time_t now = time (NULL); \
53 if (pwm->cancel || (timeout && now - start >= timeout)) \
54 { \
55 if (pwm->cancel) \
56 rc = gpg_error (GPG_ERR_CANCELED); \
57 else \
58 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
59 ret = GNUTLS_E_TIMEDOUT; \
60 break; \
61 } \
62 struct timeval totv = { 0, WAIT_INTERVAL }; \
63 select (0, NULL, NULL, NULL, &totv); \
64 } \
65 if (ret != GNUTLS_E_INTERRUPTED) \
66 break; \
67 } \
68 while (0)
70 void
71 tls_free (pwm_t *pwm)
73 struct tls_s *tls = pwm->tcp->tls;
74 int ret = 0;
76 if (!tls)
77 return;
79 pwmd_free (tls->ca);
80 pwmd_free (tls->cert);
81 pwmd_free (tls->key);
82 pwmd_free (tls->priority);
83 pwmd_free (tls->server_fp);
85 if (tls->session)
87 time_t start = time (NULL);
91 gpg_error_t rc = 0;
93 ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR);
94 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
95 if (rc)
96 break;
98 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
100 gnutls_deinit (tls->session);
102 else if (pwm->fd != ASSUAN_INVALID_FD)
104 __assuan_close (pwm->ctx, pwm->fd);
105 pwm->fd = ASSUAN_INVALID_FD;
108 if (tls->x509)
109 gnutls_certificate_free_credentials (tls->x509);
111 tls->session = NULL;
112 tls->x509 = NULL;
113 pwmd_free (tls);
114 pwm->tls_error = ret;
117 ssize_t
118 tls_read_hook (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
120 struct tls_s *tls = pwm->tcp->tls;
121 ssize_t ret;
122 time_t start = time (NULL);
124 if (pwm->fd == ASSUAN_INVALID_FD)
125 return -1;
127 (void)fd;
130 gpg_error_t rc = 0;
131 struct timeval tv = { 0, 0 };
132 fd_set fds;
133 int n;
135 FD_ZERO (&fds);
136 FD_SET (HANDLE2SOCKET (pwm->fd), &fds);
137 n = select (HANDLE2SOCKET (pwm->fd)+1, &fds, NULL, NULL, &tv);
138 if (n == 0 && tls->nl)
140 char c[] = "#\n";
142 memcpy (data, c, sizeof(c));
143 ret = strlen (c);
144 break;
147 ret = gnutls_record_recv (tls->session, data, len);
148 if (ret == GNUTLS_E_REHANDSHAKE)
152 ret = gnutls_handshake (tls->session);
153 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
154 if (rc)
155 break;
157 while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
159 if (!ret && tls->nl)
161 char c[] = "#\n";
163 memcpy (data, c, sizeof(c));
164 ret = strlen (c);
165 break;
167 else if (ret)
168 break;
169 else
170 ret = GNUTLS_E_AGAIN;
173 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
174 if (rc)
175 break;
177 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
179 if (ret < 0)
180 pwm->tls_error = ret;
182 if (!ret)
183 return 0;
184 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
185 errno = EPIPE;
187 if (ret > 0)
188 tls->nl = ((char *)data)[ret-1] == '\n';
190 return ret < 0 ? -1 : ret;
193 ssize_t
194 tls_write_hook (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
196 struct tls_s *tls = pwm->tcp->tls;
198 /* This is probably the BYE command after a previous call timed
199 * out. If not done, the time(2) call seems to hang.*/
200 if (pwm->fd == ASSUAN_INVALID_FD)
201 return -1;
203 ssize_t ret;
204 time_t start = time (NULL);
206 (void)fd;
209 gpg_error_t rc = 0;
211 ret = gnutls_record_send (tls->session, data, len);
212 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
213 if (rc)
214 break;
216 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
218 if (ret < 0)
219 pwm->tls_error = ret;
221 if (!ret)
222 return 0;
223 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
224 errno = EPIPE;
226 return ret < 0 ? -1 : ret;
229 static gpg_error_t
230 tls_fingerprint (pwm_t *pwm, char **result)
232 gnutls_session_t ses = pwm->tcp->tls->session;
233 gnutls_x509_crt_t crt;
234 const gnutls_datum_t *cert_list;
235 unsigned count;
236 unsigned char buf[32];
237 size_t len = sizeof (buf);
239 *result = NULL;
240 gnutls_x509_crt_init (&crt);
241 if (!crt)
242 return GPG_ERR_ENOMEM;
244 cert_list = gnutls_certificate_get_peers (ses, &count);
245 if (count)
247 pwm->tls_error = gnutls_x509_crt_import (crt, &cert_list[0],
248 GNUTLS_X509_FMT_DER);
249 if (!pwm->tls_error)
250 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
253 gnutls_x509_crt_deinit (crt);
254 if (!count)
256 pwm->tls_error = GNUTLS_CERT_INVALID;
257 return GPG_ERR_MISSING_CERT;
260 if (!pwm->tls_error)
261 *result = bin2hex (buf, len);
263 return !pwm->tls_error ? 0 : GPG_ERR_ENOMEM;
266 static gpg_error_t
267 verify_server_certificate (unsigned status)
269 if (status & GNUTLS_CERT_REVOKED)
271 fprintf (stderr, "server certificate is revoked\n");
272 return GPG_ERR_CERT_REVOKED;
275 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
277 fprintf (stderr, "server certificate has no signer\n");
278 return GPG_ERR_NO_SIGNATURE_SCHEME;
281 if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
283 fprintf (stderr, "server certificate signature verification failed\n");
284 return GPG_ERR_BAD_SIGNATURE;
287 if (status & GNUTLS_CERT_EXPIRED)
289 fprintf (stderr, "server certificate expired\n");
290 return GPG_ERR_CERT_EXPIRED;
293 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
295 fprintf (stderr, "server certificate signer is not from CA\n");
296 return GPG_ERR_BAD_CA_CERT;
299 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
301 fprintf (stderr, "server certificate has insecure algorithm\n");
302 return GPG_ERR_UNSUPPORTED_ALGORITHM;
305 if (status)
307 fprintf (stderr, "server certificate is invalid: %u\n", status);
308 return GPG_ERR_BAD_CERT;
311 return 0;
314 static gpg_error_t
315 verify_certificate (pwm_t *pwm)
317 unsigned int status;
318 const gnutls_datum_t *cert_list;
319 unsigned int cert_list_size;
320 int i;
321 gnutls_x509_crt_t cert;
322 gpg_error_t rc = 0;
324 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
325 if (i < 0)
327 pwm->tls_error = i;
328 return GPG_ERR_BAD_CERT;
331 rc = verify_server_certificate (status);
332 if (rc)
333 return rc;
335 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
336 return GPG_ERR_UNSUPPORTED_CERT;
338 if (gnutls_x509_crt_init (&cert) < 0)
339 return gpg_error_from_errno (ENOMEM);
341 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
342 &cert_list_size);
343 if (!cert_list)
345 rc = GPG_ERR_MISSING_CERT;
346 goto done;
349 for (i = 0; i < cert_list_size; i++)
351 pwm->tls_error = gnutls_x509_crt_import (cert, &cert_list[i],
352 GNUTLS_X509_FMT_DER);
353 if (pwm->tls_error < 0)
355 rc = GPG_ERR_BAD_CERT_CHAIN;
356 goto done;
359 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
361 rc = GPG_ERR_CERT_EXPIRED;
362 goto done;
365 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
367 rc = GPG_ERR_CERT_TOO_YOUNG;
368 goto done;
371 if (pwm->tcp->tls->verify)
373 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
375 rc = GPG_ERR_BAD_CERT_CHAIN;
376 goto done;
381 if (pwm->tcp->tls->server_fp)
383 char *result;
385 rc = tls_fingerprint (pwm, &result);
386 if (!rc)
388 if (!result || !*result ||
389 strcasecmp (result, pwm->tcp->tls->server_fp))
390 rc = GPG_ERR_BAD_CERT;
392 pwmd_free (result);
396 done:
397 gnutls_x509_crt_deinit (cert);
398 return rc;
401 static gpg_error_t
402 tls_init (pwm_t * pwm)
404 gpg_error_t rc;
405 int ret;
406 const char *errstr;
408 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
409 if (ret)
411 pwm->tls_error = ret;
412 return gpg_error_from_errno (ENOMEM);
415 /* The client certificate must be signed by the CA of the pwmd server
416 * certificate in order for the client to authenticate successfully. Man in
417 * the middle attacks are still possible if the attacker is running a pwmd
418 * that doesn't require client certificate authentication. So require the
419 * client to verify the server certificate.
421 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
422 pwm->tcp->tls->ca,
423 GNUTLS_X509_FMT_PEM);
424 if (ret == -1)
426 rc = GPG_ERR_INV_CERT_OBJ;
427 goto fail;
430 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
431 pwm->tcp->tls->cert,
432 pwm->tcp->tls->key,
433 GNUTLS_X509_FMT_PEM);
434 if (ret != GNUTLS_E_SUCCESS)
436 pwm->tls_error = ret;
437 rc = GPG_ERR_INV_CERT_OBJ;
438 goto fail;
441 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
442 if (ret != GNUTLS_E_SUCCESS)
444 pwm->tls_error = ret;
445 rc = GPG_ERR_INV_CERT_OBJ;
446 goto fail;
449 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
450 pwm->tcp->tls->priority
451 ? pwm->tcp->tls->priority
452 : DEFAULT_TLS_PRIORITY,
453 &errstr);
454 if (ret != GNUTLS_E_SUCCESS)
456 pwm->tls_error = ret;
457 rc = GPG_ERR_INV_CERT_OBJ;
458 goto fail;
461 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
462 pwm->tcp->tls->x509);
463 if (ret != GNUTLS_E_SUCCESS)
465 pwm->tls_error = ret;
466 rc = GPG_ERR_INV_CERT_OBJ;
467 goto fail;
470 gnutls_transport_set_ptr (pwm->tcp->tls->session,
471 (gnutls_transport_ptr_t) HANDLE2SOCKET (pwm->fd));
472 rc = set_non_blocking (pwm->fd, 1);
473 if (rc)
474 goto fail;
476 time_t start = time (NULL);
479 ret = gnutls_handshake (pwm->tcp->tls->session);
480 TEST_TIMEOUT (pwm, pwm->tcp->tls->timeout, ret, start, rc);
481 if (rc)
482 goto fail;
484 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
486 if (ret != GNUTLS_E_SUCCESS)
488 pwm->tls_error = ret;
489 rc = GPG_ERR_INV_CERT_OBJ;
490 goto fail;
493 rc = verify_certificate (pwm);
495 fail:
496 if (rc)
498 /* Keep the original error since it maybe overwritten during TLS
499 * shutdown. */
500 ret = pwm->tls_error;
501 /* The session may not have completed a handshake. Will crash during
502 * gnutls_bye(). */
503 gnutls_deinit (pwm->tcp->tls->session);
504 pwm->tcp->tls->session = NULL;
505 free_tcp (pwm);
506 pwm->tls_error = ret;
509 return rc;
512 gpg_error_t
513 tls_connect (pwm_t * pwm, const char *host, int port, const char *cert,
514 const char *key, const char *cacert, const char *prio,
515 const char *server_fp, int verify)
517 struct tcp_s *tcp;
518 gpg_error_t rc;
520 if (!cert || !key || !cacert)
521 return GPG_ERR_INV_ARG;
523 tcp = pwmd_calloc (1, sizeof (struct tcp_s));
524 if (!tcp)
525 return GPG_ERR_ENOMEM;
527 pthread_cond_init (&tcp->dns_cond, NULL);
528 pthread_mutex_init (&tcp->dns_mutex, NULL);
529 pwm->tcp = tcp;
530 tcp->port = port;
531 tcp->host = pwmd_strdup (host);
532 if (!tcp->host)
534 rc = GPG_ERR_ENOMEM;
535 goto fail;
538 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
539 if (!tcp->tls)
541 rc = GPG_ERR_ENOMEM;
542 goto fail;
545 tcp->tls->timeout = pwm->socket_timeout;
546 tcp->tls->verify = verify;
547 tcp->tls->cert = pwmd_strdup (cert);
548 if (!tcp->tls->cert)
550 rc = GPG_ERR_ENOMEM;
551 goto fail;
554 tcp->tls->key = pwmd_strdup (key);
555 if (!tcp->tls->key)
557 rc = GPG_ERR_ENOMEM;
558 goto fail;
561 tcp->tls->ca = pwmd_strdup (cacert);
562 if (!tcp->tls->ca)
564 rc = GPG_ERR_ENOMEM;
565 goto fail;
568 if (prio)
570 tcp->tls->priority = pwmd_strdup (prio);
571 if (!tcp->tls->priority)
573 rc = GPG_ERR_ENOMEM;
574 goto fail;
578 if (server_fp)
580 tcp->tls->server_fp = pwmd_strdup (server_fp);
581 if (!tcp->tls->server_fp)
583 rc = GPG_ERR_ENOMEM;
584 goto fail;
588 rc = tcp_connect_common (pwm);
589 if (!rc)
591 rc = tls_init (pwm);
592 if (!rc)
594 #ifdef __MINGW32__
595 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fh, 0);
596 #else
597 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
598 #endif
602 fail:
603 if (rc)
604 free_tcp (pwm);
606 return rc;
610 * tls[46]://[hostname][:port]
612 gpg_error_t
613 tls_parse_url (const char *str, char **host, int *port)
615 *port = 6466;
616 return parse_hostname_common (str, host, port);