Lock the rcfile_mutex at each keyfileh access.
[pwmd.git] / src / tls.c
blob76a8e044a1fd100ab3cffea1f9bf3f71e0f1ea68
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2008 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #include <glib.h>
20 #include <glib/gprintf.h>
21 #include <gnutls/x509.h>
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include "mem.h"
28 #include "misc.h"
29 #include "common.h"
30 #include "lock.h"
31 #include "tls.h"
33 static gchar *tls_fingerprint(gnutls_session_t ses)
35 gnutls_x509_crt_t crt;
36 const gnutls_datum_t *cert_list;
37 guint count;
38 guchar buf[20];
39 gsize len;
41 gnutls_x509_crt_init(&crt);
43 if (!crt) {
44 log_write("%s(%i): %s(): %s", __FILE__, __LINE__, __FUNCTION__,
45 gnutls_strerror(GNUTLS_E_MEMORY_ERROR));
46 return NULL;
49 cert_list = gnutls_certificate_get_peers(ses, &count);
51 if (count) {
52 gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
53 len = sizeof(buf);
54 gnutls_x509_crt_get_fingerprint(crt, GNUTLS_MAC_SHA1, buf, &len);
57 gnutls_x509_crt_deinit(crt);
58 return tohex(buf, len);
61 static gint verify_client_certificate(guint status)
63 if (!status)
64 return 0;
66 if (status & GNUTLS_CERT_INVALID)
67 log_write(N_("client certificate is invalid"));
69 if (status & GNUTLS_CERT_REVOKED)
70 log_write(N_("client certificate is revoked"));
72 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
73 log_write(N_("client certificate has no signer"));
75 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
76 log_write(N_("client certificate signer is not from CA"));
78 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
79 log_write(N_("client certificate has insecure algorithm"));
81 return GNUTLS_E_CERTIFICATE_ERROR;
84 struct tls_s *tls_init(gint fd, const gchar *prio)
86 struct tls_s *tls = g_malloc0(sizeof(struct tls_s));
87 gint ret;
88 guint status;
89 const gchar *prio_error;
90 gnutls_kx_algorithm_t kx;
92 if (!tls) {
93 log_write("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
94 strerror(ENOMEM));
95 return NULL;
98 ret = gnutls_init(&tls->ses, GNUTLS_SERVER);
100 if (ret != GNUTLS_E_SUCCESS)
101 goto fail;
103 ret = gnutls_priority_set_direct(tls->ses, prio, &prio_error);
105 if (ret != GNUTLS_E_SUCCESS)
106 goto fail;
108 ret = gnutls_credentials_set(tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
110 if (ret != GNUTLS_E_SUCCESS)
111 goto fail;
113 gnutls_certificate_server_set_request(tls->ses, GNUTLS_CERT_REQUIRE);
114 gnutls_transport_set_ptr(tls->ses, (gnutls_transport_ptr_t)fd);
115 ret = gnutls_handshake(tls->ses);
117 if (ret != GNUTLS_E_SUCCESS)
118 goto fail;
120 ret = gnutls_certificate_verify_peers2(tls->ses, &status);
122 if (ret)
123 goto fail;
125 kx = gnutls_kx_get(tls->ses);
126 tls->fp = tls_fingerprint(tls->ses);
127 log_write("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
128 gnutls_protocol_get_name(gnutls_protocol_get_version(tls->ses)),
129 gnutls_cipher_get_name(gnutls_cipher_get(tls->ses)),
130 gnutls_mac_get_name(gnutls_mac_get(tls->ses)),
131 gnutls_kx_get_name(kx),
132 gnutls_dh_get_prime_bits(tls->ses),
133 tls->fp ? tls->fp : "N/A");
134 ret = verify_client_certificate(status);
136 if (ret)
137 goto fail;
139 return tls;
141 fail:
142 log_write("%s", gnutls_strerror(ret));
143 gnutls_deinit(tls->ses);
145 if (tls->fp)
146 g_free(tls->fp);
148 g_free(tls);
149 return NULL;
152 /* From the documentation. */
153 gint tls_get_params(gnutls_session_t ses, gnutls_params_type_t type,
154 gnutls_params_st *st)
156 if (type == GNUTLS_PARAMS_RSA_EXPORT)
157 st->params.rsa_export = rsa_params;
158 else if (type == GNUTLS_PARAMS_DH)
159 st->params.dh = dh_params;
160 else
161 return -1;
163 st->type = type;
164 st->deinit = 0;
165 return 0;
168 void tls_log(gint level, const char *msg)
170 log_write("TLS: %i: %s", level, msg);
173 int read_hook(assuan_context_t ctx, assuan_fd_t fd, void *data,
174 size_t len, ssize_t *ret)
176 struct client_s *cl = assuan_get_pointer(ctx);
178 if (!cl || !cl->thd->remote)
179 *ret = read((int)fd, data, len);
180 else {
181 do {
182 *ret = gnutls_record_recv(cl->thd->tls->ses, data, len);
184 if (*ret == GNUTLS_E_REHANDSHAKE) {
185 *ret = gnutls_rehandshake(cl->thd->tls->ses);
187 if (*ret == GNUTLS_E_WARNING_ALERT_RECEIVED ||
188 *ret == GNUTLS_A_NO_RENEGOTIATION) {
189 log_write("%s", gnutls_strerror(*ret));
190 continue;
193 if (*ret != GNUTLS_E_SUCCESS) {
194 log_write("%s", gnutls_strerror(*ret));
195 *ret = 0;
196 break;
199 *ret = gnutls_handshake(cl->thd->tls->ses);
201 if (*ret != GNUTLS_E_SUCCESS) {
202 log_write("%s", gnutls_strerror(*ret));
203 *ret = 0;
204 break;
207 continue;
209 } while (*ret == GNUTLS_E_INTERRUPTED || *ret == GNUTLS_E_AGAIN);
212 return *ret <= 0 ? 0 : 1;
215 int write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data,
216 size_t len, ssize_t *ret)
218 struct client_s *cl = assuan_get_pointer(ctx);
220 if (!cl || !cl->thd->remote)
221 *ret = write((int)fd, data, len);
222 else {
223 do {
224 *ret = gnutls_record_send(cl->thd->tls->ses, data, len);
225 } while (*ret == GNUTLS_E_INTERRUPTED || *ret == GNUTLS_E_AGAIN);
228 return *ret <= 0 ? 0 : 1;
231 gboolean initTlsParams()
233 gint n;
234 gchar *tmp, *tmp2;
236 n = gnutls_certificate_allocate_credentials(&x509_cred);
238 if (n != GNUTLS_E_SUCCESS) {
239 log_write("%s", gnutls_strerror(n));
240 x509_cred = NULL;
241 goto fail;
244 MUTEX_LOCK(&rcfile_mutex);
246 if (g_key_file_has_key(keyfileh, "global", "tcp_use_crl", NULL) &&
247 get_key_file_boolean("global", "tcp_use_crl") == TRUE) {
248 MUTEX_UNLOCK(&rcfile_mutex);
249 tmp = expand_homedir("~/.pwmd/crl.pem");
251 if (!tmp) {
252 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
253 goto fail;
256 n = gnutls_certificate_set_x509_crl_file(x509_cred, tmp,
257 GNUTLS_X509_FMT_PEM);
259 if (n < 0) {
260 log_write("%s: %s", tmp, gnutls_strerror(n));
261 g_free(tmp);
262 goto fail;
265 g_free(tmp);
267 else
268 MUTEX_UNLOCK(&rcfile_mutex);
270 tmp = expand_homedir("~/.pwmd/ca-cert.pem");
272 if (!tmp) {
273 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
274 goto fail;
277 n = gnutls_certificate_set_x509_trust_file(x509_cred, tmp,
278 GNUTLS_X509_FMT_PEM);
280 if (n < 0) {
281 log_write("%s: %s", tmp, gnutls_strerror(n));
282 g_free(tmp);
283 goto fail;
286 g_free(tmp);
287 tmp = expand_homedir("~/.pwmd/server-cert.pem");
289 if (!tmp) {
290 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
291 goto fail;
294 tmp2 = expand_homedir("~/.pwmd/server-key.pem");
296 if (!tmp2) {
297 g_free(tmp);
298 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
299 goto fail;
302 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
303 GNUTLS_X509_FMT_PEM);
304 g_free(tmp);
305 g_free(tmp2);
307 if (n != GNUTLS_E_SUCCESS) {
308 log_write("%s", gnutls_strerror(n));
309 goto fail;
312 log_write("%s", N_("Generating key exchange parameters..."));
313 n = gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
315 if (n) {
316 log_write("%s", gpg_strerror(n));
317 goto fail;
320 n = gnutls_dh_params_init(&dh_params);
322 if (n != GNUTLS_E_SUCCESS) {
323 log_write("%s", gnutls_strerror(n));
324 goto fail;
327 n = gnutls_dh_params_generate2(dh_params, 1024);
329 if (n != GNUTLS_E_SUCCESS) {
330 log_write("%s", gnutls_strerror(n));
331 goto fail;
334 gnutls_certificate_set_dh_params(x509_cred, dh_params);
335 n = gnutls_rsa_params_init(&rsa_params);
337 if (n != GNUTLS_E_SUCCESS) {
338 log_write("%s", gnutls_strerror(n));
339 goto fail;
342 n = gnutls_rsa_params_generate2(rsa_params, 512);
344 if (n != GNUTLS_E_SUCCESS) {
345 log_write("%s", gnutls_strerror(n));
346 goto fail;
349 gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
350 gnutls_certificate_set_params_function(x509_cred, tls_get_params);
351 return TRUE;
353 fail:
354 return FALSE;
357 void deinitTlsParams()
359 if (dh_params) {
360 gnutls_dh_params_deinit(dh_params);
361 dh_params = NULL;
364 if (rsa_params) {
365 gnutls_rsa_params_deinit(rsa_params);
366 rsa_params = NULL;
369 if (x509_cred) {
370 gnutls_certificate_free_credentials(x509_cred);
371 x509_cred = NULL;