contrib: Add helper to build Android dependencies.
[libpwmd.git] / src / tls.c
blob41dc29c3cd6cdc783700439bf4f4873e6a716b65
1 /*
2 Copyright (C) 2012-2023 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 version 2.1 as published by the Free Software Foundation.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18 USA
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <sys/types.h>
29 #ifdef HAVE_STRINGS_H
30 #include <strings.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
39 #ifndef __MINGW32__
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 #endif
45 #include "types.h"
46 #include "misc.h"
48 #define DEFAULT_TLS_PRIORITY "SECURE256:SECURE192:SECURE128:" \
49 "-VERS-SSL3.0:-VERS-TLS1.0"
50 #define WAIT_INTERVAL 50000
51 #define TEST_TIMEOUT(pwm, timeout, ret, start, rc) \
52 do { \
53 rc = 0; \
54 if (ret == GNUTLS_E_AGAIN || pwm->cancel) \
55 { \
56 time_t now = time (NULL); \
57 if (pwm->cancel || (timeout && now - start >= timeout)) \
58 { \
59 if (pwm->cancel) \
60 rc = gpg_error (GPG_ERR_CANCELED); \
61 else \
62 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
63 ret = GNUTLS_E_TIMEDOUT; \
64 break; \
65 } \
66 struct timeval totv = { 0, WAIT_INTERVAL }; \
67 select (0, NULL, NULL, NULL, &totv); \
68 } \
69 if (ret != GNUTLS_E_INTERRUPTED) \
70 break; \
71 } \
72 while (0)
74 void
75 tls_free (pwm_t *pwm)
77 struct tls_s *tls = pwm->tcp->tls;
78 int ret = 0;
80 if (!tls)
81 return;
83 pwmd_free (tls->ca);
84 pwmd_free (tls->cert);
85 pwmd_free (tls->key);
86 pwmd_free (tls->priority);
87 pwmd_free (tls->server_fp);
89 if (tls->session)
91 time_t start = time (NULL);
95 gpg_error_t rc = 0;
97 ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR);
98 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
99 if (rc)
100 break;
102 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
104 gnutls_deinit (tls->session);
106 else if (pwm->fd != ASSUAN_INVALID_FD)
108 __assuan_close (pwm->ctx, pwm->fd);
109 pwm->fd = ASSUAN_INVALID_FD;
112 if (tls->x509)
113 gnutls_certificate_free_credentials (tls->x509);
115 tls->session = NULL;
116 tls->x509 = NULL;
117 pwmd_free (tls);
118 pwm->tls_error = ret;
121 ssize_t
122 tls_read_hook (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
124 struct tls_s *tls = pwm->tcp->tls;
125 ssize_t ret;
126 time_t start = time (NULL);
128 if (pwm->fd == ASSUAN_INVALID_FD)
129 return -1;
131 (void)fd;
134 gpg_error_t rc = 0;
135 struct timeval tv = { 0, WAIT_INTERVAL };
136 fd_set fds;
137 int n;
139 FD_ZERO (&fds);
140 FD_SET (HANDLE2SOCKET (pwm->fd), &fds);
141 n = select (HANDLE2SOCKET (pwm->fd)+1, &fds, NULL, NULL, &tv);
142 if (n == 0 && tls->nl)
144 char c[] = "#\n";
146 memcpy (data, c, sizeof(c));
147 ret = strlen (c);
148 break;
151 ret = gnutls_record_recv (tls->session, data, len);
152 if (ret == GNUTLS_E_REHANDSHAKE)
156 ret = gnutls_handshake (tls->session);
157 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
158 if (rc)
159 break;
161 while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
163 if (!ret && tls->nl)
165 char c[] = "#\n";
167 memcpy (data, c, sizeof(c));
168 ret = strlen (c);
169 break;
171 else if (ret)
172 break;
173 else
174 ret = GNUTLS_E_AGAIN;
177 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
178 if (rc)
179 break;
181 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
183 if (ret < 0)
184 pwm->tls_error = ret;
186 if (!ret)
187 return 0;
188 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
189 errno = EPIPE;
191 if (ret > 0)
192 tls->nl = ((char *)data)[ret-1] == '\n';
194 return ret < 0 ? -1 : ret;
197 ssize_t
198 tls_write_hook (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
200 struct tls_s *tls = pwm->tcp->tls;
202 /* This is probably the BYE command after a previous call timed
203 * out. If not done, the time(2) call seems to hang.*/
204 if (pwm->fd == ASSUAN_INVALID_FD)
205 return -1;
207 ssize_t ret;
208 time_t start = time (NULL);
210 (void)fd;
213 gpg_error_t rc = 0;
215 ret = gnutls_record_send (tls->session, data, len);
216 TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc);
217 if (rc)
218 break;
220 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
222 if (ret < 0)
223 pwm->tls_error = ret;
225 if (!ret)
226 return 0;
227 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
228 errno = EPIPE;
230 return ret < 0 ? -1 : ret;
233 static gpg_error_t
234 tls_fingerprint (pwm_t *pwm, char **result)
236 gnutls_session_t ses = pwm->tcp->tls->session;
237 gnutls_x509_crt_t crt;
238 const gnutls_datum_t *cert_list;
239 unsigned count;
240 unsigned char buf[32];
241 size_t len = sizeof (buf);
243 *result = NULL;
244 gnutls_x509_crt_init (&crt);
245 if (!crt)
246 return GPG_ERR_ENOMEM;
248 cert_list = gnutls_certificate_get_peers (ses, &count);
249 if (count)
251 pwm->tls_error = gnutls_x509_crt_import (crt, &cert_list[0],
252 GNUTLS_X509_FMT_DER);
253 if (!pwm->tls_error)
254 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
257 gnutls_x509_crt_deinit (crt);
258 if (!count)
260 pwm->tls_error = GNUTLS_CERT_INVALID;
261 return GPG_ERR_MISSING_CERT;
264 if (!pwm->tls_error)
265 *result = bin2hex (buf, len);
267 return !pwm->tls_error ? 0 : GPG_ERR_ENOMEM;
270 static gpg_error_t
271 verify_server_certificate (unsigned status)
273 if (status & GNUTLS_CERT_REVOKED)
275 fprintf (stderr, "server certificate is revoked\n");
276 return GPG_ERR_CERT_REVOKED;
279 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
281 fprintf (stderr, "server certificate has no signer\n");
282 return GPG_ERR_NO_SIGNATURE_SCHEME;
285 if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
287 fprintf (stderr, "server certificate signature verification failed\n");
288 return GPG_ERR_BAD_SIGNATURE;
291 if (status & GNUTLS_CERT_EXPIRED)
293 fprintf (stderr, "server certificate expired\n");
294 return GPG_ERR_CERT_EXPIRED;
297 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
299 fprintf (stderr, "server certificate signer is not from CA\n");
300 return GPG_ERR_BAD_CA_CERT;
303 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
305 fprintf (stderr, "server certificate has insecure algorithm\n");
306 return GPG_ERR_UNSUPPORTED_ALGORITHM;
309 if (status)
311 fprintf (stderr, "server certificate is invalid: %u\n", status);
312 return GPG_ERR_BAD_CERT;
315 return 0;
318 static gpg_error_t
319 verify_certificate (pwm_t *pwm)
321 unsigned int status;
322 const gnutls_datum_t *cert_list;
323 unsigned int cert_list_size;
324 int i;
325 gnutls_x509_crt_t cert;
326 gpg_error_t rc = 0;
328 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
329 if (i < 0)
331 pwm->tls_error = i;
332 return GPG_ERR_BAD_CERT;
335 rc = verify_server_certificate (status);
336 if (rc)
337 return rc;
339 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
340 return GPG_ERR_UNSUPPORTED_CERT;
342 if (gnutls_x509_crt_init (&cert) < 0)
343 return gpg_error_from_errno (ENOMEM);
345 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
346 &cert_list_size);
347 if (!cert_list)
349 rc = GPG_ERR_MISSING_CERT;
350 goto done;
353 for (i = 0; i < cert_list_size; i++)
355 pwm->tls_error = gnutls_x509_crt_import (cert, &cert_list[i],
356 GNUTLS_X509_FMT_DER);
357 if (pwm->tls_error < 0)
359 rc = GPG_ERR_BAD_CERT_CHAIN;
360 goto done;
363 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
365 rc = GPG_ERR_CERT_EXPIRED;
366 goto done;
369 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
371 rc = GPG_ERR_CERT_TOO_YOUNG;
372 goto done;
375 if (pwm->tcp->tls->verify)
377 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
379 rc = GPG_ERR_BAD_CERT_CHAIN;
380 goto done;
385 if (pwm->tcp->tls->server_fp)
387 char *result;
389 rc = tls_fingerprint (pwm, &result);
390 if (!rc)
392 if (!result || !*result ||
393 strcasecmp (result, pwm->tcp->tls->server_fp))
394 rc = GPG_ERR_BAD_CERT;
396 pwmd_free (result);
400 done:
401 gnutls_x509_crt_deinit (cert);
402 return rc;
405 static gpg_error_t
406 tls_init (pwm_t * pwm)
408 gpg_error_t rc;
409 int ret;
410 const char *errstr;
412 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
413 if (ret)
415 pwm->tls_error = ret;
416 return gpg_error_from_errno (ENOMEM);
419 /* The client certificate must be signed by the CA of the pwmd server
420 * certificate in order for the client to authenticate successfully. Man in
421 * the middle attacks are still possible if the attacker is running a pwmd
422 * that doesn't require client certificate authentication. So require the
423 * client to verify the server certificate.
425 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
426 pwm->tcp->tls->ca,
427 GNUTLS_X509_FMT_PEM);
428 if (ret == -1)
430 rc = GPG_ERR_INV_CERT_OBJ;
431 goto fail;
434 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
435 pwm->tcp->tls->cert,
436 pwm->tcp->tls->key,
437 GNUTLS_X509_FMT_PEM);
438 if (ret != GNUTLS_E_SUCCESS)
440 pwm->tls_error = ret;
441 rc = GPG_ERR_INV_CERT_OBJ;
442 goto fail;
445 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
446 if (ret != GNUTLS_E_SUCCESS)
448 pwm->tls_error = ret;
449 rc = GPG_ERR_INV_CERT_OBJ;
450 goto fail;
453 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
454 pwm->tcp->tls->priority
455 ? pwm->tcp->tls->priority
456 : DEFAULT_TLS_PRIORITY,
457 &errstr);
458 if (ret != GNUTLS_E_SUCCESS)
460 pwm->tls_error = ret;
461 rc = GPG_ERR_INV_CERT_OBJ;
462 goto fail;
465 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
466 pwm->tcp->tls->x509);
467 if (ret != GNUTLS_E_SUCCESS)
469 pwm->tls_error = ret;
470 rc = GPG_ERR_INV_CERT_OBJ;
471 goto fail;
474 gnutls_transport_set_ptr (pwm->tcp->tls->session,
475 (gnutls_transport_ptr_t) HANDLE2SOCKET (pwm->fd));
476 rc = set_non_blocking (pwm->fd, 1);
477 if (rc)
478 goto fail;
480 time_t start = time (NULL);
483 ret = gnutls_handshake (pwm->tcp->tls->session);
484 TEST_TIMEOUT (pwm, pwm->tcp->tls->timeout, ret, start, rc);
485 if (rc)
486 goto fail;
488 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
490 if (ret != GNUTLS_E_SUCCESS)
492 pwm->tls_error = ret;
493 rc = GPG_ERR_INV_CERT_OBJ;
494 goto fail;
497 rc = verify_certificate (pwm);
499 fail:
500 if (rc)
502 /* Keep the original error since it maybe overwritten during TLS
503 * shutdown. */
504 ret = pwm->tls_error;
505 /* The session may not have completed a handshake. Will crash during
506 * gnutls_bye(). */
507 gnutls_deinit (pwm->tcp->tls->session);
508 pwm->tcp->tls->session = NULL;
509 free_tcp (pwm);
510 pwm->tls_error = ret;
513 return rc;
516 gpg_error_t
517 tls_connect (pwm_t * pwm, const char *host, int port, const char *cert,
518 const char *key, const char *cacert, const char *prio,
519 const char *server_fp, int verify)
521 struct tcp_s *tcp;
522 gpg_error_t rc;
524 if (!cert || !key || !cacert)
525 return GPG_ERR_INV_ARG;
527 tcp = pwmd_calloc (1, sizeof (struct tcp_s));
528 if (!tcp)
529 return GPG_ERR_ENOMEM;
531 pthread_cond_init (&tcp->dns_cond, NULL);
532 pthread_mutex_init (&tcp->dns_mutex, NULL);
533 pwm->tcp = tcp;
534 tcp->port = port;
535 tcp->host = pwmd_strdup (host);
536 if (!tcp->host)
538 rc = GPG_ERR_ENOMEM;
539 goto fail;
542 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
543 if (!tcp->tls)
545 rc = GPG_ERR_ENOMEM;
546 goto fail;
549 tcp->tls->timeout = pwm->socket_timeout;
550 tcp->tls->verify = verify;
551 tcp->tls->cert = pwmd_strdup (cert);
552 if (!tcp->tls->cert)
554 rc = GPG_ERR_ENOMEM;
555 goto fail;
558 tcp->tls->key = pwmd_strdup (key);
559 if (!tcp->tls->key)
561 rc = GPG_ERR_ENOMEM;
562 goto fail;
565 tcp->tls->ca = pwmd_strdup (cacert);
566 if (!tcp->tls->ca)
568 rc = GPG_ERR_ENOMEM;
569 goto fail;
572 if (prio)
574 tcp->tls->priority = pwmd_strdup (prio);
575 if (!tcp->tls->priority)
577 rc = GPG_ERR_ENOMEM;
578 goto fail;
582 if (server_fp)
584 tcp->tls->server_fp = pwmd_strdup (server_fp);
585 if (!tcp->tls->server_fp)
587 rc = GPG_ERR_ENOMEM;
588 goto fail;
592 rc = tcp_connect_common (pwm);
593 if (!rc)
595 rc = tls_init (pwm);
596 if (!rc)
598 #ifdef __MINGW32__
599 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fh, 0);
600 #else
601 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
602 #endif
606 fail:
607 if (rc)
608 free_tcp (pwm);
610 return rc;
614 * tls[46]://[hostname][:port]
616 gpg_error_t
617 tls_parse_url (const char *str, char **host, int *port)
619 *port = 6466;
620 return parse_hostname_common (str, host, port);