pwmc: Reset filename upon .open failure.
[libpwmd.git] / src / tls.c
blob2f6e74e895896e6d9a09de5e193b8cc230950994
1 /*
2 Copyright (C) 2012-2016 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 <sys/socket.h>
32 #include <fcntl.h>
34 #ifdef HAVE_STRINGS_H
35 #include <strings.h>
36 #endif
38 #include "types.h"
39 #include "misc.h"
41 #define DEFAULT_TLS_PRIORITY "SECURE256:SECURE192:SECURE128:" \
42 "-VERS-SSL3.0:-VERS-TLS1.0"
43 #define WAIT_INTERVAL 50000
44 #define TEST_TIMEOUT(pwm, timeout, ret, start, rc) \
45 do { \
46 rc = 0; \
47 if (ret == GNUTLS_E_AGAIN || pwm->cancel) \
48 { \
49 time_t now = time (NULL); \
50 if (pwm->cancel || (timeout && now - start >= timeout)) \
51 { \
52 if (pwm->cancel) \
53 rc = gpg_error (GPG_ERR_CANCELED); \
54 else \
55 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
56 ret = GNUTLS_E_TIMEDOUT; \
57 break; \
58 } \
59 struct timeval totv = { 0, WAIT_INTERVAL }; \
60 select (0, NULL, NULL, NULL, &totv); \
61 } \
62 if (ret != GNUTLS_E_INTERRUPTED) \
63 break; \
64 } \
65 while (0)
67 void
68 tls_free (pwm_t *pwm)
70 struct tls_s *tls = pwm->tcp->tls;
71 int ret = 0;
73 if (!tls)
74 return;
76 pwmd_free (tls->ca);
77 pwmd_free (tls->cert);
78 pwmd_free (tls->key);
79 pwmd_free (tls->priority);
80 pwmd_free (tls->server_fp);
82 if (tls->session)
84 time_t start = time (NULL);
88 gpg_error_t rc = 0;
90 ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR);
91 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
92 if (rc)
93 break;
95 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
97 gnutls_deinit (tls->session);
99 else if (pwm->fd != -1)
101 close (pwm->fd);
102 pwm->fd = -1;
105 if (tls->x509)
106 gnutls_certificate_free_credentials (tls->x509);
108 tls->session = NULL;
109 tls->x509 = NULL;
110 pwmd_free (tls);
111 pwm->tls_error = ret;
114 ssize_t
115 tls_read_hook (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
117 struct tls_s *tls = pwm->tcp->tls;
118 ssize_t ret;
119 time_t start = time (NULL);
121 if (*tls->fd == -1)
122 return -1;
124 (void)fd;
127 gpg_error_t rc = 0;
128 struct timeval tv = { 0, 0 };
129 fd_set fds;
130 int n;
132 FD_ZERO (&fds);
133 FD_SET (*tls->fd, &fds);
134 n = select (*tls->fd+1, &fds, NULL, NULL, &tv);
135 if (n == 0 && tls->nl)
137 char c[] = "#\n";
139 memcpy (data, c, sizeof(c));
140 ret = strlen (c);
141 break;
144 ret = gnutls_record_recv (tls->session, data, len);
145 if (ret == GNUTLS_E_REHANDSHAKE)
149 ret = gnutls_handshake (tls->session);
150 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
151 if (rc)
152 break;
154 while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
156 if (!ret && tls->nl)
158 char c[] = "#\n";
160 memcpy (data, c, sizeof(c));
161 ret = strlen (c);
162 break;
164 else if (ret)
165 break;
166 else
167 ret = GNUTLS_E_AGAIN;
170 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
171 if (rc)
172 break;
174 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
176 if (ret < 0)
177 pwm->tls_error = ret;
179 if (!ret)
180 return 0;
181 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
182 errno = EPIPE;
184 if (ret > 0)
185 tls->nl = ((char *)data)[ret-1] == '\n';
187 return ret < 0 ? -1 : ret;
190 ssize_t
191 tls_write_hook (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
193 struct tls_s *tls = pwm->tcp->tls;
195 /* This is probably the BYE command after a previous call timed
196 * out. If not done, the time(2) call seems to hang.*/
197 if (*tls->fd == -1)
198 return -1;
200 ssize_t ret;
201 time_t start = time (NULL);
203 (void)fd;
206 gpg_error_t rc = 0;
208 ret = gnutls_record_send (tls->session, data, len);
209 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
210 if (rc)
211 break;
213 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
215 if (ret < 0)
216 pwm->tls_error = ret;
218 if (!ret)
219 return 0;
220 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
221 errno = EPIPE;
223 return ret < 0 ? -1 : ret;
226 static gpg_error_t
227 tls_fingerprint (pwm_t *pwm, char **result)
229 gnutls_session_t ses = pwm->tcp->tls->session;
230 gnutls_x509_crt_t crt;
231 const gnutls_datum_t *cert_list;
232 unsigned count;
233 unsigned char buf[32];
234 size_t len = sizeof (buf);
236 *result = NULL;
237 gnutls_x509_crt_init (&crt);
238 if (!crt)
239 return GPG_ERR_ENOMEM;
241 cert_list = gnutls_certificate_get_peers (ses, &count);
242 if (count)
244 pwm->tls_error = gnutls_x509_crt_import (crt, &cert_list[0],
245 GNUTLS_X509_FMT_DER);
246 if (!pwm->tls_error)
247 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
250 gnutls_x509_crt_deinit (crt);
251 if (!count)
253 pwm->tls_error = GNUTLS_CERT_INVALID;
254 return GPG_ERR_MISSING_CERT;
257 if (!pwm->tls_error)
258 *result = bin2hex (buf, len);
260 return !pwm->tls_error ? 0 : GPG_ERR_ENOMEM;
263 static gpg_error_t
264 verify_server_certificate (unsigned status)
266 if (status & GNUTLS_CERT_REVOKED)
268 fprintf (stderr, "server certificate is revoked\n");
269 return GPG_ERR_CERT_REVOKED;
272 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
274 fprintf (stderr, "server certificate has no signer\n");
275 return GPG_ERR_NO_SIGNATURE_SCHEME;
278 if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
280 fprintf (stderr, "server certificate signature verification failed\n");
281 return GPG_ERR_BAD_SIGNATURE;
284 if (status & GNUTLS_CERT_EXPIRED)
286 fprintf (stderr, "server certificate expired\n");
287 return GPG_ERR_CERT_EXPIRED;
290 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
292 fprintf (stderr, "server certificate signer is not from CA\n");
293 return GPG_ERR_BAD_CA_CERT;
296 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
298 fprintf (stderr, "server certificate has insecure algorithm\n");
299 return GPG_ERR_UNSUPPORTED_ALGORITHM;
302 if (status)
304 fprintf (stderr, "server certificate is invalid: %u\n", status);
305 return GPG_ERR_BAD_CERT;
308 return 0;
311 static gpg_error_t
312 verify_certificate (pwm_t *pwm)
314 unsigned int status;
315 const gnutls_datum_t *cert_list;
316 unsigned int cert_list_size;
317 int i;
318 gnutls_x509_crt_t cert;
319 gpg_error_t rc = 0;
321 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
322 if (i < 0)
324 pwm->tls_error = i;
325 return GPG_ERR_BAD_CERT;
328 rc = verify_server_certificate (status);
329 if (rc)
330 return rc;
332 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
333 return GPG_ERR_UNSUPPORTED_CERT;
335 if (gnutls_x509_crt_init (&cert) < 0)
336 return gpg_error_from_errno (ENOMEM);
338 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
339 &cert_list_size);
340 if (!cert_list)
342 rc = GPG_ERR_MISSING_CERT;
343 goto done;
346 for (i = 0; i < cert_list_size; i++)
348 pwm->tls_error = gnutls_x509_crt_import (cert, &cert_list[i],
349 GNUTLS_X509_FMT_DER);
350 if (pwm->tls_error < 0)
352 rc = GPG_ERR_BAD_CERT_CHAIN;
353 goto done;
356 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
358 rc = GPG_ERR_CERT_EXPIRED;
359 goto done;
362 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
364 rc = GPG_ERR_CERT_TOO_YOUNG;
365 goto done;
368 if (pwm->tcp->tls->verify)
370 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
372 rc = GPG_ERR_BAD_CERT_CHAIN;
373 goto done;
378 if (pwm->tcp->tls->server_fp)
380 char *result;
382 rc = tls_fingerprint (pwm, &result);
383 if (!rc)
385 if (!result || !*result ||
386 strcasecmp (result, pwm->tcp->tls->server_fp))
387 rc = GPG_ERR_BAD_CERT;
389 pwmd_free (result);
393 done:
394 gnutls_x509_crt_deinit (cert);
395 return rc;
398 static gpg_error_t
399 tls_init (pwm_t * pwm)
401 gpg_error_t rc;
402 int ret;
403 const char *errstr;
405 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
406 if (ret)
408 pwm->tls_error = ret;
409 return gpg_error_from_errno (ENOMEM);
412 /* The client certificate must be signed by the CA of the pwmd server
413 * certificate in order for the client to authenticate successfully. Man in
414 * the middle attacks are still possible if the attacker is running a pwmd
415 * that doesn't require client certificate authentication. So require the
416 * client to verify the server certificate.
418 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
419 pwm->tcp->tls->ca,
420 GNUTLS_X509_FMT_PEM);
421 if (ret == -1)
423 rc = GPG_ERR_INV_CERT_OBJ;
424 goto fail;
427 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
428 pwm->tcp->tls->cert,
429 pwm->tcp->tls->key,
430 GNUTLS_X509_FMT_PEM);
431 if (ret != GNUTLS_E_SUCCESS)
433 pwm->tls_error = ret;
434 rc = GPG_ERR_INV_CERT_OBJ;
435 goto fail;
438 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
439 if (ret != GNUTLS_E_SUCCESS)
441 pwm->tls_error = ret;
442 rc = GPG_ERR_INV_CERT_OBJ;
443 goto fail;
446 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
447 pwm->tcp->tls->priority
448 ? pwm->tcp->tls->priority
449 : DEFAULT_TLS_PRIORITY,
450 &errstr);
451 if (ret != GNUTLS_E_SUCCESS)
453 pwm->tls_error = ret;
454 rc = GPG_ERR_INV_CERT_OBJ;
455 goto fail;
458 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
459 pwm->tcp->tls->x509);
460 if (ret != GNUTLS_E_SUCCESS)
462 pwm->tls_error = ret;
463 rc = GPG_ERR_INV_CERT_OBJ;
464 goto fail;
467 gnutls_transport_set_ptr (pwm->tcp->tls->session,
468 (gnutls_transport_ptr_t) pwm->fd);
469 if (fcntl (pwm->fd, F_SETFL, O_NONBLOCK) == -1)
471 rc = gpg_error_from_errno (errno);
472 goto fail;
475 time_t start = time (NULL);
478 ret = gnutls_handshake (pwm->tcp->tls->session);
479 TEST_TIMEOUT (pwm, pwm->tcp->tls->timeout, ret, start, rc);
480 if (rc)
481 goto fail;
483 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
485 if (ret != GNUTLS_E_SUCCESS)
487 pwm->tls_error = ret;
488 rc = GPG_ERR_INV_CERT_OBJ;
489 goto fail;
492 rc = verify_certificate (pwm);
494 fail:
495 if (rc)
497 /* Keep the original error since it maybe overwritten during TLS
498 * shutdown. */
499 ret = pwm->tls_error;
500 /* The session may not have completed a handshake. Will crash during
501 * gnutls_bye(). */
502 gnutls_deinit (pwm->tcp->tls->session);
503 pwm->tcp->tls->session = NULL;
504 free_tcp (pwm);
505 pwm->tls_error = ret;
508 return rc;
511 gpg_error_t
512 tls_connect (pwm_t * pwm, const char *host, int port, const char *cert,
513 const char *key, const char *cacert, const char *prio,
514 const char *server_fp, int verify)
516 struct tcp_s *tcp;
517 gpg_error_t rc;
519 if (!cert || !key || !cacert)
520 return GPG_ERR_INV_ARG;
522 tcp = pwmd_calloc (1, sizeof (struct tcp_s));
523 if (!tcp)
524 return GPG_ERR_ENOMEM;
526 pthread_cond_init (&tcp->dns_cond, NULL);
527 pthread_mutex_init (&tcp->dns_mutex, NULL);
528 pwm->tcp = tcp;
529 tcp->port = port;
530 tcp->host = pwmd_strdup (host);
531 if (!tcp->host)
533 rc = GPG_ERR_ENOMEM;
534 goto fail;
537 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
538 if (!tcp->tls)
540 rc = GPG_ERR_ENOMEM;
541 goto fail;
544 tcp->tls->timeout = pwm->socket_timeout;
545 tcp->tls->verify = verify;
546 tcp->tls->cert = pwmd_strdup (cert);
547 if (!tcp->tls->cert)
549 rc = GPG_ERR_ENOMEM;
550 goto fail;
553 tcp->tls->key = pwmd_strdup (key);
554 if (!tcp->tls->key)
556 rc = GPG_ERR_ENOMEM;
557 goto fail;
560 tcp->tls->ca = pwmd_strdup (cacert);
561 if (!tcp->tls->ca)
563 rc = GPG_ERR_ENOMEM;
564 goto fail;
567 if (prio)
569 tcp->tls->priority = pwmd_strdup (prio);
570 if (!tcp->tls->priority)
572 rc = GPG_ERR_ENOMEM;
573 goto fail;
577 if (server_fp)
579 tcp->tls->server_fp = pwmd_strdup (server_fp);
580 if (!tcp->tls->server_fp)
582 rc = GPG_ERR_ENOMEM;
583 goto fail;
587 rc = tcp_connect_common (pwm);
588 if (!rc)
590 rc = tls_init (pwm);
591 if (!rc)
593 pwm->tcp->fd = pwm->tcp->tls->fd = &pwm->fd;
594 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
598 fail:
599 if (rc)
600 free_tcp (pwm);
602 return rc;
606 * tls[46]://[hostname][:port]
608 gpg_error_t
609 tls_parse_url (const char *str, char **host, int *port)
611 *port = 6466;
612 return parse_hostname_common (str, host, port);