Update doc/yat2m.c.
[pwmd.git] / src / tls.c
blob5f96c9d6df7fe860ee72e3a54865ea2ca5cd0896
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015
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);
81 gnutls_x509_crt_deinit (crt);
83 return len ? bin2hex (buf, len) : NULL;
86 gnutls_x509_crt_deinit (crt);
87 return NULL;
90 static int
91 verify_client_certificate (unsigned status)
93 if (!status)
94 return 0;
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_SIGNATURE_FAILURE)
103 log_write (_("client certificate signature verification failed"));
105 if (status & GNUTLS_CERT_EXPIRED)
106 log_write (_("client certificate expired"));
108 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
109 log_write (_("client certificate signer is not from CA"));
111 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
112 log_write (_("client certificate has insecure algorithm"));
114 if (status & GNUTLS_CERT_INVALID)
115 log_write (_("client certificate is invalid"));
117 return GNUTLS_E_CERTIFICATE_ERROR;
120 struct tls_s *
121 tls_init (int fd, int timeout, const char *prio)
123 struct tls_s *tls = xcalloc (1, sizeof (struct tls_s));
124 int ret;
125 unsigned status;
126 const char *prio_error;
127 gnutls_kx_algorithm_t kx;
128 gpg_error_t rc = 0;
130 if (!tls)
132 log_write ("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
133 strerror (ENOMEM));
134 return NULL;
137 ret = gnutls_init (&tls->ses, GNUTLS_SERVER);
138 if (ret != GNUTLS_E_SUCCESS)
139 goto fail;
141 ret = gnutls_priority_set_direct (tls->ses, prio, &prio_error);
142 if (ret != GNUTLS_E_SUCCESS)
143 goto fail;
145 ret = gnutls_credentials_set (tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
146 if (ret != GNUTLS_E_SUCCESS)
147 goto fail;
149 gnutls_certificate_server_set_request (tls->ses, GNUTLS_CERT_REQUIRE);
150 gnutls_transport_set_ptr (tls->ses, (gnutls_transport_ptr_t) fd);
151 time_t start = time (NULL);
154 ret = gnutls_handshake (tls->ses);
155 TEST_TIMEOUT (timeout, ret, start, rc);
156 if (rc)
157 goto fail;
159 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
161 if (ret != GNUTLS_E_SUCCESS)
162 goto fail;
164 ret = gnutls_certificate_verify_peers2 (tls->ses, &status);
165 if (ret)
166 goto fail;
168 kx = gnutls_kx_get (tls->ses);
169 tls->fp = tls_fingerprint (tls->ses);
170 log_write ("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
171 gnutls_protocol_get_name (gnutls_protocol_get_version
172 (tls->ses)),
173 gnutls_cipher_get_name (gnutls_cipher_get (tls->ses)),
174 gnutls_mac_get_name (gnutls_mac_get (tls->ses)),
175 gnutls_kx_get_name (kx), gnutls_dh_get_prime_bits (tls->ses),
176 tls->fp ? tls->fp : "N/A");
177 ret = verify_client_certificate (status);
178 if (ret)
179 goto fail;
181 return tls;
183 fail:
184 log_write ("%s", rc ? gpg_strerror(rc) : gnutls_strerror (ret));
185 gnutls_deinit (tls->ses);
186 xfree (tls->fp);
187 xfree (tls);
188 return NULL;
191 void
192 tls_log (int level, const char *msg)
194 log_write ("TLS: %i: %s", level, msg);
197 void
198 tls_audit_log (gnutls_session_t s, const char *msg)
200 (void)s;
201 log_write ("TLS: %s", msg);
204 ssize_t
205 tls_read_hook (assuan_context_t ctx, assuan_fd_t fd, void *data, size_t len)
207 struct client_s *client = assuan_get_pointer (ctx);
208 time_t start = time (NULL);
209 ssize_t ret;
211 rehandshake:
214 gpg_error_t rc = 0;
216 ret = gnutls_record_recv (client->thd->tls->ses, data, len);
217 if (ret == GNUTLS_E_REHANDSHAKE)
219 ret = gnutls_rehandshake (client->thd->tls->ses);
220 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
221 goto rehandshake;
222 else if (ret != GNUTLS_E_SUCCESS)
224 log_write ("%s", gnutls_strerror (ret));
225 ret = 0;
226 break;
231 ret = gnutls_handshake (client->thd->tls->ses);
232 rc = 0;
233 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
234 if (rc)
236 log_write ("%s", gnutls_strerror (ret));
237 ret = 0;
238 close (client->thd->fd);
239 client->thd->fd = -1;
240 break;
243 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
245 if (rc)
246 break;
248 continue;
251 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
252 if (rc)
254 errno = ETIMEDOUT;
255 close (client->thd->fd);
256 client->thd->fd = -1;
257 return -1;
260 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
262 if (ret < 0)
264 log_write ("%s", gnutls_strerror (ret));
265 ret = 0;
268 return ret;
271 ssize_t
272 tls_write_hook (assuan_context_t ctx, assuan_fd_t fd, const void *data,
273 size_t len)
275 struct client_s *client = assuan_get_pointer (ctx);
276 ssize_t ret;
277 time_t start = time (NULL);
281 gpg_error_t rc = 0;
283 ret = gnutls_record_send (client->thd->tls->ses, data, len);
284 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
285 if (rc)
287 errno = ETIMEDOUT;
288 close (client->thd->fd);
289 client->thd->fd = -1;
290 return -1;
293 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
295 return ret;
298 static int
299 parse_dh_sec_level (const char *str, gpg_error_t *rc)
301 *rc = 0;
303 if (!str)
304 return gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_MEDIUM);
306 if (!strcasecmp (str, "low"))
307 return gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LOW);
308 else if (!strcasecmp (str, "medium"))
309 return gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_MEDIUM);
310 else if (!strcasecmp (str, "high"))
311 return gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_HIGH);
312 else if (!strcasecmp (str, "ultra"))
313 return gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_ULTRA);
315 *rc = GPG_ERR_INV_VALUE;
316 return GNUTLS_SEC_PARAM_UNKNOWN;
319 gpg_error_t
320 tls_init_params (const char *dh_sec_level)
322 int n;
323 char *tmp, *tmp2;
324 gpg_error_t rc = GPG_ERR_UNKNOWN_ERRNO;
325 int bits = parse_dh_sec_level (dh_sec_level, &rc);
327 if (rc)
328 return rc;
330 rc = GPG_ERR_UNKNOWN_ERRNO;
331 n = gnutls_certificate_allocate_credentials (&x509_cred);
332 if (n != GNUTLS_E_SUCCESS)
334 log_write ("%s", gnutls_strerror (n));
335 x509_cred = NULL;
336 goto fail;
339 tmp = str_asprintf ("%s/ca-cert.pem", homedir);
340 if (!tmp)
342 rc = GPG_ERR_ENOMEM;
343 goto fail;
346 n = gnutls_certificate_set_x509_trust_file (x509_cred, tmp,
347 GNUTLS_X509_FMT_PEM);
348 if (n < 0)
350 log_write ("%s: %s", tmp, gnutls_strerror (n));
351 xfree (tmp);
352 goto fail;
355 xfree (tmp);
356 tmp = str_asprintf ("%s/server-cert.pem", homedir);
357 if (!tmp)
359 rc = GPG_ERR_ENOMEM;
360 goto fail;
363 tmp2 = str_asprintf ("%s/server-key.pem", homedir);
364 if (!tmp2)
366 xfree (tmp);
367 log_write ("%s(%i): %s", __FILE__, __LINE__, strerror (ENOMEM));
368 goto fail;
371 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
372 GNUTLS_X509_FMT_PEM);
373 xfree (tmp);
374 xfree (tmp2);
375 if (n != GNUTLS_E_SUCCESS)
377 log_write ("%s", gnutls_strerror (n));
378 goto fail;
381 log_write ("%s", _("Generating key exchange parameters..."));
382 n = gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
383 if (n)
385 log_write ("%s", pwmd_strerror (n));
386 goto fail;
389 n = gnutls_dh_params_init (&dh_params);
390 if (n != GNUTLS_E_SUCCESS)
392 log_write ("%s", gnutls_strerror (n));
393 goto fail;
396 n = gnutls_dh_params_generate2 (dh_params, bits);
397 if (n != GNUTLS_E_SUCCESS)
399 log_write ("%s", gnutls_strerror (n));
400 goto fail;
403 gnutls_certificate_set_dh_params (x509_cred, dh_params);
404 return 0;
406 fail:
407 return rc;
410 void
411 tls_deinit_params ()
413 if (dh_params)
415 gnutls_dh_params_deinit (dh_params);
416 dh_params = NULL;
419 if (x509_cred)
421 gnutls_certificate_free_credentials (x509_cred);
422 x509_cred = NULL;
426 static gpg_error_t
427 do_tls_validate_access (struct client_s *client, const char *section)
429 char **access = config_get_list (section, "allowed");
430 char **p;
431 int allowed = 0;
433 if (!access || !*access)
434 return GPG_ERR_EACCES;
436 for (p = access; p && *p; p++)
438 int not = 0;
439 char *fp = *p;
441 if (*fp && *fp == '+' && *(fp + 1) == 0) // allow all connections
443 allowed = 1;
444 continue;
447 if (*fp == '!' || *fp == '-')
449 not = 1;
450 fp++;
451 if (!*fp)
452 break;
455 if (*fp++ != '#')
456 continue;
458 if (!strcasecmp (client->thd->tls->fp, fp))
459 allowed = not ? 0 : 1;
462 strv_free (access);
463 return allowed ? 0 : GPG_ERR_EACCES;
466 gpg_error_t
467 tls_validate_access (struct client_s *client, const char *filename)
469 gpg_error_t rc = do_tls_validate_access (client, "global");
471 if (!rc && filename)
472 rc = do_tls_validate_access (client, filename);
474 return rc;