Fix wait interval for TLS timeout test.
[libpwmd.git] / src / tls.c
blobeb235baaef0e53e582f8d709c729223e1365aa94
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of pwmd.
7 Pwmd 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 Pwmd 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 Pwmd. If not, see <http://www.gnu.org/licenses/>.
20 #include <gnutls/x509.h>
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <errno.h>
28 #include "pwmd-error.h"
29 #include "mem.h"
30 #include "util-misc.h"
31 #include "util-string.h"
32 #include "common.h"
33 #include "tls.h"
34 #include "mutex.h"
36 #define WAIT_INTERVAL 50000
37 #define TEST_TIMEOUT(timeout, ret, start, rc) \
38 do { \
39 if (ret == GNUTLS_E_AGAIN) \
40 { \
41 time_t now = time (NULL); \
42 if (timeout && now - start >= timeout) \
43 { \
44 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
45 break; \
46 } \
47 struct timeval tv = { 0, WAIT_INTERVAL }; \
48 select (0, NULL, NULL, NULL, &tv); \
49 } \
50 if (ret != GNUTLS_E_INTERRUPTED) \
51 break; \
52 } \
53 while (0)
55 static char *
56 tls_fingerprint (gnutls_session_t ses)
58 gnutls_x509_crt_t crt;
59 const gnutls_datum_t *cert_list;
60 unsigned count;
61 unsigned char buf[32];
62 size_t len = sizeof (buf);
64 gnutls_x509_crt_init (&crt);
65 if (!crt)
67 log_write ("%s(%i): %s(): %s", __FILE__, __LINE__, __FUNCTION__,
68 gnutls_strerror (GNUTLS_E_MEMORY_ERROR));
69 return NULL;
72 cert_list = gnutls_certificate_get_peers (ses, &count);
73 if (count)
75 gnutls_x509_crt_import (crt, &cert_list[0], GNUTLS_X509_FMT_DER);
76 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
79 gnutls_x509_crt_deinit (crt);
80 return bin2hex (buf, len);
83 static int
84 verify_client_certificate (unsigned status)
86 if (!status)
87 return 0;
89 if (status & GNUTLS_CERT_INVALID)
90 log_write (_("client certificate is invalid"));
92 if (status & GNUTLS_CERT_REVOKED)
93 log_write (_("client certificate is revoked"));
95 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
96 log_write (_("client certificate has no signer"));
98 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
99 log_write (_("client certificate signer is not from CA"));
101 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
102 log_write (_("client certificate has insecure algorithm"));
104 return GNUTLS_E_CERTIFICATE_ERROR;
107 struct tls_s *
108 tls_init (int fd, int timeout, const char *prio)
110 struct tls_s *tls = xcalloc (1, sizeof (struct tls_s));
111 int ret;
112 unsigned status;
113 const char *prio_error;
114 gnutls_kx_algorithm_t kx;
115 gpg_error_t rc = 0;
117 if (!tls)
119 log_write ("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
120 strerror (ENOMEM));
121 return NULL;
124 ret = gnutls_init (&tls->ses, GNUTLS_SERVER);
125 if (ret != GNUTLS_E_SUCCESS)
126 goto fail;
128 ret = gnutls_priority_set_direct (tls->ses, prio, &prio_error);
129 if (ret != GNUTLS_E_SUCCESS)
130 goto fail;
132 ret = gnutls_credentials_set (tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
133 if (ret != GNUTLS_E_SUCCESS)
134 goto fail;
136 gnutls_certificate_server_set_request (tls->ses, GNUTLS_CERT_REQUIRE);
137 gnutls_transport_set_ptr (tls->ses, (gnutls_transport_ptr_t) fd);
138 time_t start = time (NULL);
141 ret = gnutls_handshake (tls->ses);
142 TEST_TIMEOUT (timeout, ret, start, rc);
143 if (rc)
144 goto fail;
146 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
148 if (ret != GNUTLS_E_SUCCESS)
149 goto fail;
151 ret = gnutls_certificate_verify_peers2 (tls->ses, &status);
152 if (ret)
153 goto fail;
155 kx = gnutls_kx_get (tls->ses);
156 tls->fp = tls_fingerprint (tls->ses);
157 log_write ("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
158 gnutls_protocol_get_name (gnutls_protocol_get_version
159 (tls->ses)),
160 gnutls_cipher_get_name (gnutls_cipher_get (tls->ses)),
161 gnutls_mac_get_name (gnutls_mac_get (tls->ses)),
162 gnutls_kx_get_name (kx), gnutls_dh_get_prime_bits (tls->ses),
163 tls->fp ? tls->fp : "N/A");
164 ret = verify_client_certificate (status);
165 if (ret)
166 goto fail;
168 return tls;
170 fail:
171 log_write ("%s", rc ? gpg_strerror(rc) : gnutls_strerror (ret));
172 gnutls_deinit (tls->ses);
173 xfree (tls->fp);
174 xfree (tls);
175 return NULL;
178 /* From the documentation. */
180 tls_get_params (gnutls_session_t ses, gnutls_params_type_t type,
181 gnutls_params_st * st)
183 if (type == GNUTLS_PARAMS_RSA_EXPORT)
184 st->params.rsa_export = rsa_params;
185 else if (type == GNUTLS_PARAMS_DH)
186 st->params.dh = dh_params;
187 else
188 return -1;
190 st->type = type;
191 st->deinit = 0;
192 return 0;
195 void
196 tls_log (int level, const char *msg)
198 log_write ("TLS: %i: %s", level, msg);
201 ssize_t
202 tls_read_hook (assuan_context_t ctx, assuan_fd_t fd, void *data, size_t len)
204 struct client_s *client = assuan_get_pointer (ctx);
205 time_t start = time (NULL);
206 ssize_t ret;
208 rehandshake:
211 gpg_error_t rc = 0;
213 ret = gnutls_record_recv (client->thd->tls->ses, data, len);
214 if (ret == GNUTLS_E_REHANDSHAKE)
216 ret = gnutls_rehandshake (client->thd->tls->ses);
217 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
218 goto rehandshake;
219 else if (ret != GNUTLS_E_SUCCESS)
221 log_write ("%s", gnutls_strerror (ret));
222 ret = 0;
223 break;
228 ret = gnutls_handshake (client->thd->tls->ses);
229 rc = 0;
230 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
231 if (rc)
233 log_write ("%s", gnutls_strerror (ret));
234 ret = 0;
235 close (client->thd->fd);
236 client->thd->fd = -1;
237 break;
240 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
242 if (rc)
243 break;
245 continue;
248 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
249 if (rc)
251 errno = ETIMEDOUT;
252 close (client->thd->fd);
253 client->thd->fd = -1;
254 return -1;
257 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
259 if (ret < 0)
261 log_write ("%s", gnutls_strerror (ret));
262 ret = 0;
265 return ret;
268 ssize_t
269 tls_write_hook (assuan_context_t ctx, assuan_fd_t fd, const void *data,
270 size_t len)
272 struct client_s *client = assuan_get_pointer (ctx);
273 ssize_t ret;
274 time_t start = time (NULL);
278 gpg_error_t rc = 0;
280 ret = gnutls_record_send (client->thd->tls->ses, data, len);
281 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
282 if (rc)
284 errno = ETIMEDOUT;
285 close (client->thd->fd);
286 client->thd->fd = -1;
287 return -1;
290 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
292 return ret;
295 gpg_error_t
296 tls_init_params ()
298 int n;
299 char *tmp, *tmp2;
300 gpg_error_t rc = GPG_ERR_UNKNOWN_ERRNO;
302 n = gnutls_certificate_allocate_credentials (&x509_cred);
303 if (n != GNUTLS_E_SUCCESS)
305 log_write ("%s", gnutls_strerror (n));
306 x509_cred = NULL;
307 goto fail;
310 tmp = str_asprintf ("%s/ca-cert.pem", homedir);
311 if (!tmp)
313 rc = GPG_ERR_ENOMEM;
314 goto fail;
317 n = gnutls_certificate_set_x509_trust_file (x509_cred, tmp,
318 GNUTLS_X509_FMT_PEM);
319 if (n < 0)
321 log_write ("%s: %s", tmp, gnutls_strerror (n));
322 xfree (tmp);
323 goto fail;
326 xfree (tmp);
327 tmp = str_asprintf ("%s/server-cert.pem", homedir);
328 if (!tmp)
330 rc = GPG_ERR_ENOMEM;
331 goto fail;
334 tmp2 = str_asprintf ("%s/server-key.pem", homedir);
335 if (!tmp2)
337 xfree (tmp);
338 log_write ("%s(%i): %s", __FILE__, __LINE__, strerror (ENOMEM));
339 goto fail;
342 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
343 GNUTLS_X509_FMT_PEM);
344 xfree (tmp);
345 xfree (tmp2);
346 if (n != GNUTLS_E_SUCCESS)
348 log_write ("%s", gnutls_strerror (n));
349 goto fail;
352 log_write ("%s", _("Generating key exchange parameters..."));
353 n = gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
354 if (n)
356 log_write ("%s", pwmd_strerror (n));
357 goto fail;
360 n = gnutls_dh_params_init (&dh_params);
361 if (n != GNUTLS_E_SUCCESS)
363 log_write ("%s", gnutls_strerror (n));
364 goto fail;
367 n = gnutls_dh_params_generate2 (dh_params, 1024);
368 if (n != GNUTLS_E_SUCCESS)
370 log_write ("%s", gnutls_strerror (n));
371 goto fail;
374 gnutls_certificate_set_dh_params (x509_cred, dh_params);
375 n = gnutls_rsa_params_init (&rsa_params);
376 if (n != GNUTLS_E_SUCCESS)
378 log_write ("%s", gnutls_strerror (n));
379 goto fail;
382 n = gnutls_rsa_params_generate2 (rsa_params, 512);
383 if (n != GNUTLS_E_SUCCESS)
385 log_write ("%s", gnutls_strerror (n));
386 goto fail;
389 gnutls_certificate_set_rsa_export_params (x509_cred, rsa_params);
390 gnutls_certificate_set_params_function (x509_cred, tls_get_params);
391 return 0;
393 fail:
394 return rc;
397 void
398 tls_deinit_params ()
400 if (dh_params)
402 gnutls_dh_params_deinit (dh_params);
403 dh_params = NULL;
406 if (rsa_params)
408 gnutls_rsa_params_deinit (rsa_params);
409 rsa_params = NULL;
412 if (x509_cred)
414 gnutls_certificate_free_credentials (x509_cred);
415 x509_cred = NULL;
419 static gpg_error_t
420 do_tls_validate_access (struct client_s *client, const char *section)
422 char **access = config_get_list (section, "allowed");
423 char **p;
424 int allowed = 0;
426 if (!access || !*access)
427 return GPG_ERR_EACCES;
429 for (p = access; p && *p; p++)
431 int not = 0;
432 char *fp = *p;
434 if (*fp && *fp == '+' && *(fp + 1) == 0) // allow all connections
436 allowed = 1;
437 continue;
440 if (*fp == '!' || *fp == '-')
442 not = 1;
443 fp++;
444 if (!*fp)
445 break;
448 if (*fp++ != '#')
449 continue;
451 if (!strcasecmp (client->thd->tls->fp, fp))
452 allowed = not ? 0 : 1;
455 strv_free (access);
456 return allowed ? 0 : GPG_ERR_EACCES;
459 gpg_error_t
460 tls_validate_access (struct client_s *client, const char *filename)
462 gpg_error_t rc = do_tls_validate_access (client, "global");
464 if (!rc && filename)
465 rc = do_tls_validate_access (client, filename);
467 return rc;