Disable gpg-error memory allocator callback for now.
[libpwmd.git] / src / tls.c
blobd87b4e4f17dbcef3a4104560a27b25e371bde38a
1 /*
2 Copyright (C) 2012, 2013, 2014, 2015, 2016
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of libpwmd.
7 Libpwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Libpwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <fcntl.h>
33 #ifdef HAVE_STRINGS_H
34 #include <strings.h>
35 #endif
37 #include "types.h"
38 #include "misc.h"
40 #define DEFAULT_TLS_PRIORITY "SECURE256:SECURE192:SECURE128:-VERS-SSL3.0"
41 #define WAIT_INTERVAL 50000
42 #define TEST_TIMEOUT(pwm, timeout, ret, start, rc) \
43 do { \
44 rc = 0; \
45 if (ret == GNUTLS_E_AGAIN || pwm->cancel) \
46 { \
47 time_t now = time (NULL); \
48 if (pwm->cancel || (timeout && now - start >= timeout)) \
49 { \
50 if (pwm->cancel) \
51 rc = gpg_error (GPG_ERR_CANCELED); \
52 else \
53 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
54 ret = GNUTLS_E_TIMEDOUT; \
55 break; \
56 } \
57 struct timeval tv = { 0, WAIT_INTERVAL }; \
58 select (0, NULL, NULL, NULL, &tv); \
59 } \
60 if (ret != GNUTLS_E_INTERRUPTED) \
61 break; \
62 } \
63 while (0)
65 void
66 tls_free (pwm_t *pwm)
68 struct tls_s *tls = pwm->tcp->tls;
69 int ret = 0;
71 if (!tls)
72 return;
74 pwmd_free (tls->ca);
75 pwmd_free (tls->cert);
76 pwmd_free (tls->key);
77 pwmd_free (tls->priority);
78 pwmd_free (tls->server_fp);
80 if (tls->session)
82 time_t start = time (NULL);
86 gpg_error_t rc = 0;
88 ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR);
89 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
90 if (rc)
91 break;
93 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
95 gnutls_deinit (tls->session);
97 else if (pwm->fd != -1)
99 close (pwm->fd);
100 pwm->fd = -1;
103 if (tls->x509)
104 gnutls_certificate_free_credentials (tls->x509);
106 tls->session = NULL;
107 tls->x509 = NULL;
108 pwmd_free (tls);
109 pwm->tls_error = ret;
112 ssize_t
113 tls_read_hook (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
115 struct tls_s *tls = pwm->tcp->tls;
116 ssize_t ret;
117 time_t start = time (NULL);
119 if (*tls->fd == -1)
120 return -1;
124 gpg_error_t rc = 0;
125 struct timeval tv = { 0, 0 };
126 fd_set fds;
127 int n;
129 FD_ZERO (&fds);
130 FD_SET (*tls->fd, &fds);
131 n = select (*tls->fd+1, &fds, NULL, NULL, &tv);
132 if (n == 0 && tls->nl)
134 char c[] = "#\n";
136 memcpy (data, c, sizeof(c));
137 ret = strlen (c);
138 break;
141 ret = gnutls_record_recv (tls->session, data, len);
142 if (ret == GNUTLS_E_REHANDSHAKE)
146 ret = gnutls_handshake (tls->session);
147 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
148 if (rc)
149 break;
151 while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
153 if (!ret && tls->nl)
155 char c[] = "#\n";
157 memcpy (data, c, sizeof(c));
158 ret = strlen (c);
159 break;
161 else if (ret)
162 break;
163 else
164 ret = GNUTLS_E_AGAIN;
167 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
168 if (rc)
169 break;
171 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
173 if (ret < 0)
174 pwm->tls_error = ret;
176 if (!ret)
177 return 0;
178 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
179 errno = EPIPE;
181 if (ret > 0)
182 tls->nl = ((char *)data)[ret-1] == '\n';
184 return ret < 0 ? -1 : ret;
187 ssize_t
188 tls_write_hook (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
190 struct tls_s *tls = pwm->tcp->tls;
192 /* This is probably the BYE command after a previous call timed
193 * out. If not done, the time(2) call seems to hang.*/
194 if (*tls->fd == -1)
195 return -1;
197 ssize_t ret;
198 time_t start = time (NULL);
202 gpg_error_t rc = 0;
204 ret = gnutls_record_send (tls->session, data, len);
205 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
206 if (rc)
207 break;
209 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
211 if (ret < 0)
212 pwm->tls_error = ret;
214 if (!ret)
215 return 0;
216 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
217 errno = EPIPE;
219 return ret < 0 ? -1 : ret;
222 static gpg_error_t
223 tls_fingerprint (pwm_t *pwm, char **result)
225 gnutls_session_t ses = pwm->tcp->tls->session;
226 gnutls_x509_crt_t crt;
227 const gnutls_datum_t *cert_list;
228 unsigned count;
229 unsigned char buf[32];
230 size_t len = sizeof (buf);
232 *result = NULL;
233 gnutls_x509_crt_init (&crt);
234 if (!crt)
235 return GPG_ERR_ENOMEM;
237 cert_list = gnutls_certificate_get_peers (ses, &count);
238 if (count)
240 pwm->tls_error = gnutls_x509_crt_import (crt, &cert_list[0],
241 GNUTLS_X509_FMT_DER);
242 if (!pwm->tls_error)
243 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
246 gnutls_x509_crt_deinit (crt);
247 if (!count)
249 pwm->tls_error = GNUTLS_CERT_INVALID;
250 return GPG_ERR_MISSING_CERT;
253 if (!pwm->tls_error)
254 *result = bin2hex (buf, len);
256 return !pwm->tls_error ? 0 : GPG_ERR_ENOMEM;
259 static gpg_error_t
260 verify_server_certificate (unsigned status)
262 if (status & GNUTLS_CERT_REVOKED)
264 fprintf (stderr, "server certificate is revoked\n");
265 return GPG_ERR_CERT_REVOKED;
268 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
270 fprintf (stderr, "server certificate has no signer\n");
271 return GPG_ERR_NO_SIGNATURE_SCHEME;
274 if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
276 fprintf (stderr, "server certificate signature verification failed\n");
277 return GPG_ERR_BAD_SIGNATURE;
280 if (status & GNUTLS_CERT_EXPIRED)
282 fprintf (stderr, "server certificate expired\n");
283 return GPG_ERR_CERT_EXPIRED;
286 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
288 fprintf (stderr, "server certificate signer is not from CA\n");
289 return GPG_ERR_BAD_CA_CERT;
292 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
294 fprintf (stderr, "server certificate has insecure algorithm\n");
295 return GPG_ERR_UNSUPPORTED_ALGORITHM;
298 if (status)
300 fprintf (stderr, "server certificate is invalid: %u\n", status);
301 return GPG_ERR_BAD_CERT;
304 return 0;
307 static gpg_error_t
308 verify_certificate (pwm_t *pwm)
310 unsigned int status;
311 const gnutls_datum_t *cert_list;
312 unsigned int cert_list_size;
313 int i;
314 gnutls_x509_crt_t cert;
315 gpg_error_t rc = 0;
317 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
318 if (i < 0)
320 pwm->tls_error = i;
321 return GPG_ERR_BAD_CERT;
324 rc = verify_server_certificate (status);
325 if (rc)
326 return rc;
328 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
329 return GPG_ERR_UNSUPPORTED_CERT;
331 if (gnutls_x509_crt_init (&cert) < 0)
332 return gpg_error_from_errno (ENOMEM);
334 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
335 &cert_list_size);
336 if (!cert_list)
338 rc = GPG_ERR_MISSING_CERT;
339 goto done;
342 for (i = 0; i < cert_list_size; i++)
344 pwm->tls_error = gnutls_x509_crt_import (cert, &cert_list[i],
345 GNUTLS_X509_FMT_DER);
346 if (pwm->tls_error < 0)
348 rc = GPG_ERR_BAD_CERT_CHAIN;
349 goto done;
352 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
354 rc = GPG_ERR_CERT_EXPIRED;
355 goto done;
358 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
360 rc = GPG_ERR_CERT_TOO_YOUNG;
361 goto done;
364 if (pwm->tcp->tls->verify)
366 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
368 rc = GPG_ERR_BAD_CERT_CHAIN;
369 goto done;
374 if (pwm->tcp->tls->server_fp)
376 char *result;
378 rc = tls_fingerprint (pwm, &result);
379 if (!rc)
381 if (!result || !*result ||
382 strcasecmp (result, pwm->tcp->tls->server_fp))
383 rc = GPG_ERR_BAD_CERT;
385 pwmd_free (result);
389 done:
390 gnutls_x509_crt_deinit (cert);
391 return rc;
394 static gpg_error_t
395 tls_init (pwm_t * pwm)
397 gpg_error_t rc;
398 int ret;
399 const char *errstr;
401 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
402 if (ret)
404 pwm->tls_error = ret;
405 return gpg_error_from_errno (ENOMEM);
408 /* The client certificate must be signed by the CA of the pwmd server
409 * certificate in order for the client to authenticate successfully. Man in
410 * the middle attacks are still possible if the attacker is running a pwmd
411 * that doesn't require client certificate authentication. So require the
412 * client to verify the server certificate.
414 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
415 pwm->tcp->tls->ca,
416 GNUTLS_X509_FMT_PEM);
417 if (ret == -1)
419 rc = GPG_ERR_INV_CERT_OBJ;
420 goto fail;
423 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
424 pwm->tcp->tls->cert,
425 pwm->tcp->tls->key,
426 GNUTLS_X509_FMT_PEM);
427 if (ret != GNUTLS_E_SUCCESS)
429 pwm->tls_error = ret;
430 rc = GPG_ERR_INV_CERT_OBJ;
431 goto fail;
434 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
435 if (ret != GNUTLS_E_SUCCESS)
437 pwm->tls_error = ret;
438 rc = GPG_ERR_INV_CERT_OBJ;
439 goto fail;
442 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
443 pwm->tcp->tls->priority
444 ? pwm->tcp->tls->priority
445 : DEFAULT_TLS_PRIORITY,
446 &errstr);
447 if (ret != GNUTLS_E_SUCCESS)
449 pwm->tls_error = ret;
450 rc = GPG_ERR_INV_CERT_OBJ;
451 goto fail;
454 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
455 pwm->tcp->tls->x509);
456 if (ret != GNUTLS_E_SUCCESS)
458 pwm->tls_error = ret;
459 rc = GPG_ERR_INV_CERT_OBJ;
460 goto fail;
463 gnutls_transport_set_ptr (pwm->tcp->tls->session,
464 (gnutls_transport_ptr_t) pwm->fd);
465 if (fcntl (pwm->fd, F_SETFL, O_NONBLOCK) == -1)
467 rc = gpg_error_from_errno (errno);
468 goto fail;
471 time_t start = time (NULL);
474 ret = gnutls_handshake (pwm->tcp->tls->session);
475 TEST_TIMEOUT (pwm, pwm->tcp->tls->timeout, ret, start, rc);
476 if (rc)
477 goto fail;
479 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
481 if (ret != GNUTLS_E_SUCCESS)
483 pwm->tls_error = ret;
484 rc = GPG_ERR_INV_CERT_OBJ;
485 goto fail;
488 rc = verify_certificate (pwm);
490 fail:
491 if (rc)
493 /* Keep the original error since it maybe overwritten during TLS
494 * shutdown. */
495 ret = pwm->tls_error;
496 /* The session may not have completed a handshake. Will crash during
497 * gnutls_bye(). */
498 gnutls_deinit (pwm->tcp->tls->session);
499 pwm->tcp->tls->session = NULL;
500 free_tcp (pwm);
501 pwm->tls_error = ret;
504 return rc;
507 gpg_error_t
508 tls_connect (pwm_t * pwm, const char *host, int port, const char *cert,
509 const char *key, const char *cacert, const char *prio,
510 const char *server_fp, int verify)
512 struct tcp_s *tcp;
513 gpg_error_t rc;
515 if (!cert || !key || !cacert)
516 return GPG_ERR_INV_ARG;
518 tcp = pwmd_calloc (1, sizeof (struct tcp_s));
519 if (!tcp)
520 return GPG_ERR_ENOMEM;
522 pthread_cond_init (&tcp->dns_cond, NULL);
523 pthread_mutex_init (&tcp->dns_mutex, NULL);
524 pwm->tcp = tcp;
525 tcp->port = port;
526 tcp->host = pwmd_strdup (host);
527 if (!tcp->host)
529 rc = GPG_ERR_ENOMEM;
530 goto fail;
533 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
534 if (!tcp->tls)
536 rc = GPG_ERR_ENOMEM;
537 goto fail;
540 tcp->tls->timeout = pwm->socket_timeout;
541 tcp->tls->verify = verify;
542 tcp->tls->cert = pwmd_strdup (cert);
543 if (!tcp->tls->cert)
545 rc = GPG_ERR_ENOMEM;
546 goto fail;
549 tcp->tls->key = pwmd_strdup (key);
550 if (!tcp->tls->key)
552 rc = GPG_ERR_ENOMEM;
553 goto fail;
556 tcp->tls->ca = pwmd_strdup (cacert);
557 if (!tcp->tls->ca)
559 rc = GPG_ERR_ENOMEM;
560 goto fail;
563 if (prio)
565 tcp->tls->priority = pwmd_strdup (prio);
566 if (!tcp->tls->priority)
568 rc = GPG_ERR_ENOMEM;
569 goto fail;
573 if (server_fp)
575 tcp->tls->server_fp = pwmd_strdup (server_fp);
576 if (!tcp->tls->server_fp)
578 rc = GPG_ERR_ENOMEM;
579 goto fail;
583 rc = tcp_connect_common (pwm);
584 if (!rc)
586 rc = tls_init (pwm);
587 if (!rc)
589 pwm->tcp->fd = pwm->tcp->tls->fd = &pwm->fd;
590 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
594 fail:
595 if (rc)
596 free_tcp (pwm);
598 return rc;
602 * tls[46]://[hostname][:port]
604 gpg_error_t
605 tls_parse_url (const char *str, char **host, int *port)
607 *port = 6466;
608 return parse_hostname_common (str, host, port);