Require an SHA-256 hash for an TLS fingerprint.
[libpwmd.git] / src / tls.c
blob673d445bdeb1db46d3688f14876a6722119cfac2
1 /*
2 Copyright (C) 2012, 2013, 2014
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 } \
52 if (ret != GNUTLS_E_INTERRUPTED) \
53 break; \
54 struct timeval tv = { 0, WAIT_INTERVAL }; \
55 select (0, NULL, NULL, NULL, &tv); \
56 } \
57 while (0)
59 void
60 tls_free (struct tls_s *tls)
62 if (!tls)
63 return;
65 pwmd_free (tls->ca);
66 pwmd_free (tls->cert);
67 pwmd_free (tls->key);
68 pwmd_free (tls->priority);
69 pwmd_free (tls->server_fp);
71 if (tls->session)
73 int ret;
74 time_t start = time (NULL);
78 gpg_error_t rc = 0;
80 ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR);
81 TEST_TIMEOUT (tls->timeout, ret, start, rc);
82 if (rc)
83 break;
85 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
87 gnutls_deinit (tls->session);
90 if (tls->x509)
91 gnutls_certificate_free_credentials (tls->x509);
93 tls->session = NULL;
94 tls->x509 = NULL;
95 pwmd_free (tls);
98 ssize_t
99 read_hook_tls (struct tls_s *tls, assuan_fd_t fd, void *data, size_t len)
101 if (*tls->fd == -1)
102 return -1;
104 ssize_t ret;
105 time_t start = time (NULL);
107 rehandshake:
110 gpg_error_t rc = 0;
112 ret = gnutls_record_recv (tls->session, data, len);
113 if (ret == GNUTLS_E_REHANDSHAKE)
115 ret = gnutls_rehandshake (tls->session);
116 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
117 goto rehandshake;
118 else if (ret != GNUTLS_E_SUCCESS)
120 ret = 0;
121 break;
126 ret = gnutls_handshake (tls->session);
127 rc = 0;
128 TEST_TIMEOUT (tls->timeout, ret, start, rc);
129 if (rc)
131 close (*tls->fd);
132 *tls->fd = -1;
133 errno = ETIMEDOUT;
134 return -1;
137 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
139 if (rc)
140 break;
142 continue;
145 TEST_TIMEOUT (tls->timeout, ret, start, rc);
146 if (rc)
148 close (*tls->fd);
149 *tls->fd = -1;
150 errno = ETIMEDOUT;
151 return -1;
154 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
156 if (!ret)
157 return 0;
158 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
159 errno = EPIPE;
161 return ret < 0 ? -1 : ret;
164 ssize_t
165 write_hook_tls (struct tls_s * tls, assuan_fd_t fd, const void *data,
166 size_t len)
168 /* This is probably the BYE command after a previous call timed
169 * out. If not done, the time(2) call seems to hang.*/
170 if (*tls->fd == -1)
171 return -1;
173 ssize_t ret;
174 time_t start = time (NULL);
178 gpg_error_t rc = 0;
180 ret = gnutls_record_send (tls->session, data, len);
181 TEST_TIMEOUT (tls->timeout, ret, start, rc);
182 if (rc)
184 close (*tls->fd);
185 *tls->fd = -1;
186 errno = ETIMEDOUT;
187 return -1;
190 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
192 if (!ret)
193 return 0;
194 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
195 errno = EPIPE;
197 return ret < 0 ? -1 : ret;
200 static gpg_error_t
201 tls_fingerprint (gnutls_session_t ses, char **result)
203 gnutls_x509_crt_t crt;
204 const gnutls_datum_t *cert_list;
205 unsigned count;
206 unsigned char buf[32];
207 size_t len = sizeof (buf);
209 *result = NULL;
210 gnutls_x509_crt_init (&crt);
211 if (!crt)
212 return GPG_ERR_ENOMEM;
214 cert_list = gnutls_certificate_get_peers (ses, &count);
215 if (count)
217 gnutls_x509_crt_import (crt, &cert_list[0], GNUTLS_X509_FMT_DER);
218 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
221 gnutls_x509_crt_deinit (crt);
222 *result = bin2hex (buf, len);
223 return *result ? 0 : GPG_ERR_ENOMEM;
226 static gpg_error_t
227 verify_certificate (pwm_t * pwm)
229 unsigned int status;
230 const gnutls_datum_t *cert_list;
231 unsigned int cert_list_size;
232 int i;
233 gnutls_x509_crt_t cert;
234 gpg_error_t rc = 0;
236 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
237 if (i < 0)
239 gnutls_perror (i);
240 return GPG_ERR_BAD_CERT;
243 if (status & GNUTLS_CERT_INVALID)
244 return GPG_ERR_BAD_CERT;
246 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
247 return GPG_ERR_BAD_SIGNATURE;
249 if (status & GNUTLS_CERT_REVOKED)
250 return GPG_ERR_CERT_REVOKED;
252 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
253 return GPG_ERR_UNSUPPORTED_CERT;
255 if (gnutls_x509_crt_init (&cert) < 0)
256 return gpg_error_from_errno (ENOMEM);
258 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
259 &cert_list_size);
260 if (!cert_list)
262 rc = GPG_ERR_MISSING_CERT;
263 goto done;
266 for (i = 0; i < cert_list_size; i++)
268 if (gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER) <
271 rc = GPG_ERR_BAD_CERT_CHAIN;
272 goto done;
275 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
277 rc = GPG_ERR_CERT_EXPIRED;
278 goto done;
281 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
283 rc = GPG_ERR_CERT_TOO_YOUNG;
284 goto done;
287 if (pwm->tcp->tls->verify)
289 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
291 rc = GPG_ERR_BAD_CERT_CHAIN;
292 goto done;
297 if (pwm->tcp->tls->server_fp)
299 char *result;
301 rc = tls_fingerprint (pwm->tcp->tls->session, &result);
302 if (!rc)
304 if (!result || !*result ||
305 strcasecmp (result, pwm->tcp->tls->server_fp))
306 rc = GPG_ERR_BAD_CERT;
308 pwmd_free (result);
312 done:
313 gnutls_x509_crt_deinit (cert);
314 return rc;
317 static gpg_error_t
318 tls_init (pwm_t * pwm)
320 gpg_error_t rc;
321 int ret;
322 const char *errstr;
324 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
325 if (ret)
326 return gpg_error_from_errno (ENOMEM);
328 /* The client certificate must be signed by the CA of the pwmd server
329 * certificate in order for the client to authenticate successfully. Man in
330 * the middle attacks are still possible if the attacker is running a pwmd
331 * that doesn't require client certificate authentication. So require the
332 * client to verify the server certificate.
334 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
335 pwm->tcp->tls->ca,
336 GNUTLS_X509_FMT_PEM);
337 if (ret == -1)
339 gnutls_perror (ret);
340 rc = GPG_ERR_INV_CERT_OBJ;
341 goto fail;
344 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
345 pwm->tcp->tls->cert,
346 pwm->tcp->tls->key,
347 GNUTLS_X509_FMT_PEM);
348 if (ret != GNUTLS_E_SUCCESS)
350 gnutls_perror (ret);
351 rc = GPG_ERR_INV_CERT_OBJ;
352 goto fail;
355 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
356 if (ret != GNUTLS_E_SUCCESS)
358 gnutls_perror (ret);
359 rc = GPG_ERR_INV_CERT_OBJ;
360 goto fail;
363 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
364 pwm->tcp->tls->priority ? pwm->tcp->tls->
365 priority : "SECURE256", &errstr);
366 if (ret != GNUTLS_E_SUCCESS)
368 gnutls_perror (ret);
369 rc = GPG_ERR_INV_CERT_OBJ;
370 goto fail;
373 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
374 pwm->tcp->tls->x509);
375 if (ret != GNUTLS_E_SUCCESS)
377 gnutls_perror (ret);
378 rc = GPG_ERR_INV_CERT_OBJ;
379 goto fail;
382 gnutls_transport_set_ptr (pwm->tcp->tls->session,
383 (gnutls_transport_ptr_t) pwm->fd);
384 fcntl (pwm->fd, F_SETFL, O_NONBLOCK);
385 time_t start = time (NULL);
388 ret = gnutls_handshake (pwm->tcp->tls->session);
389 rc = 0;
390 TEST_TIMEOUT (pwm->tcp->tls->timeout, ret, start, rc);
391 if (rc)
392 goto fail;
394 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
396 if (ret != GNUTLS_E_SUCCESS)
398 gnutls_perror (ret);
399 rc = GPG_ERR_INV_CERT_OBJ;
400 goto fail;
403 rc = verify_certificate (pwm);
405 fail:
406 if (rc)
408 free_tcp (pwm->tcp);
409 pwm->tcp = NULL;
412 return rc;
415 gpg_error_t
416 _do_tls_connect (pwm_t * pwm, const char *host, int port,
417 const char *cert, const char *key, const char *cacert,
418 const char *prio, const char *server_fp, int verify)
420 struct tcp_s *tcp = pwmd_calloc (1, sizeof (struct tcp_s));
421 gpg_error_t rc;
423 if (!tcp)
424 return GPG_ERR_ENOMEM;
426 if (!cert || !key || !cacert)
428 pwmd_free (tcp);
429 return GPG_ERR_INV_ARG;
432 pwm->tcp = tcp;
433 tcp->port = port;
434 tcp->host = pwmd_strdup (host);
435 if (!tcp->host)
437 rc = GPG_ERR_ENOMEM;
438 goto fail;
441 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
442 if (!tcp->tls)
444 rc = GPG_ERR_ENOMEM;
445 goto fail;
448 tcp->tls->timeout = pwm->socket_timeout;
449 tcp->tls->verify = verify;
450 tcp->tls->cert = pwmd_strdup (cert);
451 if (!tcp->tls->cert)
453 rc = GPG_ERR_ENOMEM;
454 goto fail;
457 tcp->tls->key = pwmd_strdup (key);
458 if (!tcp->tls->key)
460 rc = GPG_ERR_ENOMEM;
461 goto fail;
464 tcp->tls->ca = pwmd_strdup (cacert);
465 if (!tcp->tls->ca)
467 rc = GPG_ERR_ENOMEM;
468 goto fail;
471 if (prio)
473 tcp->tls->priority = pwmd_strdup (prio);
474 if (!tcp->tls->priority)
476 rc = GPG_ERR_ENOMEM;
477 goto fail;
481 if (server_fp)
483 tcp->tls->server_fp = pwmd_strdup (server_fp);
484 if (!tcp->tls->server_fp)
486 rc = GPG_ERR_ENOMEM;
487 goto fail;
491 rc = tcp_connect_common (pwm);
492 if (!rc)
494 rc = tls_init (pwm);
495 if (!rc)
497 pwm->tcp->fd = pwm->tcp->tls->fd = &pwm->fd;
498 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
502 fail:
503 if (rc)
505 free_tcp (pwm->tcp);
506 pwm->tcp = NULL;
509 return rc;
513 * tls[46]://[hostname][:port]
515 gpg_error_t
516 _parse_tls_url (const char *str, char **host, int *port)
518 *port = 6466;
519 return parse_hostname_common (str, host, port);