Fix compile time warning.
[pwmd.git] / src / tls.c
blob68679625fcb4eec95888a3160d8d752f3c92b36e
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
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 "pwmd-error.h"
27 #include "mem.h"
28 #include "util-misc.h"
29 #include "util-string.h"
30 #include "common.h"
31 #include "tls.h"
32 #include "mutex.h"
34 #define WAIT_INTERVAL 50000
35 #define TEST_TIMEOUT(timeout, ret, start, rc) \
36 do { \
37 if (ret == GNUTLS_E_AGAIN) \
38 { \
39 time_t now = time (NULL); \
40 if (timeout && now - start >= timeout) \
41 { \
42 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
43 break; \
44 } \
45 } \
46 if (ret != GNUTLS_E_INTERRUPTED) \
47 break; \
48 struct timeval tv = { 0, WAIT_INTERVAL }; \
49 select (0, NULL, NULL, NULL, &tv); \
50 } \
51 while (0)
53 static char *
54 tls_fingerprint (gnutls_session_t ses)
56 gnutls_x509_crt_t crt;
57 const gnutls_datum_t *cert_list;
58 unsigned count;
59 unsigned char buf[20];
60 size_t len = sizeof (buf);
62 gnutls_x509_crt_init (&crt);
63 if (!crt)
65 log_write ("%s(%i): %s(): %s", __FILE__, __LINE__, __FUNCTION__,
66 gnutls_strerror (GNUTLS_E_MEMORY_ERROR));
67 return NULL;
70 cert_list = gnutls_certificate_get_peers (ses, &count);
71 if (count)
73 gnutls_x509_crt_import (crt, &cert_list[0], GNUTLS_X509_FMT_DER);
74 gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_SHA1, buf, &len);
77 gnutls_x509_crt_deinit (crt);
78 return bin2hex (buf, len);
81 static int
82 verify_client_certificate (unsigned status)
84 if (!status)
85 return 0;
87 if (status & GNUTLS_CERT_INVALID)
88 log_write (_("client certificate is invalid"));
90 if (status & GNUTLS_CERT_REVOKED)
91 log_write (_("client certificate is revoked"));
93 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
94 log_write (_("client certificate has no signer"));
96 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
97 log_write (_("client certificate signer is not from CA"));
99 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
100 log_write (_("client certificate has insecure algorithm"));
102 return GNUTLS_E_CERTIFICATE_ERROR;
105 struct tls_s *
106 tls_init (int fd, int timeout, const char *prio)
108 struct tls_s *tls = xcalloc (1, sizeof (struct tls_s));
109 int ret;
110 unsigned status;
111 const char *prio_error;
112 gnutls_kx_algorithm_t kx;
114 if (!tls)
116 log_write ("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
117 strerror (ENOMEM));
118 return NULL;
121 ret = gnutls_init (&tls->ses, GNUTLS_SERVER);
122 if (ret != GNUTLS_E_SUCCESS)
123 goto fail;
125 ret = gnutls_priority_set_direct (tls->ses, prio, &prio_error);
126 if (ret != GNUTLS_E_SUCCESS)
127 goto fail;
129 ret = gnutls_credentials_set (tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
130 if (ret != GNUTLS_E_SUCCESS)
131 goto fail;
133 gnutls_certificate_server_set_request (tls->ses, GNUTLS_CERT_REQUIRE);
134 gnutls_transport_set_ptr (tls->ses, (gnutls_transport_ptr_t) fd);
135 time_t start = time (NULL);
138 gpg_error_t rc = 0;
140 ret = gnutls_handshake (tls->ses);
141 TEST_TIMEOUT (timeout, ret, start, rc);
142 if (rc)
143 goto fail;
145 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
147 if (ret != GNUTLS_E_SUCCESS)
148 goto fail;
150 ret = gnutls_certificate_verify_peers2 (tls->ses, &status);
151 if (ret)
152 goto fail;
154 kx = gnutls_kx_get (tls->ses);
155 tls->fp = tls_fingerprint (tls->ses);
156 log_write ("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
157 gnutls_protocol_get_name (gnutls_protocol_get_version
158 (tls->ses)),
159 gnutls_cipher_get_name (gnutls_cipher_get (tls->ses)),
160 gnutls_mac_get_name (gnutls_mac_get (tls->ses)),
161 gnutls_kx_get_name (kx), gnutls_dh_get_prime_bits (tls->ses),
162 tls->fp ? tls->fp : "N/A");
163 ret = verify_client_certificate (status);
164 if (ret)
165 goto fail;
167 return tls;
169 fail:
170 log_write ("%s", gnutls_strerror (ret));
171 gnutls_deinit (tls->ses);
172 xfree (tls->fp);
173 xfree (tls);
174 return NULL;
177 /* From the documentation. */
179 tls_get_params (gnutls_session_t ses, gnutls_params_type_t type,
180 gnutls_params_st * st)
182 if (type == GNUTLS_PARAMS_RSA_EXPORT)
183 st->params.rsa_export = rsa_params;
184 else if (type == GNUTLS_PARAMS_DH)
185 st->params.dh = dh_params;
186 else
187 return -1;
189 st->type = type;
190 st->deinit = 0;
191 return 0;
194 void
195 tls_log (int level, const char *msg)
197 log_write ("TLS: %i: %s", level, msg);
200 ssize_t
201 tls_read_hook (assuan_context_t ctx, assuan_fd_t fd, void *data, size_t len)
203 struct client_s *client = assuan_get_pointer (ctx);
204 time_t start = time (NULL);
205 ssize_t ret;
207 rehandshake:
210 gpg_error_t rc = 0;
212 ret = gnutls_record_recv (client->thd->tls->ses, data, len);
213 if (ret == GNUTLS_E_REHANDSHAKE)
215 ret = gnutls_rehandshake (client->thd->tls->ses);
216 if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
217 goto rehandshake;
218 else if (ret != GNUTLS_E_SUCCESS)
220 log_write ("%s", gnutls_strerror (ret));
221 ret = 0;
222 break;
227 ret = gnutls_handshake (client->thd->tls->ses);
228 rc = 0;
229 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
230 if (rc)
232 log_write ("%s", gnutls_strerror (ret));
233 ret = 0;
234 close (client->thd->fd);
235 client->thd->fd = -1;
236 break;
239 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
241 if (rc)
242 break;
244 continue;
247 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
248 if (rc)
250 errno = ETIMEDOUT;
251 close (client->thd->fd);
252 client->thd->fd = -1;
253 return -1;
256 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
258 if (ret < 0)
260 log_write ("%s", gnutls_strerror (ret));
261 ret = 0;
264 return ret;
267 ssize_t
268 tls_write_hook (assuan_context_t ctx, assuan_fd_t fd, const void *data,
269 size_t len)
271 struct client_s *client = assuan_get_pointer (ctx);
272 ssize_t ret;
273 time_t start = time (NULL);
277 gpg_error_t rc = 0;
279 ret = gnutls_record_send (client->thd->tls->ses, data, len);
280 TEST_TIMEOUT (client->thd->timeout, ret, start, rc);
281 if (rc)
283 errno = ETIMEDOUT;
284 close (client->thd->fd);
285 client->thd->fd = -1;
286 return -1;
289 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
291 return ret;
294 gpg_error_t
295 tls_init_params ()
297 int n;
298 char *tmp, *tmp2;
299 gpg_error_t rc = GPG_ERR_UNKNOWN_ERRNO;
301 n = gnutls_certificate_allocate_credentials (&x509_cred);
302 if (n != GNUTLS_E_SUCCESS)
304 log_write ("%s", gnutls_strerror (n));
305 x509_cred = NULL;
306 goto fail;
309 tmp = str_asprintf ("%s/ca-cert.pem", homedir);
310 if (!tmp)
312 rc = GPG_ERR_ENOMEM;
313 goto fail;
316 n = gnutls_certificate_set_x509_trust_file (x509_cred, tmp,
317 GNUTLS_X509_FMT_PEM);
318 if (n < 0)
320 log_write ("%s: %s", tmp, gnutls_strerror (n));
321 xfree (tmp);
322 goto fail;
325 xfree (tmp);
326 tmp = str_asprintf ("%s/server-cert.pem", homedir);
327 if (!tmp)
329 rc = GPG_ERR_ENOMEM;
330 goto fail;
333 tmp2 = str_asprintf ("%s/server-key.pem", homedir);
334 if (!tmp2)
336 xfree (tmp);
337 log_write ("%s(%i): %s", __FILE__, __LINE__, strerror (ENOMEM));
338 goto fail;
341 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
342 GNUTLS_X509_FMT_PEM);
343 xfree (tmp);
344 xfree (tmp2);
345 if (n != GNUTLS_E_SUCCESS)
347 log_write ("%s", gnutls_strerror (n));
348 goto fail;
351 log_write ("%s", _("Generating key exchange parameters..."));
352 n = gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
353 if (n)
355 log_write ("%s", pwmd_strerror (n));
356 goto fail;
359 n = gnutls_dh_params_init (&dh_params);
360 if (n != GNUTLS_E_SUCCESS)
362 log_write ("%s", gnutls_strerror (n));
363 goto fail;
366 n = gnutls_dh_params_generate2 (dh_params, 1024);
367 if (n != GNUTLS_E_SUCCESS)
369 log_write ("%s", gnutls_strerror (n));
370 goto fail;
373 gnutls_certificate_set_dh_params (x509_cred, dh_params);
374 n = gnutls_rsa_params_init (&rsa_params);
375 if (n != GNUTLS_E_SUCCESS)
377 log_write ("%s", gnutls_strerror (n));
378 goto fail;
381 n = gnutls_rsa_params_generate2 (rsa_params, 512);
382 if (n != GNUTLS_E_SUCCESS)
384 log_write ("%s", gnutls_strerror (n));
385 goto fail;
388 gnutls_certificate_set_rsa_export_params (x509_cred, rsa_params);
389 gnutls_certificate_set_params_function (x509_cred, tls_get_params);
390 return 0;
392 fail:
393 return rc;
396 void
397 tls_deinit_params ()
399 if (dh_params)
401 gnutls_dh_params_deinit (dh_params);
402 dh_params = NULL;
405 if (rsa_params)
407 gnutls_rsa_params_deinit (rsa_params);
408 rsa_params = NULL;
411 if (x509_cred)
413 gnutls_certificate_free_credentials (x509_cred);
414 x509_cred = NULL;
418 static gpg_error_t
419 do_tls_validate_access (struct client_s *client, const char *section)
421 char **access = config_get_list (section, "tls_access");
422 char **p;
423 int allow = 0, deny = 0;
425 if (!access || !*access)
426 return GPG_ERR_INV_USER_ID;
428 for (p = access; p && *p; p++)
430 int not = 0;
431 char *fp = *p;
433 if (*fp && *fp == '+' && *(fp + 1) == 0)
435 allow = 1;
436 continue;
439 if (*fp == '!')
441 not = 1;
442 fp++;
443 if (!*fp)
444 break;
447 if (!strcasecmp (client->thd->tls->fp, fp))
449 if (not)
451 deny = 1;
452 break;
455 strv_free (access);
456 return 0;
460 /* Not allowed. */
461 strv_free (access);
462 return allow && !deny ? 0 : GPG_ERR_INV_USER_ID;
465 gpg_error_t
466 tls_validate_access (struct client_s *client, const char *filename)
468 gpg_error_t rc = do_tls_validate_access (client, "global");
470 if (!rc && filename)
471 rc = do_tls_validate_access (client, filename);
473 return rc;