Fix setting the TLS error to the read/write length.
[libpwmd.git] / src / tls.c
blobf65be42e5059b156d0fb6779c5e912d53f08ad85
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 (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);
92 if (tls->x509)
93 gnutls_certificate_free_credentials (tls->x509);
95 tls->session = NULL;
96 tls->x509 = NULL;
97 pwmd_free (tls);
98 pwm->tls_error = ret;
101 ssize_t
102 read_hook_tls (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len)
104 struct tls_s *tls = pwm->tcp->tls;
106 if (*tls->fd == -1)
107 return -1;
109 ssize_t ret;
110 time_t start = time (NULL);
112 rehandshake:
115 gpg_error_t rc = 0;
117 ret = gnutls_record_recv (tls->session, data, len);
118 if (ret == GNUTLS_E_REHANDSHAKE)
120 ret = gnutls_rehandshake (tls->session);
121 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
122 goto rehandshake;
123 else if (ret != GNUTLS_E_SUCCESS)
125 pwm->tls_error = ret;
126 ret = 0;
127 break;
132 ret = gnutls_handshake (tls->session);
133 rc = 0;
134 TEST_TIMEOUT (tls->timeout, ret, start, rc);
135 if (rc)
137 close (*tls->fd);
138 *tls->fd = -1;
139 errno = ETIMEDOUT;
140 return -1;
143 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
145 pwm->tls_error = ret;
146 if (rc)
147 break;
149 continue;
152 TEST_TIMEOUT (tls->timeout, ret, start, rc);
153 if (rc)
155 close (*tls->fd);
156 *tls->fd = -1;
157 errno = ETIMEDOUT;
158 return -1;
161 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
163 if (ret < 0)
164 pwm->tls_error = ret;
166 if (!ret)
167 return 0;
168 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
169 errno = EPIPE;
171 return ret < 0 ? -1 : ret;
174 ssize_t
175 write_hook_tls (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len)
177 struct tls_s *tls = pwm->tcp->tls;
179 /* This is probably the BYE command after a previous call timed
180 * out. If not done, the time(2) call seems to hang.*/
181 if (*tls->fd == -1)
182 return -1;
184 ssize_t ret;
185 time_t start = time (NULL);
189 gpg_error_t rc = 0;
191 ret = gnutls_record_send (tls->session, data, len);
192 TEST_TIMEOUT (tls->timeout, ret, start, rc);
193 if (rc)
195 close (*tls->fd);
196 *tls->fd = -1;
197 errno = ETIMEDOUT;
198 return -1;
201 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
203 if (ret < 0)
204 pwm->tls_error = ret;
206 if (!ret)
207 return 0;
208 else if (ret == GNUTLS_E_PREMATURE_TERMINATION)
209 errno = EPIPE;
211 return ret < 0 ? -1 : ret;
214 static gpg_error_t
215 tls_fingerprint (pwm_t *pwm, char **result)
217 gnutls_session_t ses = pwm->tcp->tls->session;
218 gnutls_x509_crt_t crt;
219 const gnutls_datum_t *cert_list;
220 unsigned count;
221 unsigned char buf[32];
222 size_t len = sizeof (buf);
224 *result = NULL;
225 gnutls_x509_crt_init (&crt);
226 if (!crt)
227 return GPG_ERR_ENOMEM;
229 cert_list = gnutls_certificate_get_peers (ses, &count);
230 if (count)
232 pwm->tls_error = gnutls_x509_crt_import (crt, &cert_list[0],
233 GNUTLS_X509_FMT_DER);
234 if (!pwm->tls_error)
235 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
238 gnutls_x509_crt_deinit (crt);
239 if (!pwm->tls_error)
240 *result = bin2hex (buf, len);
242 return !pwm->tls_error ? 0 : GPG_ERR_ENOMEM;
245 static gpg_error_t
246 verify_certificate (pwm_t *pwm)
248 unsigned int status;
249 const gnutls_datum_t *cert_list;
250 unsigned int cert_list_size;
251 int i;
252 gnutls_x509_crt_t cert;
253 gpg_error_t rc = 0;
255 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
256 if (i < 0)
258 pwm->tls_error = i;
259 gnutls_perror (i);
260 return GPG_ERR_BAD_CERT;
263 if (status & GNUTLS_CERT_INVALID)
264 return GPG_ERR_BAD_CERT;
266 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
267 return GPG_ERR_BAD_SIGNATURE;
269 if (status & GNUTLS_CERT_REVOKED)
270 return GPG_ERR_CERT_REVOKED;
272 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
273 return GPG_ERR_UNSUPPORTED_CERT;
275 if (gnutls_x509_crt_init (&cert) < 0)
276 return gpg_error_from_errno (ENOMEM);
278 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
279 &cert_list_size);
280 if (!cert_list)
282 rc = GPG_ERR_MISSING_CERT;
283 goto done;
286 for (i = 0; i < cert_list_size; i++)
288 pwm->tls_error = gnutls_x509_crt_import (cert, &cert_list[i],
289 GNUTLS_X509_FMT_DER);
290 if (pwm->tls_error < 0)
292 rc = GPG_ERR_BAD_CERT_CHAIN;
293 goto done;
296 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
298 rc = GPG_ERR_CERT_EXPIRED;
299 goto done;
302 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
304 rc = GPG_ERR_CERT_TOO_YOUNG;
305 goto done;
308 if (pwm->tcp->tls->verify)
310 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
312 rc = GPG_ERR_BAD_CERT_CHAIN;
313 goto done;
318 if (pwm->tcp->tls->server_fp)
320 char *result;
322 rc = tls_fingerprint (pwm, &result);
323 if (!rc)
325 if (!result || !*result ||
326 strcasecmp (result, pwm->tcp->tls->server_fp))
327 rc = GPG_ERR_BAD_CERT;
329 pwmd_free (result);
333 done:
334 gnutls_x509_crt_deinit (cert);
335 return rc;
338 static gpg_error_t
339 tls_init (pwm_t * pwm)
341 gpg_error_t rc;
342 int ret;
343 const char *errstr;
345 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
346 if (ret)
348 pwm->tls_error = ret;
349 return gpg_error_from_errno (ENOMEM);
352 /* The client certificate must be signed by the CA of the pwmd server
353 * certificate in order for the client to authenticate successfully. Man in
354 * the middle attacks are still possible if the attacker is running a pwmd
355 * that doesn't require client certificate authentication. So require the
356 * client to verify the server certificate.
358 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
359 pwm->tcp->tls->ca,
360 GNUTLS_X509_FMT_PEM);
361 if (ret == -1)
363 gnutls_perror (ret);
364 rc = GPG_ERR_INV_CERT_OBJ;
365 goto fail;
368 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
369 pwm->tcp->tls->cert,
370 pwm->tcp->tls->key,
371 GNUTLS_X509_FMT_PEM);
372 if (ret != GNUTLS_E_SUCCESS)
374 pwm->tls_error = ret;
375 gnutls_perror (ret);
376 rc = GPG_ERR_INV_CERT_OBJ;
377 goto fail;
380 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
381 if (ret != GNUTLS_E_SUCCESS)
383 pwm->tls_error = ret;
384 gnutls_perror (ret);
385 rc = GPG_ERR_INV_CERT_OBJ;
386 goto fail;
389 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
390 pwm->tcp->tls->priority ? pwm->tcp->tls->
391 priority : "SECURE256", &errstr);
392 if (ret != GNUTLS_E_SUCCESS)
394 pwm->tls_error = ret;
395 gnutls_perror (ret);
396 rc = GPG_ERR_INV_CERT_OBJ;
397 goto fail;
400 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
401 pwm->tcp->tls->x509);
402 if (ret != GNUTLS_E_SUCCESS)
404 pwm->tls_error = ret;
405 gnutls_perror (ret);
406 rc = GPG_ERR_INV_CERT_OBJ;
407 goto fail;
410 gnutls_transport_set_ptr (pwm->tcp->tls->session,
411 (gnutls_transport_ptr_t) pwm->fd);
412 fcntl (pwm->fd, F_SETFL, O_NONBLOCK);
413 time_t start = time (NULL);
416 ret = gnutls_handshake (pwm->tcp->tls->session);
417 rc = 0;
418 TEST_TIMEOUT (pwm->tcp->tls->timeout, ret, start, rc);
419 if (rc)
420 goto fail;
422 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
424 if (ret != GNUTLS_E_SUCCESS)
426 pwm->tls_error = ret;
427 gnutls_perror (ret);
428 rc = GPG_ERR_INV_CERT_OBJ;
429 goto fail;
432 rc = verify_certificate (pwm);
434 fail:
435 if (rc)
436 free_tcp (pwm);
438 return rc;
441 gpg_error_t
442 _do_tls_connect (pwm_t * pwm, const char *host, int port,
443 const char *cert, const char *key, const char *cacert,
444 const char *prio, const char *server_fp, int verify)
446 struct tcp_s *tcp = pwmd_calloc (1, sizeof (struct tcp_s));
447 gpg_error_t rc;
449 if (!tcp)
450 return GPG_ERR_ENOMEM;
452 if (!cert || !key || !cacert)
454 pwmd_free (tcp);
455 return GPG_ERR_INV_ARG;
458 pwm->tcp = tcp;
459 tcp->port = port;
460 tcp->host = pwmd_strdup (host);
461 if (!tcp->host)
463 rc = GPG_ERR_ENOMEM;
464 goto fail;
467 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
468 if (!tcp->tls)
470 rc = GPG_ERR_ENOMEM;
471 goto fail;
474 tcp->tls->timeout = pwm->socket_timeout;
475 tcp->tls->verify = verify;
476 tcp->tls->cert = pwmd_strdup (cert);
477 if (!tcp->tls->cert)
479 rc = GPG_ERR_ENOMEM;
480 goto fail;
483 tcp->tls->key = pwmd_strdup (key);
484 if (!tcp->tls->key)
486 rc = GPG_ERR_ENOMEM;
487 goto fail;
490 tcp->tls->ca = pwmd_strdup (cacert);
491 if (!tcp->tls->ca)
493 rc = GPG_ERR_ENOMEM;
494 goto fail;
497 if (prio)
499 tcp->tls->priority = pwmd_strdup (prio);
500 if (!tcp->tls->priority)
502 rc = GPG_ERR_ENOMEM;
503 goto fail;
507 if (server_fp)
509 tcp->tls->server_fp = pwmd_strdup (server_fp);
510 if (!tcp->tls->server_fp)
512 rc = GPG_ERR_ENOMEM;
513 goto fail;
517 rc = tcp_connect_common (pwm);
518 if (!rc)
520 rc = tls_init (pwm);
521 if (!rc)
523 pwm->tcp->fd = pwm->tcp->tls->fd = &pwm->fd;
524 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
528 fail:
529 if (rc)
530 free_tcp (pwm);
532 return rc;
536 * tls[46]://[hostname][:port]
538 gpg_error_t
539 _parse_tls_url (const char *str, char **host, int *port)
541 *port = 6466;
542 return parse_hostname_common (str, host, port);