Portability fixes. Mostly for Android.
[pwmd.git] / src / tls.c
blob28f069e2fc9ed51943aa0fc8960ef4579b9fa892
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 = expand_homedir("~/.pwmd/crl.pem");
237 if (!tmp) {
238 rc = GPG_ERR_ENOMEM;
239 goto fail;
242 n = gnutls_certificate_set_x509_crl_file(x509_cred, tmp,
243 GNUTLS_X509_FMT_PEM);
244 if (n < 0) {
245 log_write("%s: %s", tmp, gnutls_strerror(n));
246 xfree(tmp);
247 goto fail;
250 xfree(tmp);
252 else
253 MUTEX_UNLOCK(&rcfile_mutex);
255 tmp = expand_homedir("~/.pwmd/ca-cert.pem");
256 if (!tmp) {
257 rc = GPG_ERR_ENOMEM;
258 goto fail;
261 n = gnutls_certificate_set_x509_trust_file(x509_cred, tmp,
262 GNUTLS_X509_FMT_PEM);
263 if (n < 0) {
264 log_write("%s: %s", tmp, gnutls_strerror(n));
265 xfree(tmp);
266 goto fail;
269 xfree(tmp);
270 tmp = expand_homedir("~/.pwmd/server-cert.pem");
271 if (!tmp) {
272 rc = GPG_ERR_ENOMEM;
273 goto fail;
276 tmp2 = expand_homedir("~/.pwmd/server-key.pem");
277 if (!tmp2) {
278 xfree(tmp);
279 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
280 goto fail;
283 n = gnutls_certificate_set_x509_key_file (x509_cred, tmp, tmp2,
284 GNUTLS_X509_FMT_PEM);
285 xfree(tmp);
286 xfree(tmp2);
287 if (n != GNUTLS_E_SUCCESS) {
288 log_write("%s", gnutls_strerror(n));
289 goto fail;
292 log_write("%s", _("Generating key exchange parameters..."));
293 n = gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
294 if (n) {
295 log_write("%s", pwmd_strerror(n));
296 goto fail;
299 n = gnutls_dh_params_init(&dh_params);
300 if (n != GNUTLS_E_SUCCESS) {
301 log_write("%s", gnutls_strerror(n));
302 goto fail;
305 n = gnutls_dh_params_generate2(dh_params, 1024);
306 if (n != GNUTLS_E_SUCCESS) {
307 log_write("%s", gnutls_strerror(n));
308 goto fail;
311 gnutls_certificate_set_dh_params(x509_cred, dh_params);
312 n = gnutls_rsa_params_init(&rsa_params);
313 if (n != GNUTLS_E_SUCCESS) {
314 log_write("%s", gnutls_strerror(n));
315 goto fail;
318 n = gnutls_rsa_params_generate2(rsa_params, 512);
319 if (n != GNUTLS_E_SUCCESS) {
320 log_write("%s", gnutls_strerror(n));
321 goto fail;
324 gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
325 gnutls_certificate_set_params_function(x509_cred, tls_get_params);
326 return 0;
328 fail:
329 return rc;
332 void tls_deinit_params()
334 if (dh_params) {
335 gnutls_dh_params_deinit(dh_params);
336 dh_params = NULL;
339 if (rsa_params) {
340 gnutls_rsa_params_deinit(rsa_params);
341 rsa_params = NULL;
344 if (x509_cred) {
345 gnutls_certificate_free_credentials(x509_cred);
346 x509_cred = NULL;
350 gpg_error_t tls_validate_access(struct client_s *cl, const char
351 *filename)
353 char **access = config_get_list(filename, "tls_access");
354 char **p;
355 int allow = 0, deny = 0;
357 if (!access || !*access)
358 return GPG_ERR_INV_USER_ID;
360 for (p = access; p && *p; p++) {
361 int not = 0;
362 char *fp = *p;
364 if (*fp && *fp == '+' && *(fp+1) == 0) {
365 allow = 1;
366 continue;
369 if (*fp == '!') {
370 not = 1;
371 fp++;
372 if (!*fp)
373 break;
376 if (!strcasecmp(cl->thd->tls->fp, fp)) {
377 if (not) {
378 deny = 1;
379 break;
382 strv_free(access);
383 return 0;
387 /* Not allowed. */
388 strv_free(access);
389 return allow && !deny ? 0 : GPG_ERR_INV_USER_ID;