Fixed status messages to use the new iteration type.
[pwmd.git] / src / tls.c
blob85dfa02946f35a7ad4e59a729dd5ce16d341a39e
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 "tls.h"
32 static gchar *tls_fingerprint(gnutls_session_t ses)
34 gnutls_x509_crt_t crt;
35 const gnutls_datum_t *cert_list;
36 guint count;
37 guchar buf[20];
38 gsize len;
40 gnutls_x509_crt_init(&crt);
42 if (!crt) {
43 log_write("%s(%i): %s(): %s", __FILE__, __LINE__, __FUNCTION__,
44 gnutls_strerror(GNUTLS_E_MEMORY_ERROR));
45 return NULL;
48 cert_list = gnutls_certificate_get_peers(ses, &count);
50 if (count) {
51 gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
52 len = sizeof(buf);
53 gnutls_x509_crt_get_fingerprint(crt, GNUTLS_MAC_SHA1, buf, &len);
56 gnutls_x509_crt_deinit(crt);
57 return tohex(buf, len);
60 static gint verify_client_certificate(guint status)
62 if (!status)
63 return 0;
65 if (status & GNUTLS_CERT_INVALID)
66 log_write(N_("client certificate is invalid"));
68 if (status & GNUTLS_CERT_REVOKED)
69 log_write(N_("client certificate is revoked"));
71 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
72 log_write(N_("client certificate has no signer"));
74 if (status & GNUTLS_CERT_SIGNER_NOT_CA)
75 log_write(N_("client certificate signer is not from CA"));
77 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
78 log_write(N_("client certificate has insecure algorithm"));
80 return GNUTLS_E_CERTIFICATE_ERROR;
83 struct tls_s *tls_init(gint fd, const gchar *prio)
85 struct tls_s *tls = g_malloc0(sizeof(struct tls_s));
86 gint ret;
87 guint status;
88 const gchar *prio_error;
89 gnutls_kx_algorithm_t kx;
91 if (!tls) {
92 log_write("%s(%i): %s: %s", __FILE__, __LINE__, __FUNCTION__,
93 strerror(ENOMEM));
94 return NULL;
97 ret = gnutls_init(&tls->ses, GNUTLS_SERVER);
99 if (ret != GNUTLS_E_SUCCESS)
100 goto fail;
102 ret = gnutls_priority_set_direct(tls->ses, prio, &prio_error);
104 if (ret != GNUTLS_E_SUCCESS)
105 goto fail;
107 ret = gnutls_credentials_set(tls->ses, GNUTLS_CRD_CERTIFICATE, x509_cred);
109 if (ret != GNUTLS_E_SUCCESS)
110 goto fail;
112 gnutls_certificate_server_set_request(tls->ses, GNUTLS_CERT_REQUIRE);
113 gnutls_transport_set_ptr(tls->ses, (gnutls_transport_ptr_t)fd);
114 ret = gnutls_handshake(tls->ses);
116 if (ret != GNUTLS_E_SUCCESS)
117 goto fail;
119 ret = gnutls_certificate_verify_peers2(tls->ses, &status);
121 if (ret)
122 goto fail;
124 kx = gnutls_kx_get(tls->ses);
125 tls->fp = tls_fingerprint(tls->ses);
126 log_write("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
127 gnutls_protocol_get_name(gnutls_protocol_get_version(tls->ses)),
128 gnutls_cipher_get_name(gnutls_cipher_get(tls->ses)),
129 gnutls_mac_get_name(gnutls_mac_get(tls->ses)),
130 gnutls_kx_get_name(kx),
131 gnutls_dh_get_prime_bits(tls->ses),
132 tls->fp ? tls->fp : "N/A");
133 ret = verify_client_certificate(status);
135 if (ret)
136 goto fail;
138 return tls;
140 fail:
141 log_write("%s", gnutls_strerror(ret));
142 gnutls_deinit(tls->ses);
144 if (tls->fp)
145 g_free(tls->fp);
147 g_free(tls);
148 return NULL;
151 /* From the documentation. */
152 gint tls_get_params(gnutls_session_t ses, gnutls_params_type_t type,
153 gnutls_params_st *st)
155 if (type == GNUTLS_PARAMS_RSA_EXPORT)
156 st->params.rsa_export = rsa_params;
157 else if (type == GNUTLS_PARAMS_DH)
158 st->params.dh = dh_params;
159 else
160 return -1;
162 st->type = type;
163 st->deinit = 0;
164 return 0;
167 void tls_log(gint level, const char *msg)
169 log_write("TLS: %i: %s", level, msg);
172 int read_hook(assuan_context_t ctx, assuan_fd_t fd, void *data,
173 size_t len, ssize_t *ret)
175 struct client_s *cl = assuan_get_pointer(ctx);
177 if (!cl || !cl->thd->remote)
178 *ret = read((int)fd, data, len);
179 else {
180 do {
181 *ret = gnutls_record_recv(cl->thd->tls->ses, data, len);
183 if (*ret == GNUTLS_E_REHANDSHAKE) {
184 *ret = gnutls_rehandshake(cl->thd->tls->ses);
186 if (*ret == GNUTLS_E_WARNING_ALERT_RECEIVED ||
187 *ret == GNUTLS_A_NO_RENEGOTIATION) {
188 log_write("%s", gnutls_strerror(*ret));
189 continue;
192 if (*ret != GNUTLS_E_SUCCESS) {
193 log_write("%s", gnutls_strerror(*ret));
194 *ret = 0;
195 break;
198 *ret = gnutls_handshake(cl->thd->tls->ses);
200 if (*ret != GNUTLS_E_SUCCESS) {
201 log_write("%s", gnutls_strerror(*ret));
202 *ret = 0;
203 break;
206 continue;
208 } while (*ret == GNUTLS_E_INTERRUPTED || *ret == GNUTLS_E_AGAIN);
211 return *ret <= 0 ? 0 : 1;
214 int write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data,
215 size_t len, ssize_t *ret)
217 struct client_s *cl = assuan_get_pointer(ctx);
219 if (!cl || !cl->thd->remote)
220 *ret = write((int)fd, data, len);
221 else {
222 do {
223 *ret = gnutls_record_send(cl->thd->tls->ses, data, len);
224 } while (*ret == GNUTLS_E_INTERRUPTED || *ret == GNUTLS_E_AGAIN);
227 return *ret <= 0 ? 0 : 1;
230 gboolean initTlsParams()
232 gint n;
233 gchar *tmp, *tmp2;
235 n = gnutls_certificate_allocate_credentials(&x509_cred);
237 if (n != GNUTLS_E_SUCCESS) {
238 log_write("%s", gnutls_strerror(n));
239 x509_cred = NULL;
240 goto fail;
243 if (g_key_file_has_key(keyfileh, "global", "tcp_use_crl", NULL) &&
244 get_key_file_boolean("global", "tcp_use_crl") == TRUE) {
245 tmp = expand_homedir("~/.pwmd/crl.pem");
247 if (!tmp) {
248 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
249 goto fail;
252 n = gnutls_certificate_set_x509_crl_file(x509_cred, tmp,
253 GNUTLS_X509_FMT_PEM);
255 if (n < 0) {
256 log_write("%s: %s", tmp, gnutls_strerror(n));
257 g_free(tmp);
258 goto fail;
261 g_free(tmp);
264 tmp = expand_homedir("~/.pwmd/ca-cert.pem");
266 if (!tmp) {
267 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
268 goto fail;
271 n = gnutls_certificate_set_x509_trust_file(x509_cred, tmp,
272 GNUTLS_X509_FMT_PEM);
274 if (n < 0) {
275 log_write("%s: %s", tmp, gnutls_strerror(n));
276 g_free(tmp);
277 goto fail;
280 g_free(tmp);
281 tmp = expand_homedir("~/.pwmd/server-cert.pem");
283 if (!tmp) {
284 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
285 goto fail;
288 tmp2 = expand_homedir("~/.pwmd/server-key.pem");
290 if (!tmp2) {
291 g_free(tmp);
292 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
293 goto fail;
296 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
297 GNUTLS_X509_FMT_PEM);
298 g_free(tmp);
299 g_free(tmp2);
301 if (n != GNUTLS_E_SUCCESS) {
302 log_write("%s", gnutls_strerror(n));
303 goto fail;
306 log_write("%s", N_("Generating key exchange parameters..."));
307 n = gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
309 if (n) {
310 log_write("%s", gpg_strerror(n));
311 goto fail;
314 n = gnutls_dh_params_init(&dh_params);
316 if (n != GNUTLS_E_SUCCESS) {
317 log_write("%s", gnutls_strerror(n));
318 goto fail;
321 n = gnutls_dh_params_generate2(dh_params, 1024);
323 if (n != GNUTLS_E_SUCCESS) {
324 log_write("%s", gnutls_strerror(n));
325 goto fail;
328 gnutls_certificate_set_dh_params(x509_cred, dh_params);
329 n = gnutls_rsa_params_init(&rsa_params);
331 if (n != GNUTLS_E_SUCCESS) {
332 log_write("%s", gnutls_strerror(n));
333 goto fail;
336 n = gnutls_rsa_params_generate2(rsa_params, 512);
338 if (n != GNUTLS_E_SUCCESS) {
339 log_write("%s", gnutls_strerror(n));
340 goto fail;
343 gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
344 gnutls_certificate_set_params_function(x509_cred, tls_get_params);
345 return TRUE;
347 fail:
348 return FALSE;
351 void deinitTlsParams()
353 if (dh_params) {
354 gnutls_dh_params_deinit(dh_params);
355 dh_params = NULL;
358 if (rsa_params) {
359 gnutls_rsa_params_deinit(rsa_params);
360 rsa_params = NULL;
363 if (x509_cred) {
364 gnutls_certificate_free_credentials(x509_cred);
365 x509_cred = NULL;