Version 3.0.12.
[pwmd.git] / src / tls.c
blob468bce4f781a9cf01dc4e310229f561891a52f78
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 #ifdef HAVE_STRINGS_H
29 #include <strings.h>
30 #endif
32 #include "pwmd-error.h"
33 #include "mem.h"
34 #include "util-misc.h"
35 #include "util-string.h"
36 #include "common.h"
37 #include "tls.h"
38 #include "mutex.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 static char *
60 tls_fingerprint (gnutls_session_t ses)
62 gnutls_x509_crt_t crt;
63 const gnutls_datum_t *cert_list;
64 unsigned count;
65 unsigned char buf[32];
66 size_t len = sizeof (buf);
68 gnutls_x509_crt_init (&crt);
69 if (!crt)
71 log_write ("%s(%i): %s(): %s", __FILE__, __LINE__, __FUNCTION__,
72 gnutls_strerror (GNUTLS_E_MEMORY_ERROR));
73 return NULL;
76 cert_list = gnutls_certificate_get_peers (ses, &count);
77 if (count)
79 gnutls_x509_crt_import (crt, &cert_list[0], GNUTLS_X509_FMT_DER);
80 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA256, buf, &len);
83 gnutls_x509_crt_deinit (crt);
84 return bin2hex (buf, len);
87 static int
88 verify_client_certificate (unsigned status)
90 if (!status)
91 return 0;
93 if (status & GNUTLS_CERT_INVALID)
94 log_write (_("client certificate is invalid"));
96 if (status & GNUTLS_CERT_REVOKED)
97 log_write (_("client certificate is revoked"));
99 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
100 log_write (_("client certificate has no signer"));
102 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
103 log_write (_("client certificate signer is not from CA"));
105 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
106 log_write (_("client certificate has insecure algorithm"));
108 return GNUTLS_E_CERTIFICATE_ERROR;
111 struct tls_s *
112 tls_init (int fd, int timeout, const char *prio)
114 struct tls_s *tls = xcalloc (1, sizeof (struct tls_s));
115 int ret;
116 unsigned status;
117 const char *prio_error;
118 gnutls_kx_algorithm_t kx;
119 gpg_error_t rc = 0;
121 if (!tls)
123 log_write ("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
124 strerror (ENOMEM));
125 return NULL;
128 ret = gnutls_init (&tls->ses, GNUTLS_SERVER);
129 if (ret != GNUTLS_E_SUCCESS)
130 goto fail;
132 ret = gnutls_priority_set_direct (tls->ses, prio, &prio_error);
133 if (ret != GNUTLS_E_SUCCESS)
134 goto fail;
136 ret = gnutls_credentials_set (tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
137 if (ret != GNUTLS_E_SUCCESS)
138 goto fail;
140 gnutls_certificate_server_set_request (tls->ses, GNUTLS_CERT_REQUIRE);
141 gnutls_transport_set_ptr (tls->ses, (gnutls_transport_ptr_t) fd);
142 time_t start = time (NULL);
145 ret = gnutls_handshake (tls->ses);
146 TEST_TIMEOUT (timeout, ret, start, rc);
147 if (rc)
148 goto fail;
150 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
152 if (ret != GNUTLS_E_SUCCESS)
153 goto fail;
155 ret = gnutls_certificate_verify_peers2 (tls->ses, &status);
156 if (ret)
157 goto fail;
159 kx = gnutls_kx_get (tls->ses);
160 tls->fp = tls_fingerprint (tls->ses);
161 log_write ("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
162 gnutls_protocol_get_name (gnutls_protocol_get_version
163 (tls->ses)),
164 gnutls_cipher_get_name (gnutls_cipher_get (tls->ses)),
165 gnutls_mac_get_name (gnutls_mac_get (tls->ses)),
166 gnutls_kx_get_name (kx), gnutls_dh_get_prime_bits (tls->ses),
167 tls->fp ? tls->fp : "N/A");
168 ret = verify_client_certificate (status);
169 if (ret)
170 goto fail;
172 return tls;
174 fail:
175 log_write ("%s", rc ? gpg_strerror(rc) : gnutls_strerror (ret));
176 gnutls_deinit (tls->ses);
177 xfree (tls->fp);
178 xfree (tls);
179 return NULL;
182 /* From the documentation. */
184 tls_get_params (gnutls_session_t ses, gnutls_params_type_t type,
185 gnutls_params_st * st)
187 if (type == GNUTLS_PARAMS_RSA_EXPORT)
188 st->params.rsa_export = rsa_params;
189 else if (type == GNUTLS_PARAMS_DH)
190 st->params.dh = dh_params;
191 else
192 return -1;
194 st->type = type;
195 st->deinit = 0;
196 return 0;
199 void
200 tls_log (int level, const char *msg)
202 log_write ("TLS: %i: %s", level, msg);
205 ssize_t
206 tls_read_hook (assuan_context_t ctx, assuan_fd_t fd, void *data, size_t len)
208 struct client_s *client = assuan_get_pointer (ctx);
209 time_t start = time (NULL);
210 ssize_t ret;
212 rehandshake:
215 gpg_error_t rc = 0;
217 ret = gnutls_record_recv (client->thd->tls->ses, data, len);
218 if (ret == GNUTLS_E_REHANDSHAKE)
220 ret = gnutls_rehandshake (client->thd->tls->ses);
221 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
222 goto rehandshake;
223 else if (ret != GNUTLS_E_SUCCESS)
225 log_write ("%s", gnutls_strerror (ret));
226 ret = 0;
227 break;
232 ret = gnutls_handshake (client->thd->tls->ses);
233 rc = 0;
234 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
235 if (rc)
237 log_write ("%s", gnutls_strerror (ret));
238 ret = 0;
239 close (client->thd->fd);
240 client->thd->fd = -1;
241 break;
244 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
246 if (rc)
247 break;
249 continue;
252 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
253 if (rc)
255 errno = ETIMEDOUT;
256 close (client->thd->fd);
257 client->thd->fd = -1;
258 return -1;
261 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
263 if (ret < 0)
265 log_write ("%s", gnutls_strerror (ret));
266 ret = 0;
269 return ret;
272 ssize_t
273 tls_write_hook (assuan_context_t ctx, assuan_fd_t fd, const void *data,
274 size_t len)
276 struct client_s *client = assuan_get_pointer (ctx);
277 ssize_t ret;
278 time_t start = time (NULL);
282 gpg_error_t rc = 0;
284 ret = gnutls_record_send (client->thd->tls->ses, data, len);
285 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
286 if (rc)
288 errno = ETIMEDOUT;
289 close (client->thd->fd);
290 client->thd->fd = -1;
291 return -1;
294 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
296 return ret;
299 gpg_error_t
300 tls_init_params ()
302 int n;
303 char *tmp, *tmp2;
304 gpg_error_t rc = GPG_ERR_UNKNOWN_ERRNO;
306 n = gnutls_certificate_allocate_credentials (&x509_cred);
307 if (n != GNUTLS_E_SUCCESS)
309 log_write ("%s", gnutls_strerror (n));
310 x509_cred = NULL;
311 goto fail;
314 tmp = str_asprintf ("%s/ca-cert.pem", homedir);
315 if (!tmp)
317 rc = GPG_ERR_ENOMEM;
318 goto fail;
321 n = gnutls_certificate_set_x509_trust_file (x509_cred, tmp,
322 GNUTLS_X509_FMT_PEM);
323 if (n < 0)
325 log_write ("%s: %s", tmp, gnutls_strerror (n));
326 xfree (tmp);
327 goto fail;
330 xfree (tmp);
331 tmp = str_asprintf ("%s/server-cert.pem", homedir);
332 if (!tmp)
334 rc = GPG_ERR_ENOMEM;
335 goto fail;
338 tmp2 = str_asprintf ("%s/server-key.pem", homedir);
339 if (!tmp2)
341 xfree (tmp);
342 log_write ("%s(%i): %s", __FILE__, __LINE__, strerror (ENOMEM));
343 goto fail;
346 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
347 GNUTLS_X509_FMT_PEM);
348 xfree (tmp);
349 xfree (tmp2);
350 if (n != GNUTLS_E_SUCCESS)
352 log_write ("%s", gnutls_strerror (n));
353 goto fail;
356 log_write ("%s", _("Generating key exchange parameters..."));
357 n = gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
358 if (n)
360 log_write ("%s", pwmd_strerror (n));
361 goto fail;
364 n = gnutls_dh_params_init (&dh_params);
365 if (n != GNUTLS_E_SUCCESS)
367 log_write ("%s", gnutls_strerror (n));
368 goto fail;
371 n = gnutls_dh_params_generate2 (dh_params, 1024);
372 if (n != GNUTLS_E_SUCCESS)
374 log_write ("%s", gnutls_strerror (n));
375 goto fail;
378 gnutls_certificate_set_dh_params (x509_cred, dh_params);
379 n = gnutls_rsa_params_init (&rsa_params);
380 if (n != GNUTLS_E_SUCCESS)
382 log_write ("%s", gnutls_strerror (n));
383 goto fail;
386 n = gnutls_rsa_params_generate2 (rsa_params, 512);
387 if (n != GNUTLS_E_SUCCESS)
389 log_write ("%s", gnutls_strerror (n));
390 goto fail;
393 gnutls_certificate_set_rsa_export_params (x509_cred, rsa_params);
394 gnutls_certificate_set_params_function (x509_cred, tls_get_params);
395 return 0;
397 fail:
398 return rc;
401 void
402 tls_deinit_params ()
404 if (dh_params)
406 gnutls_dh_params_deinit (dh_params);
407 dh_params = NULL;
410 if (rsa_params)
412 gnutls_rsa_params_deinit (rsa_params);
413 rsa_params = NULL;
416 if (x509_cred)
418 gnutls_certificate_free_credentials (x509_cred);
419 x509_cred = NULL;
423 static gpg_error_t
424 do_tls_validate_access (struct client_s *client, const char *section)
426 char **access = config_get_list (section, "allowed");
427 char **p;
428 int allowed = 0;
430 if (!access || !*access)
431 return GPG_ERR_EACCES;
433 for (p = access; p && *p; p++)
435 int not = 0;
436 char *fp = *p;
438 if (*fp && *fp == '+' && *(fp + 1) == 0) // allow all connections
440 allowed = 1;
441 continue;
444 if (*fp == '!' || *fp == '-')
446 not = 1;
447 fp++;
448 if (!*fp)
449 break;
452 if (*fp++ != '#')
453 continue;
455 if (!strcasecmp (client->thd->tls->fp, fp))
456 allowed = not ? 0 : 1;
459 strv_free (access);
460 return allowed ? 0 : GPG_ERR_EACCES;
463 gpg_error_t
464 tls_validate_access (struct client_s *client, const char *filename)
466 gpg_error_t rc = do_tls_validate_access (client, "global");
468 if (!rc && filename)
469 rc = do_tls_validate_access (client, filename);
471 return rc;