Make cache_timer_thread() joinable.
[pwmd.git] / src / tls.c
blob24e7baff05d2d3858030b3902c3c496377de8e54
1 /*
2 Copyright (C) 2008 Ben Kibbey <bjk@luxsci.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
18 #include <gnutls/x509.h>
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include "pwmd-error.h"
25 #include "mem.h"
26 #include "util-misc.h"
27 #include "util-string.h"
28 #include "common.h"
29 #include "tls.h"
30 #include "mutex.h"
32 static char *tls_fingerprint(gnutls_session_t ses)
34 gnutls_x509_crt_t crt;
35 const gnutls_datum_t *cert_list;
36 unsigned count;
37 unsigned char buf[20];
38 size_t len = sizeof(buf);
40 gnutls_x509_crt_init(&crt);
41 if (!crt) {
42 log_write("%s(%i): %s(): %s", __FILE__, __LINE__, __FUNCTION__,
43 gnutls_strerror(GNUTLS_E_MEMORY_ERROR));
44 return NULL;
47 cert_list = gnutls_certificate_get_peers(ses, &count);
48 if (count) {
49 gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
50 gnutls_x509_crt_get_fingerprint(crt, GNUTLS_DIG_SHA1, buf, &len);
53 gnutls_x509_crt_deinit(crt);
54 return bin2hex(buf, len);
57 static int verify_client_certificate(unsigned status)
59 if (!status)
60 return 0;
62 if (status & GNUTLS_CERT_INVALID)
63 log_write(_("client certificate is invalid"));
65 if (status & GNUTLS_CERT_REVOKED)
66 log_write(_("client certificate is revoked"));
68 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
69 log_write(_("client certificate has no signer"));
71 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
72 log_write(_("client certificate signer is not from CA"));
74 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
75 log_write(_("client certificate has insecure algorithm"));
77 return GNUTLS_E_CERTIFICATE_ERROR;
80 struct tls_s *tls_init(int fd, const char *prio)
82 struct tls_s *tls = xcalloc(1, sizeof(struct tls_s));
83 int ret;
84 unsigned status;
85 const char *prio_error;
86 gnutls_kx_algorithm_t kx;
88 if (!tls) {
89 log_write("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
90 strerror(ENOMEM));
91 return NULL;
94 ret = gnutls_init(&tls->ses, GNUTLS_SERVER);
95 if (ret != GNUTLS_E_SUCCESS)
96 goto fail;
98 ret = gnutls_priority_set_direct(tls->ses, prio, &prio_error);
99 if (ret != GNUTLS_E_SUCCESS)
100 goto fail;
102 ret = gnutls_credentials_set(tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
103 if (ret != GNUTLS_E_SUCCESS)
104 goto fail;
106 gnutls_certificate_server_set_request(tls->ses, GNUTLS_CERT_REQUIRE);
107 gnutls_transport_set_ptr(tls->ses, (gnutls_transport_ptr_t)fd);
108 ret = gnutls_handshake(tls->ses);
109 if (ret != GNUTLS_E_SUCCESS)
110 goto fail;
112 ret = gnutls_certificate_verify_peers2(tls->ses, &status);
113 if (ret)
114 goto fail;
116 kx = gnutls_kx_get(tls->ses);
117 tls->fp = tls_fingerprint(tls->ses);
118 log_write("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
119 gnutls_protocol_get_name(gnutls_protocol_get_version(tls->ses)),
120 gnutls_cipher_get_name(gnutls_cipher_get(tls->ses)),
121 gnutls_mac_get_name(gnutls_mac_get(tls->ses)),
122 gnutls_kx_get_name(kx),
123 gnutls_dh_get_prime_bits(tls->ses),
124 tls->fp ? tls->fp : "N/A");
125 ret = verify_client_certificate(status);
126 if (ret)
127 goto fail;
129 return tls;
131 fail:
132 log_write("%s", gnutls_strerror(ret));
133 gnutls_deinit(tls->ses);
134 xfree(tls->fp);
135 xfree(tls);
136 return NULL;
139 /* From the documentation. */
140 int tls_get_params(gnutls_session_t ses, gnutls_params_type_t type,
141 gnutls_params_st *st)
143 if (type == GNUTLS_PARAMS_RSA_EXPORT)
144 st->params.rsa_export = rsa_params;
145 else if (type == GNUTLS_PARAMS_DH)
146 st->params.dh = dh_params;
147 else
148 return -1;
150 st->type = type;
151 st->deinit = 0;
152 return 0;
155 void tls_log(int level, const char *msg)
157 log_write("TLS: %i: %s", level, msg);
160 ssize_t tls_read_hook(assuan_context_t ctx, assuan_fd_t fd, void *data,
161 size_t len)
163 struct client_s *client = assuan_get_pointer(ctx);
164 ssize_t ret;
166 do {
167 ret = gnutls_record_recv(client->thd->tls->ses, data, len);
169 if (ret == GNUTLS_E_REHANDSHAKE) {
170 ret = gnutls_rehandshake(client->thd->tls->ses);
171 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED ||
172 ret == GNUTLS_A_NO_RENEGOTIATION) {
173 log_write("%s", gnutls_strerror(ret));
174 continue;
177 if (ret != GNUTLS_E_SUCCESS) {
178 log_write("%s", gnutls_strerror(ret));
179 ret = 0;
180 break;
183 ret = gnutls_handshake(client->thd->tls->ses);
184 if (ret != GNUTLS_E_SUCCESS) {
185 log_write("%s", gnutls_strerror(ret));
186 ret = 0;
187 break;
190 continue;
192 } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
194 if (ret < 0) {
195 log_write("%s", gnutls_strerror(ret));
196 ret = 0;
199 return ret;
202 ssize_t tls_write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data,
203 size_t len)
205 struct client_s *client = assuan_get_pointer(ctx);
206 ssize_t ret;
208 do {
209 struct timeval tv = { 0, 50000 };
211 ret = gnutls_record_send(client->thd->tls->ses, data, len);
212 if (ret == GNUTLS_E_AGAIN)
213 select(0, NULL, NULL, NULL, &tv);
214 } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
216 return ret;
219 gpg_error_t tls_init_params()
221 int n;
222 char *tmp, *tmp2;
223 gpg_error_t rc = GPG_ERR_UNKNOWN_ERRNO;
225 n = gnutls_certificate_allocate_credentials(&x509_cred);
226 if (n != GNUTLS_E_SUCCESS) {
227 log_write("%s", gnutls_strerror(n));
228 x509_cred = NULL;
229 goto fail;
232 MUTEX_LOCK(&rcfile_mutex);
234 if (config_get_boolean("global", "tls_use_crl")) {
235 MUTEX_UNLOCK(&rcfile_mutex);
236 tmp = str_asprintf("%s/crl.pem", homedir);
237 if (!tmp) {
238 rc = GPG_ERR_ENOMEM;
239 goto fail;
242 if (access(tmp, R_OK) == -1 && errno == ENOENT)
243 log_write("%s: %s", tmp, gnutls_strerror(n));
244 else {
245 n = gnutls_certificate_set_x509_crl_file(x509_cred, tmp,
246 GNUTLS_X509_FMT_PEM);
247 if (n < 0) {
248 log_write("%s: %s", tmp, gnutls_strerror(n));
249 xfree(tmp);
250 goto fail;
254 xfree(tmp);
256 else
257 MUTEX_UNLOCK(&rcfile_mutex);
259 tmp = str_asprintf("%s/ca-cert.pem", homedir);
260 if (!tmp) {
261 rc = GPG_ERR_ENOMEM;
262 goto fail;
265 n = gnutls_certificate_set_x509_trust_file(x509_cred, tmp,
266 GNUTLS_X509_FMT_PEM);
267 if (n < 0) {
268 log_write("%s: %s", tmp, gnutls_strerror(n));
269 xfree(tmp);
270 goto fail;
273 xfree(tmp);
274 tmp = str_asprintf("%s/server-cert.pem", homedir);
275 if (!tmp) {
276 rc = GPG_ERR_ENOMEM;
277 goto fail;
280 tmp2 = str_asprintf("%s/server-key.pem", homedir);
281 if (!tmp2) {
282 xfree(tmp);
283 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
284 goto fail;
287 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
288 GNUTLS_X509_FMT_PEM);
289 xfree(tmp);
290 xfree(tmp2);
291 if (n != GNUTLS_E_SUCCESS) {
292 log_write("%s", gnutls_strerror(n));
293 goto fail;
296 log_write("%s", _("Generating key exchange parameters..."));
297 n = gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
298 if (n) {
299 log_write("%s", pwmd_strerror(n));
300 goto fail;
303 n = gnutls_dh_params_init(&dh_params);
304 if (n != GNUTLS_E_SUCCESS) {
305 log_write("%s", gnutls_strerror(n));
306 goto fail;
309 n = gnutls_dh_params_generate2(dh_params, 1024);
310 if (n != GNUTLS_E_SUCCESS) {
311 log_write("%s", gnutls_strerror(n));
312 goto fail;
315 gnutls_certificate_set_dh_params(x509_cred, dh_params);
316 n = gnutls_rsa_params_init(&rsa_params);
317 if (n != GNUTLS_E_SUCCESS) {
318 log_write("%s", gnutls_strerror(n));
319 goto fail;
322 n = gnutls_rsa_params_generate2(rsa_params, 512);
323 if (n != GNUTLS_E_SUCCESS) {
324 log_write("%s", gnutls_strerror(n));
325 goto fail;
328 gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
329 gnutls_certificate_set_params_function(x509_cred, tls_get_params);
330 return 0;
332 fail:
333 return rc;
336 void tls_deinit_params()
338 if (dh_params) {
339 gnutls_dh_params_deinit(dh_params);
340 dh_params = NULL;
343 if (rsa_params) {
344 gnutls_rsa_params_deinit(rsa_params);
345 rsa_params = NULL;
348 if (x509_cred) {
349 gnutls_certificate_free_credentials(x509_cred);
350 x509_cred = NULL;
354 gpg_error_t tls_validate_access(struct client_s *cl, const char
355 *filename)
357 char **access = config_get_list(filename, "tls_access");
358 char **p;
359 int allow = 0, deny = 0;
361 if (!access || !*access)
362 return GPG_ERR_INV_USER_ID;
364 for (p = access; p && *p; p++) {
365 int not = 0;
366 char *fp = *p;
368 if (*fp && *fp == '+' && *(fp+1) == 0) {
369 allow = 1;
370 continue;
373 if (*fp == '!') {
374 not = 1;
375 fp++;
376 if (!*fp)
377 break;
380 if (!strcasecmp(cl->thd->tls->fp, fp)) {
381 if (not) {
382 deny = 1;
383 break;
386 strv_free(access);
387 return 0;
391 /* Not allowed. */
392 strv_free(access);
393 return allow && !deny ? 0 : GPG_ERR_INV_USER_ID;