Show reason the server certificate was rejected.
[libpwmd.git] / src / tls.c
blobc4d2849385ac304caf70749bd3e0e2ca8aab4d8f
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 WAIT_INTERVAL 50000
41 #define TEST_TIMEOUT(timeout, ret, start, rc) \
42 do { \
43 if (ret == GNUTLS_E_AGAIN) \
44 { \
45 time_t now = time (NULL); \
46 if (timeout && now - start >= timeout) \
47 { \
48 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
49 break; \
50 } \
51 struct timeval tv = { 0, WAIT_INTERVAL }; \
52 select (0, NULL, NULL, NULL, &tv); \
53 } \
54 if (ret != GNUTLS_E_INTERRUPTED) \
55 break; \
56 } \
57 while (0)
59 void
60 tls_free (pwm_t *pwm)
62 struct tls_s *tls = pwm->tcp->tls;
63 int ret = 0;
65 if (!tls)
66 return;
68 pwmd_free (tls->ca);
69 pwmd_free (tls->cert);
70 pwmd_free (tls->key);
71 pwmd_free (tls->priority);
72 pwmd_free (tls->server_fp);
74 if (tls->session)
76 time_t start = time (NULL);
80 gpg_error_t rc = 0;
82 ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR);
83 TEST_TIMEOUT (tls->timeout, ret, start, rc);
84 if (rc)
85 break;
87 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
89 gnutls_deinit (tls->session);
91 else if (pwm->fd != -1)
93 close (pwm->fd);
94 pwm->fd = -1;
97 if (tls->x509)
98 gnutls_certificate_free_credentials (tls->x509);
100 tls->session = NULL;
101 tls->x509 = NULL;
102 pwmd_free (tls);
103 pwm->tls_error = ret;
106 ssize_t
107 read_hook_tls (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
109 struct tls_s *tls = pwm->tcp->tls;
111 if (*tls->fd == -1)
112 return -1;
114 ssize_t ret;
115 time_t start = time (NULL);
117 rehandshake:
120 gpg_error_t rc = 0;
122 ret = gnutls_record_recv (tls->session, data, len);
123 if (ret == GNUTLS_E_REHANDSHAKE)
125 ret = gnutls_rehandshake (tls->session);
126 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
127 goto rehandshake;
128 else if (ret != GNUTLS_E_SUCCESS)
130 pwm->tls_error = ret;
131 ret = 0;
132 break;
137 ret = gnutls_handshake (tls->session);
138 rc = 0;
139 TEST_TIMEOUT (tls->timeout, ret, start, rc);
140 if (rc)
142 close (*tls->fd);
143 *tls->fd = -1;
144 errno = ETIMEDOUT;
145 return -1;
148 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
150 pwm->tls_error = ret;
151 continue;
154 TEST_TIMEOUT (tls->timeout, ret, start, rc);
155 if (rc)
157 close (*tls->fd);
158 *tls->fd = -1;
159 errno = ETIMEDOUT;
160 return -1;
163 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
165 if (ret < 0)
166 pwm->tls_error = ret;
168 if (!ret)
169 return 0;
170 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
171 errno = EPIPE;
173 return ret < 0 ? -1 : ret;
176 ssize_t
177 write_hook_tls (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
179 struct tls_s *tls = pwm->tcp->tls;
181 /* This is probably the BYE command after a previous call timed
182 * out. If not done, the time(2) call seems to hang.*/
183 if (*tls->fd == -1)
184 return -1;
186 ssize_t ret;
187 time_t start = time (NULL);
191 gpg_error_t rc = 0;
193 ret = gnutls_record_send (tls->session, data, len);
194 TEST_TIMEOUT (tls->timeout, ret, start, rc);
195 if (rc)
197 close (*tls->fd);
198 *tls->fd = -1;
199 errno = ETIMEDOUT;
200 return -1;
203 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
205 if (ret < 0)
206 pwm->tls_error = ret;
208 if (!ret)
209 return 0;
210 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
211 errno = EPIPE;
213 return ret < 0 ? -1 : ret;
216 static gpg_error_t
217 tls_fingerprint (pwm_t *pwm, char **result)
219 gnutls_session_t ses = pwm->tcp->tls->session;
220 gnutls_x509_crt_t crt;
221 const gnutls_datum_t *cert_list;
222 unsigned count;
223 unsigned char buf[32];
224 size_t len = sizeof (buf);
226 *result = NULL;
227 gnutls_x509_crt_init (&crt);
228 if (!crt)
229 return GPG_ERR_ENOMEM;
231 cert_list = gnutls_certificate_get_peers (ses, &count);
232 if (count)
234 pwm->tls_error = gnutls_x509_crt_import (crt, &cert_list[0],
235 GNUTLS_X509_FMT_DER);
236 if (!pwm->tls_error)
237 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
240 gnutls_x509_crt_deinit (crt);
241 if (!count)
243 pwm->tls_error = GNUTLS_CERT_INVALID;
244 return GPG_ERR_MISSING_CERT;
247 if (!pwm->tls_error)
248 *result = bin2hex (buf, len);
250 return !pwm->tls_error ? 0 : GPG_ERR_ENOMEM;
253 static gpg_error_t
254 verify_server_certificate (unsigned status)
256 if (status & GNUTLS_CERT_REVOKED)
258 fprintf (stderr, "server certificate is revoked\n");
259 return GPG_ERR_CERT_REVOKED;
262 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
264 fprintf (stderr, "server certificate has no signer\n");
265 return GPG_ERR_NO_SIGNATURE_SCHEME;
268 if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
270 fprintf (stderr, "server certificate signature verification failed\n");
271 return GPG_ERR_BAD_SIGNATURE;
274 if (status & GNUTLS_CERT_EXPIRED)
276 fprintf (stderr, "server certificate expired\n");
277 return GPG_ERR_CERT_EXPIRED;
280 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
282 fprintf (stderr, "server certificate signer is not from CA\n");
283 return GPG_ERR_BAD_CA_CERT;
286 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
288 fprintf (stderr, "server certificate has insecure algorithm\n");
289 return GPG_ERR_UNSUPPORTED_ALGORITHM;
292 if (status)
294 fprintf (stderr, "server certificate is invalid: %u\n", status);
295 return GPG_ERR_BAD_CERT;
298 return 0;
301 static gpg_error_t
302 verify_certificate (pwm_t *pwm)
304 unsigned int status;
305 const gnutls_datum_t *cert_list;
306 unsigned int cert_list_size;
307 int i;
308 gnutls_x509_crt_t cert;
309 gpg_error_t rc = 0;
311 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
312 if (i < 0)
314 pwm->tls_error = i;
315 return GPG_ERR_BAD_CERT;
318 rc = verify_server_certificate (status);
319 if (rc)
320 return rc;
322 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
323 return GPG_ERR_UNSUPPORTED_CERT;
325 if (gnutls_x509_crt_init (&cert) < 0)
326 return gpg_error_from_errno (ENOMEM);
328 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
329 &cert_list_size);
330 if (!cert_list)
332 rc = GPG_ERR_MISSING_CERT;
333 goto done;
336 for (i = 0; i < cert_list_size; i++)
338 pwm->tls_error = gnutls_x509_crt_import (cert, &cert_list[i],
339 GNUTLS_X509_FMT_DER);
340 if (pwm->tls_error < 0)
342 rc = GPG_ERR_BAD_CERT_CHAIN;
343 goto done;
346 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
348 rc = GPG_ERR_CERT_EXPIRED;
349 goto done;
352 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
354 rc = GPG_ERR_CERT_TOO_YOUNG;
355 goto done;
358 if (pwm->tcp->tls->verify)
360 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
362 rc = GPG_ERR_BAD_CERT_CHAIN;
363 goto done;
368 if (pwm->tcp->tls->server_fp)
370 char *result;
372 rc = tls_fingerprint (pwm, &result);
373 if (!rc)
375 if (!result || !*result ||
376 strcasecmp (result, pwm->tcp->tls->server_fp))
377 rc = GPG_ERR_BAD_CERT;
379 pwmd_free (result);
383 done:
384 gnutls_x509_crt_deinit (cert);
385 return rc;
388 static gpg_error_t
389 tls_init (pwm_t * pwm)
391 gpg_error_t rc;
392 int ret;
393 const char *errstr;
395 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
396 if (ret)
398 pwm->tls_error = ret;
399 return gpg_error_from_errno (ENOMEM);
402 /* The client certificate must be signed by the CA of the pwmd server
403 * certificate in order for the client to authenticate successfully. Man in
404 * the middle attacks are still possible if the attacker is running a pwmd
405 * that doesn't require client certificate authentication. So require the
406 * client to verify the server certificate.
408 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
409 pwm->tcp->tls->ca,
410 GNUTLS_X509_FMT_PEM);
411 if (ret == -1)
413 rc = GPG_ERR_INV_CERT_OBJ;
414 goto fail;
417 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
418 pwm->tcp->tls->cert,
419 pwm->tcp->tls->key,
420 GNUTLS_X509_FMT_PEM);
421 if (ret != GNUTLS_E_SUCCESS)
423 pwm->tls_error = ret;
424 rc = GPG_ERR_INV_CERT_OBJ;
425 goto fail;
428 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
429 if (ret != GNUTLS_E_SUCCESS)
431 pwm->tls_error = ret;
432 rc = GPG_ERR_INV_CERT_OBJ;
433 goto fail;
436 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
437 pwm->tcp->tls->priority ? pwm->tcp->tls->
438 priority : "SECURE256", &errstr);
439 if (ret != GNUTLS_E_SUCCESS)
441 pwm->tls_error = ret;
442 rc = GPG_ERR_INV_CERT_OBJ;
443 goto fail;
446 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
447 pwm->tcp->tls->x509);
448 if (ret != GNUTLS_E_SUCCESS)
450 pwm->tls_error = ret;
451 rc = GPG_ERR_INV_CERT_OBJ;
452 goto fail;
455 gnutls_transport_set_ptr (pwm->tcp->tls->session,
456 (gnutls_transport_ptr_t) pwm->fd);
457 if (fcntl (pwm->fd, F_SETFL, O_NONBLOCK) == -1)
459 rc = gpg_error_from_errno (errno);
460 goto fail;
463 time_t start = time (NULL);
466 ret = gnutls_handshake (pwm->tcp->tls->session);
467 rc = 0;
468 TEST_TIMEOUT (pwm->tcp->tls->timeout, ret, start, rc);
469 if (rc)
470 goto fail;
472 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
474 if (ret != GNUTLS_E_SUCCESS)
476 pwm->tls_error = ret;
477 rc = GPG_ERR_INV_CERT_OBJ;
478 goto fail;
481 rc = verify_certificate (pwm);
483 fail:
484 if (rc)
486 /* Keep the original error since it maybe overwritten during TLS
487 * shutdown. */
488 ret = pwm->tls_error;
489 free_tcp (pwm);
490 pwm->tls_error = ret;
493 return rc;
496 gpg_error_t
497 _do_tls_connect (pwm_t * pwm, const char *host, int port,
498 const char *cert, const char *key, const char *cacert,
499 const char *prio, const char *server_fp, int verify)
501 struct tcp_s *tcp = pwmd_calloc (1, sizeof (struct tcp_s));
502 gpg_error_t rc;
504 if (!tcp)
505 return GPG_ERR_ENOMEM;
507 if (!cert || !key || !cacert)
509 pwmd_free (tcp);
510 return GPG_ERR_INV_ARG;
513 pwm->tcp = tcp;
514 tcp->port = port;
515 tcp->host = pwmd_strdup (host);
516 if (!tcp->host)
518 rc = GPG_ERR_ENOMEM;
519 goto fail;
522 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
523 if (!tcp->tls)
525 rc = GPG_ERR_ENOMEM;
526 goto fail;
529 tcp->tls->timeout = pwm->socket_timeout;
530 tcp->tls->verify = verify;
531 tcp->tls->cert = pwmd_strdup (cert);
532 if (!tcp->tls->cert)
534 rc = GPG_ERR_ENOMEM;
535 goto fail;
538 tcp->tls->key = pwmd_strdup (key);
539 if (!tcp->tls->key)
541 rc = GPG_ERR_ENOMEM;
542 goto fail;
545 tcp->tls->ca = pwmd_strdup (cacert);
546 if (!tcp->tls->ca)
548 rc = GPG_ERR_ENOMEM;
549 goto fail;
552 if (prio)
554 tcp->tls->priority = pwmd_strdup (prio);
555 if (!tcp->tls->priority)
557 rc = GPG_ERR_ENOMEM;
558 goto fail;
562 if (server_fp)
564 tcp->tls->server_fp = pwmd_strdup (server_fp);
565 if (!tcp->tls->server_fp)
567 rc = GPG_ERR_ENOMEM;
568 goto fail;
572 rc = tcp_connect_common (pwm);
573 if (!rc)
575 rc = tls_init (pwm);
576 if (!rc)
578 pwm->tcp->fd = pwm->tcp->tls->fd = &pwm->fd;
579 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
583 fail:
584 if (rc)
585 free_tcp (pwm);
587 return rc;
591 * tls[46]://[hostname][:port]
593 gpg_error_t
594 _parse_tls_url (const char *str, char **host, int *port)
596 *port = 6466;
597 return parse_hostname_common (str, host, port);