pwmc: add the '.passwd' command.
[libpwmd.git] / src / tls.c
blob1a25e63b1ec7f070885d76f6a32813445dace706
1 /*
2 Copyright (C) 2012, 2013
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 return ret <= 0 ? 0 : ret;
159 ssize_t
160 write_hook_tls (struct tls_s * tls, assuan_fd_t fd, const void *data,
161 size_t len)
163 /* This is probably the BYE command after a previous call timed
164 * out. If not done, the time(2) call seems to hang.*/
165 if (*tls->fd == -1)
166 return -1;
168 ssize_t ret;
169 time_t start = time (NULL);
173 gpg_error_t rc = 0;
175 ret = gnutls_record_send (tls->session, data, len);
176 TEST_TIMEOUT (tls->timeout, ret, start, rc);
177 if (rc)
179 close (*tls->fd);
180 *tls->fd = -1;
181 errno = ETIMEDOUT;
182 return -1;
185 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
187 return ret <= 0 ? 0 : ret;
190 static gpg_error_t
191 tls_fingerprint (gnutls_session_t ses, char **result)
193 gnutls_x509_crt_t crt;
194 const gnutls_datum_t *cert_list;
195 unsigned count;
196 unsigned char buf[20];
197 size_t len = sizeof (buf);
199 *result = NULL;
200 gnutls_x509_crt_init (&crt);
201 if (!crt)
202 return GPG_ERR_ENOMEM;
204 cert_list = gnutls_certificate_get_peers (ses, &count);
205 if (count)
207 gnutls_x509_crt_import (crt, &cert_list[0], GNUTLS_X509_FMT_DER);
208 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA1, buf, &len);
211 gnutls_x509_crt_deinit (crt);
212 *result = bin2hex (buf, len);
213 return *result ? 0 : GPG_ERR_ENOMEM;
216 static gpg_error_t
217 verify_certificate (pwm_t * pwm)
219 unsigned int status;
220 const gnutls_datum_t *cert_list;
221 unsigned int cert_list_size;
222 int i;
223 gnutls_x509_crt_t cert;
224 gpg_error_t rc = 0;
226 i = gnutls_certificate_verify_peers2 (pwm->tcp->tls->session, &status);
227 if (i < 0)
229 gnutls_perror (i);
230 return GPG_ERR_BAD_CERT;
233 if (status & GNUTLS_CERT_INVALID)
234 return GPG_ERR_BAD_CERT;
236 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
237 return GPG_ERR_BAD_SIGNATURE;
239 if (status & GNUTLS_CERT_REVOKED)
240 return GPG_ERR_CERT_REVOKED;
242 if (gnutls_certificate_type_get (pwm->tcp->tls->session) != GNUTLS_CRT_X509)
243 return GPG_ERR_UNSUPPORTED_CERT;
245 if (gnutls_x509_crt_init (&cert) < 0)
246 return gpg_error_from_errno (ENOMEM);
248 cert_list = gnutls_certificate_get_peers (pwm->tcp->tls->session,
249 &cert_list_size);
250 if (!cert_list)
252 rc = GPG_ERR_MISSING_CERT;
253 goto done;
256 for (i = 0; i < cert_list_size; i++)
258 if (gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER) <
261 rc = GPG_ERR_BAD_CERT_CHAIN;
262 goto done;
265 if (gnutls_x509_crt_get_expiration_time (cert) < time (0))
267 rc = GPG_ERR_CERT_EXPIRED;
268 goto done;
271 if (gnutls_x509_crt_get_activation_time (cert) > time (0))
273 rc = GPG_ERR_CERT_TOO_YOUNG;
274 goto done;
277 if (pwm->tcp->tls->verify)
279 if (!gnutls_x509_crt_check_hostname (cert, pwm->tcp->host))
281 rc = GPG_ERR_BAD_CERT_CHAIN;
282 goto done;
287 if (pwm->tcp->tls->server_fp)
289 char *result;
291 rc = tls_fingerprint (pwm->tcp->tls->session, &result);
292 if (!rc)
294 if (!result || !*result ||
295 strcasecmp (result, pwm->tcp->tls->server_fp))
296 rc = GPG_ERR_BAD_CERT;
298 pwmd_free (result);
302 done:
303 gnutls_x509_crt_deinit (cert);
304 return rc;
307 static gpg_error_t
308 tls_init (pwm_t * pwm)
310 gpg_error_t rc;
311 int ret;
312 const char *errstr;
314 ret = gnutls_certificate_allocate_credentials (&pwm->tcp->tls->x509);
315 if (ret)
316 return gpg_error_from_errno (ENOMEM);
318 /* The client certificate must be signed by the CA of the pwmd server
319 * certificate in order for the client to authenticate successfully. Man in
320 * the middle attacks are still possible if the attacker is running a pwmd
321 * that doesn't require client certificate authentication. So require the
322 * client to verify the server certificate.
324 ret = gnutls_certificate_set_x509_trust_file (pwm->tcp->tls->x509,
325 pwm->tcp->tls->ca,
326 GNUTLS_X509_FMT_PEM);
327 if (ret == -1)
329 gnutls_perror (ret);
330 rc = GPG_ERR_INV_CERT_OBJ;
331 goto fail;
334 ret = gnutls_certificate_set_x509_key_file (pwm->tcp->tls->x509,
335 pwm->tcp->tls->cert,
336 pwm->tcp->tls->key,
337 GNUTLS_X509_FMT_PEM);
338 if (ret != GNUTLS_E_SUCCESS)
340 gnutls_perror (ret);
341 rc = GPG_ERR_INV_CERT_OBJ;
342 goto fail;
345 ret = gnutls_init (&pwm->tcp->tls->session, GNUTLS_CLIENT);
346 if (ret != GNUTLS_E_SUCCESS)
348 gnutls_perror (ret);
349 rc = GPG_ERR_INV_CERT_OBJ;
350 goto fail;
353 ret = gnutls_priority_set_direct (pwm->tcp->tls->session,
354 pwm->tcp->tls->priority ? pwm->tcp->tls->
355 priority : "SECURE256", &errstr);
356 if (ret != GNUTLS_E_SUCCESS)
358 gnutls_perror (ret);
359 rc = GPG_ERR_INV_CERT_OBJ;
360 goto fail;
363 ret = gnutls_credentials_set (pwm->tcp->tls->session, GNUTLS_CRD_CERTIFICATE,
364 pwm->tcp->tls->x509);
365 if (ret != GNUTLS_E_SUCCESS)
367 gnutls_perror (ret);
368 rc = GPG_ERR_INV_CERT_OBJ;
369 goto fail;
372 gnutls_transport_set_ptr (pwm->tcp->tls->session,
373 (gnutls_transport_ptr_t) pwm->fd);
374 fcntl (pwm->fd, F_SETFL, O_NONBLOCK);
375 time_t start = time (NULL);
378 ret = gnutls_handshake (pwm->tcp->tls->session);
379 rc = 0;
380 TEST_TIMEOUT (pwm->tcp->tls->timeout, ret, start, rc);
381 if (rc)
382 goto fail;
384 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
386 if (ret != GNUTLS_E_SUCCESS)
388 gnutls_perror (ret);
389 rc = GPG_ERR_INV_CERT_OBJ;
390 goto fail;
393 rc = verify_certificate (pwm);
395 fail:
396 if (rc)
398 free_tcp (pwm->tcp);
399 pwm->tcp = NULL;
402 return rc;
405 gpg_error_t
406 _do_tls_connect (pwm_t * pwm, const char *host, int port,
407 const char *cert, const char *key, const char *cacert,
408 const char *prio, const char *server_fp, int verify)
410 struct tcp_s *tcp = pwmd_calloc (1, sizeof (struct tcp_s));
411 gpg_error_t rc;
413 if (!tcp)
414 return GPG_ERR_ENOMEM;
416 if (!cert || !key || !cacert)
418 pwmd_free (tcp);
419 return GPG_ERR_INV_ARG;
422 pwm->tcp = tcp;
423 tcp->port = port;
424 tcp->host = pwmd_strdup (host);
425 if (!tcp->host)
427 rc = GPG_ERR_ENOMEM;
428 goto fail;
431 tcp->tls = pwmd_calloc (1, sizeof (struct tls_s));
432 if (!tcp->tls)
434 rc = GPG_ERR_ENOMEM;
435 goto fail;
438 tcp->tls->timeout = pwm->socket_timeout;
439 tcp->tls->verify = verify;
440 tcp->tls->cert = pwmd_strdup (cert);
441 if (!tcp->tls->cert)
443 rc = GPG_ERR_ENOMEM;
444 goto fail;
447 tcp->tls->key = pwmd_strdup (key);
448 if (!tcp->tls->key)
450 rc = GPG_ERR_ENOMEM;
451 goto fail;
454 tcp->tls->ca = pwmd_strdup (cacert);
455 if (!tcp->tls->ca)
457 rc = GPG_ERR_ENOMEM;
458 goto fail;
461 if (prio)
463 tcp->tls->priority = pwmd_strdup (prio);
464 if (!tcp->tls->priority)
466 rc = GPG_ERR_ENOMEM;
467 goto fail;
471 if (server_fp)
473 tcp->tls->server_fp = pwmd_strdup (server_fp);
474 if (!tcp->tls->server_fp)
476 rc = GPG_ERR_ENOMEM;
477 goto fail;
481 rc = tcp_connect_common (pwm);
482 if (!rc)
484 rc = tls_init (pwm);
485 if (!rc)
487 pwm->tcp->fd = pwm->tcp->tls->fd = &pwm->fd;
488 rc = assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
492 fail:
493 if (rc)
495 free_tcp (pwm->tcp);
496 pwm->tcp = NULL;
499 return rc;
503 * tls[46]://[hostname][:port]
505 gpg_error_t
506 _parse_tls_url (const char *str, char **host, int *port)
508 *port = 6466;
509 return parse_hostname_common (str, host, port);