Fixup for Coverity issue #101137.
[libpwmd.git] / src / tls.c
blob2624a24dab52bae11a4a8d9fd0597e0da5f5f933
1 /*
2 Copyright (C) 2012, 2013, 2014, 2015
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(timeout, ret, start, rc) \
43 do { \
44 if (ret == GNUTLS_E_AGAIN) \
45 { \
46 time_t now = time (NULL); \
47 if (timeout && now - start >= timeout) \
48 { \
49 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
50 break; \
51 } \
52 struct timeval tv = { 0, WAIT_INTERVAL }; \
53 select (0, NULL, NULL, NULL, &tv); \
54 } \
55 if (ret != GNUTLS_E_INTERRUPTED) \
56 break; \
57 } \
58 while (0)
60 void
61 tls_free (pwm_t *pwm)
63 struct tls_s *tls = pwm->tcp->tls;
64 int ret = 0;
66 if (!tls)
67 return;
69 pwmd_free (tls->ca);
70 pwmd_free (tls->cert);
71 pwmd_free (tls->key);
72 pwmd_free (tls->priority);
73 pwmd_free (tls->server_fp);
75 if (tls->session)
77 time_t start = time (NULL);
81 gpg_error_t rc = 0;
83 ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR);
84 TEST_TIMEOUT (tls->timeout, ret, start, rc);
85 if (rc)
86 break;
88 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
90 gnutls_deinit (tls->session);
92 else if (pwm->fd != -1)
94 close (pwm->fd);
95 pwm->fd = -1;
98 if (tls->x509)
99 gnutls_certificate_free_credentials (tls->x509);
101 tls->session = NULL;
102 tls->x509 = NULL;
103 pwmd_free (tls);
104 pwm->tls_error = ret;
107 ssize_t
108 read_hook_tls (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
110 struct tls_s *tls = pwm->tcp->tls;
112 if (*tls->fd == -1)
113 return -1;
115 ssize_t ret;
116 time_t start = time (NULL);
118 rehandshake:
121 gpg_error_t rc = 0;
123 ret = gnutls_record_recv (tls->session, data, len);
124 if (ret == GNUTLS_E_REHANDSHAKE)
126 ret = gnutls_rehandshake (tls->session);
127 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
128 goto rehandshake;
129 else if (ret != GNUTLS_E_SUCCESS)
131 pwm->tls_error = ret;
132 ret = 0;
133 break;
138 ret = gnutls_handshake (tls->session);
139 rc = 0;
140 TEST_TIMEOUT (tls->timeout, ret, start, rc);
141 if (rc)
143 close (*tls->fd);
144 *tls->fd = -1;
145 errno = ETIMEDOUT;
146 return -1;
149 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
151 pwm->tls_error = ret;
152 continue;
155 TEST_TIMEOUT (tls->timeout, ret, start, rc);
156 if (rc)
158 close (*tls->fd);
159 *tls->fd = -1;
160 errno = ETIMEDOUT;
161 return -1;
164 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
166 if (ret < 0)
167 pwm->tls_error = ret;
169 if (!ret)
170 return 0;
171 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
172 errno = EPIPE;
174 return ret < 0 ? -1 : ret;
177 ssize_t
178 write_hook_tls (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
180 struct tls_s *tls = pwm->tcp->tls;
182 /* This is probably the BYE command after a previous call timed
183 * out. If not done, the time(2) call seems to hang.*/
184 if (*tls->fd == -1)
185 return -1;
187 ssize_t ret;
188 time_t start = time (NULL);
192 gpg_error_t rc = 0;
194 ret = gnutls_record_send (tls->session, data, len);
195 TEST_TIMEOUT (tls->timeout, ret, start, rc);
196 if (rc)
198 close (*tls->fd);
199 *tls->fd = -1;
200 errno = ETIMEDOUT;
201 return -1;
204 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
206 if (ret < 0)
207 pwm->tls_error = ret;
209 if (!ret)
210 return 0;
211 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
212 errno = EPIPE;
214 return ret < 0 ? -1 : ret;
217 static gpg_error_t
218 tls_fingerprint (pwm_t *pwm, char **result)
220 gnutls_session_t ses = pwm->tcp->tls->session;
221 gnutls_x509_crt_t crt;
222 const gnutls_datum_t *cert_list;
223 unsigned count;
224 unsigned char buf[32];
225 size_t len = sizeof (buf);
227 *result = NULL;
228 gnutls_x509_crt_init (&crt);
229 if (!crt)
230 return GPG_ERR_ENOMEM;
232 cert_list = gnutls_certificate_get_peers (ses, &count);
233 if (count)
235 pwm->tls_error = gnutls_x509_crt_import (crt, &cert_list[0],
236 GNUTLS_X509_FMT_DER);
237 if (!pwm->tls_error)
238 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
241 gnutls_x509_crt_deinit (crt);
242 if (!count)
244 pwm->tls_error = GNUTLS_CERT_INVALID;
245 return GPG_ERR_MISSING_CERT;
248 if (!pwm->tls_error)
249 *result = bin2hex (buf, len);
251 return !pwm->tls_error ? 0 : GPG_ERR_ENOMEM;
254 static gpg_error_t
255 verify_server_certificate (unsigned status)
257 if (status & GNUTLS_CERT_REVOKED)
259 fprintf (stderr, "server certificate is revoked\n");
260 return GPG_ERR_CERT_REVOKED;
263 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
265 fprintf (stderr, "server certificate has no signer\n");
266 return GPG_ERR_NO_SIGNATURE_SCHEME;
269 if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
271 fprintf (stderr, "server certificate signature verification failed\n");
272 return GPG_ERR_BAD_SIGNATURE;
275 if (status & GNUTLS_CERT_EXPIRED)
277 fprintf (stderr, "server certificate expired\n");
278 return GPG_ERR_CERT_EXPIRED;
281 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
283 fprintf (stderr, "server certificate signer is not from CA\n");
284 return GPG_ERR_BAD_CA_CERT;
287 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
289 fprintf (stderr, "server certificate has insecure algorithm\n");
290 return GPG_ERR_UNSUPPORTED_ALGORITHM;
293 if (status)
295 fprintf (stderr, "server certificate is invalid: %u\n", status);
296 return GPG_ERR_BAD_CERT;
299 return 0;
302 static gpg_error_t
303 verify_certificate (pwm_t *pwm)
305 unsigned int status;
306 const gnutls_datum_t *cert_list;
307 unsigned int cert_list_size;
308 int i;
309 gnutls_x509_crt_t cert;
310 gpg_error_t rc = 0;
312 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
313 if (i < 0)
315 pwm->tls_error = i;
316 return GPG_ERR_BAD_CERT;
319 rc = verify_server_certificate (status);
320 if (rc)
321 return rc;
323 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
324 return GPG_ERR_UNSUPPORTED_CERT;
326 if (gnutls_x509_crt_init (&cert) < 0)
327 return gpg_error_from_errno (ENOMEM);
329 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
330 &cert_list_size);
331 if (!cert_list)
333 rc = GPG_ERR_MISSING_CERT;
334 goto done;
337 for (i = 0; i < cert_list_size; i++)
339 pwm->tls_error = gnutls_x509_crt_import (cert, &cert_list[i],
340 GNUTLS_X509_FMT_DER);
341 if (pwm->tls_error < 0)
343 rc = GPG_ERR_BAD_CERT_CHAIN;
344 goto done;
347 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
349 rc = GPG_ERR_CERT_EXPIRED;
350 goto done;
353 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
355 rc = GPG_ERR_CERT_TOO_YOUNG;
356 goto done;
359 if (pwm->tcp->tls->verify)
361 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
363 rc = GPG_ERR_BAD_CERT_CHAIN;
364 goto done;
369 if (pwm->tcp->tls->server_fp)
371 char *result;
373 rc = tls_fingerprint (pwm, &result);
374 if (!rc)
376 if (!result || !*result ||
377 strcasecmp (result, pwm->tcp->tls->server_fp))
378 rc = GPG_ERR_BAD_CERT;
380 pwmd_free (result);
384 done:
385 gnutls_x509_crt_deinit (cert);
386 return rc;
389 static gpg_error_t
390 tls_init (pwm_t * pwm)
392 gpg_error_t rc;
393 int ret;
394 const char *errstr;
396 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
397 if (ret)
399 pwm->tls_error = ret;
400 return gpg_error_from_errno (ENOMEM);
403 /* The client certificate must be signed by the CA of the pwmd server
404 * certificate in order for the client to authenticate successfully. Man in
405 * the middle attacks are still possible if the attacker is running a pwmd
406 * that doesn't require client certificate authentication. So require the
407 * client to verify the server certificate.
409 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
410 pwm->tcp->tls->ca,
411 GNUTLS_X509_FMT_PEM);
412 if (ret == -1)
414 rc = GPG_ERR_INV_CERT_OBJ;
415 goto fail;
418 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
419 pwm->tcp->tls->cert,
420 pwm->tcp->tls->key,
421 GNUTLS_X509_FMT_PEM);
422 if (ret != GNUTLS_E_SUCCESS)
424 pwm->tls_error = ret;
425 rc = GPG_ERR_INV_CERT_OBJ;
426 goto fail;
429 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
430 if (ret != GNUTLS_E_SUCCESS)
432 pwm->tls_error = ret;
433 rc = GPG_ERR_INV_CERT_OBJ;
434 goto fail;
437 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
438 pwm->tcp->tls->priority
439 ? pwm->tcp->tls->priority
440 : DEFAULT_TLS_PRIORITY,
441 &errstr);
442 if (ret != GNUTLS_E_SUCCESS)
444 pwm->tls_error = ret;
445 rc = GPG_ERR_INV_CERT_OBJ;
446 goto fail;
449 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
450 pwm->tcp->tls->x509);
451 if (ret != GNUTLS_E_SUCCESS)
453 pwm->tls_error = ret;
454 rc = GPG_ERR_INV_CERT_OBJ;
455 goto fail;
458 gnutls_transport_set_ptr (pwm->tcp->tls->session,
459 (gnutls_transport_ptr_t) pwm->fd);
460 if (fcntl (pwm->fd, F_SETFL, O_NONBLOCK) == -1)
462 rc = gpg_error_from_errno (errno);
463 goto fail;
466 time_t start = time (NULL);
469 ret = gnutls_handshake (pwm->tcp->tls->session);
470 rc = 0;
471 TEST_TIMEOUT (pwm->tcp->tls->timeout, ret, start, rc);
472 if (rc)
473 goto fail;
475 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
477 if (ret != GNUTLS_E_SUCCESS)
479 pwm->tls_error = ret;
480 rc = GPG_ERR_INV_CERT_OBJ;
481 goto fail;
484 rc = verify_certificate (pwm);
486 fail:
487 if (rc)
489 /* Keep the original error since it maybe overwritten during TLS
490 * shutdown. */
491 ret = pwm->tls_error;
492 /* The session may not have completed a handshake. Will crash during
493 * gnutls_bye(). */
494 gnutls_deinit (pwm->tcp->tls->session);
495 pwm->tcp->tls->session = NULL;
496 free_tcp (pwm);
497 pwm->tls_error = ret;
500 return rc;
503 gpg_error_t
504 _do_tls_connect (pwm_t * pwm, const char *host, int port,
505 const char *cert, const char *key, const char *cacert,
506 const char *prio, const char *server_fp, int verify)
508 struct tcp_s *tcp = pwmd_calloc (1, sizeof (struct tcp_s));
509 gpg_error_t rc;
511 if (!tcp)
512 return GPG_ERR_ENOMEM;
514 if (!cert || !key || !cacert)
516 pwmd_free (tcp);
517 return GPG_ERR_INV_ARG;
520 pwm->tcp = tcp;
521 tcp->port = port;
522 tcp->host = pwmd_strdup (host);
523 if (!tcp->host)
525 rc = GPG_ERR_ENOMEM;
526 goto fail;
529 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
530 if (!tcp->tls)
532 rc = GPG_ERR_ENOMEM;
533 goto fail;
536 tcp->tls->timeout = pwm->socket_timeout;
537 tcp->tls->verify = verify;
538 tcp->tls->cert = pwmd_strdup (cert);
539 if (!tcp->tls->cert)
541 rc = GPG_ERR_ENOMEM;
542 goto fail;
545 tcp->tls->key = pwmd_strdup (key);
546 if (!tcp->tls->key)
548 rc = GPG_ERR_ENOMEM;
549 goto fail;
552 tcp->tls->ca = pwmd_strdup (cacert);
553 if (!tcp->tls->ca)
555 rc = GPG_ERR_ENOMEM;
556 goto fail;
559 if (prio)
561 tcp->tls->priority = pwmd_strdup (prio);
562 if (!tcp->tls->priority)
564 rc = GPG_ERR_ENOMEM;
565 goto fail;
569 if (server_fp)
571 tcp->tls->server_fp = pwmd_strdup (server_fp);
572 if (!tcp->tls->server_fp)
574 rc = GPG_ERR_ENOMEM;
575 goto fail;
579 rc = tcp_connect_common (pwm);
580 if (!rc)
582 rc = tls_init (pwm);
583 if (!rc)
585 pwm->tcp->fd = pwm->tcp->tls->fd = &pwm->fd;
586 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
590 fail:
591 if (rc)
592 free_tcp (pwm);
594 return rc;
598 * tls[46]://[hostname][:port]
600 gpg_error_t
601 _parse_tls_url (const char *str, char **host, int *port)
603 *port = 6466;
604 return parse_hostname_common (str, host, port);