2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
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>
28 #include "pwmd-error.h"
30 #include "util-misc.h"
31 #include "util-string.h"
36 #define WAIT_INTERVAL 50000
37 #define TEST_TIMEOUT(timeout, ret, start, rc) \
39 if (ret == GNUTLS_E_AGAIN) \
41 time_t now = time (NULL); \
42 if (timeout && now - start >= timeout) \
44 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
48 if (ret != GNUTLS_E_INTERRUPTED) \
50 struct timeval tv = { 0, WAIT_INTERVAL }; \
51 select (0, NULL, NULL, NULL, &tv); \
56 tls_fingerprint (gnutls_session_t ses
)
58 gnutls_x509_crt_t crt
;
59 const gnutls_datum_t
*cert_list
;
61 unsigned char buf
[20];
62 size_t len
= sizeof (buf
);
64 gnutls_x509_crt_init (&crt
);
67 log_write ("%s(%i): %s(): %s", __FILE__
, __LINE__
, __FUNCTION__
,
68 gnutls_strerror (GNUTLS_E_MEMORY_ERROR
));
72 cert_list
= gnutls_certificate_get_peers (ses
, &count
);
75 gnutls_x509_crt_import (crt
, &cert_list
[0], GNUTLS_X509_FMT_DER
);
76 gnutls_x509_crt_get_fingerprint (crt
, GNUTLS_DIG_SHA1
, buf
, &len
);
79 gnutls_x509_crt_deinit (crt
);
80 return bin2hex (buf
, len
);
84 verify_client_certificate (unsigned status
)
89 if (status
& GNUTLS_CERT_INVALID
)
90 log_write (_("client certificate is invalid"));
92 if (status
& GNUTLS_CERT_REVOKED
)
93 log_write (_("client certificate is revoked"));
95 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
96 log_write (_("client certificate has no signer"));
98 if (status
& GNUTLS_CERT_SIGNER_NOT_CA
)
99 log_write (_("client certificate signer is not from CA"));
101 if (status
& GNUTLS_CERT_INSECURE_ALGORITHM
)
102 log_write (_("client certificate has insecure algorithm"));
104 return GNUTLS_E_CERTIFICATE_ERROR
;
108 tls_init (int fd
, int timeout
, const char *prio
)
110 struct tls_s
*tls
= xcalloc (1, sizeof (struct tls_s
));
113 const char *prio_error
;
114 gnutls_kx_algorithm_t kx
;
118 log_write ("%s(%i): %s: %s", __FILE__
, __LINE__
, __FUNCTION__
,
123 ret
= gnutls_init (&tls
->ses
, GNUTLS_SERVER
);
124 if (ret
!= GNUTLS_E_SUCCESS
)
127 ret
= gnutls_priority_set_direct (tls
->ses
, prio
, &prio_error
);
128 if (ret
!= GNUTLS_E_SUCCESS
)
131 ret
= gnutls_credentials_set (tls
->ses
, GNUTLS_CRD_CERTIFICATE
, x509_cred
);
132 if (ret
!= GNUTLS_E_SUCCESS
)
135 gnutls_certificate_server_set_request (tls
->ses
, GNUTLS_CERT_REQUIRE
);
136 gnutls_transport_set_ptr (tls
->ses
, (gnutls_transport_ptr_t
) fd
);
137 time_t start
= time (NULL
);
142 ret
= gnutls_handshake (tls
->ses
);
143 TEST_TIMEOUT (timeout
, ret
, start
, rc
);
147 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
149 if (ret
!= GNUTLS_E_SUCCESS
)
152 ret
= gnutls_certificate_verify_peers2 (tls
->ses
, &status
);
156 kx
= gnutls_kx_get (tls
->ses
);
157 tls
->fp
= tls_fingerprint (tls
->ses
);
158 log_write ("PROTO=%s CIPHER=%s MAC=%s KX=%s(%d) FP=%s",
159 gnutls_protocol_get_name (gnutls_protocol_get_version
161 gnutls_cipher_get_name (gnutls_cipher_get (tls
->ses
)),
162 gnutls_mac_get_name (gnutls_mac_get (tls
->ses
)),
163 gnutls_kx_get_name (kx
), gnutls_dh_get_prime_bits (tls
->ses
),
164 tls
->fp
? tls
->fp
: "N/A");
165 ret
= verify_client_certificate (status
);
172 log_write ("%s", gnutls_strerror (ret
));
173 gnutls_deinit (tls
->ses
);
179 /* From the documentation. */
181 tls_get_params (gnutls_session_t ses
, gnutls_params_type_t type
,
182 gnutls_params_st
* st
)
184 if (type
== GNUTLS_PARAMS_RSA_EXPORT
)
185 st
->params
.rsa_export
= rsa_params
;
186 else if (type
== GNUTLS_PARAMS_DH
)
187 st
->params
.dh
= dh_params
;
197 tls_log (int level
, const char *msg
)
199 log_write ("TLS: %i: %s", level
, msg
);
203 tls_read_hook (assuan_context_t ctx
, assuan_fd_t fd
, void *data
, size_t len
)
205 struct client_s
*client
= assuan_get_pointer (ctx
);
206 time_t start
= time (NULL
);
214 ret
= gnutls_record_recv (client
->thd
->tls
->ses
, data
, len
);
215 if (ret
== GNUTLS_E_REHANDSHAKE
)
217 ret
= gnutls_rehandshake (client
->thd
->tls
->ses
);
218 if (ret
== GNUTLS_E_GOT_APPLICATION_DATA
)
220 else if (ret
!= GNUTLS_E_SUCCESS
)
222 log_write ("%s", gnutls_strerror (ret
));
229 ret
= gnutls_handshake (client
->thd
->tls
->ses
);
231 TEST_TIMEOUT (client
->thd
->timeout
, ret
, start
, rc
);
234 log_write ("%s", gnutls_strerror (ret
));
236 close (client
->thd
->fd
);
237 client
->thd
->fd
= -1;
241 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
249 TEST_TIMEOUT (client
->thd
->timeout
, ret
, start
, rc
);
253 close (client
->thd
->fd
);
254 client
->thd
->fd
= -1;
258 while (ret
== GNUTLS_E_INTERRUPTED
|| ret
== GNUTLS_E_AGAIN
);
262 log_write ("%s", gnutls_strerror (ret
));
270 tls_write_hook (assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
273 struct client_s
*client
= assuan_get_pointer (ctx
);
275 time_t start
= time (NULL
);
281 ret
= gnutls_record_send (client
->thd
->tls
->ses
, data
, len
);
282 TEST_TIMEOUT (client
->thd
->timeout
, ret
, start
, rc
);
286 close (client
->thd
->fd
);
287 client
->thd
->fd
= -1;
291 while (ret
== GNUTLS_E_INTERRUPTED
|| ret
== GNUTLS_E_AGAIN
);
301 gpg_error_t rc
= GPG_ERR_UNKNOWN_ERRNO
;
303 n
= gnutls_certificate_allocate_credentials (&x509_cred
);
304 if (n
!= GNUTLS_E_SUCCESS
)
306 log_write ("%s", gnutls_strerror (n
));
311 tmp
= str_asprintf ("%s/ca-cert.pem", homedir
);
318 n
= gnutls_certificate_set_x509_trust_file (x509_cred
, tmp
,
319 GNUTLS_X509_FMT_PEM
);
322 log_write ("%s: %s", tmp
, gnutls_strerror (n
));
328 tmp
= str_asprintf ("%s/server-cert.pem", homedir
);
335 tmp2
= str_asprintf ("%s/server-key.pem", homedir
);
339 log_write ("%s(%i): %s", __FILE__
, __LINE__
, strerror (ENOMEM
));
343 n
= gnutls_certificate_set_x509_key_file (x509_cred
, tmp
, tmp2
,
344 GNUTLS_X509_FMT_PEM
);
347 if (n
!= GNUTLS_E_SUCCESS
)
349 log_write ("%s", gnutls_strerror (n
));
353 log_write ("%s", _("Generating key exchange parameters..."));
354 n
= gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM
, 0);
357 log_write ("%s", pwmd_strerror (n
));
361 n
= gnutls_dh_params_init (&dh_params
);
362 if (n
!= GNUTLS_E_SUCCESS
)
364 log_write ("%s", gnutls_strerror (n
));
368 n
= gnutls_dh_params_generate2 (dh_params
, 1024);
369 if (n
!= GNUTLS_E_SUCCESS
)
371 log_write ("%s", gnutls_strerror (n
));
375 gnutls_certificate_set_dh_params (x509_cred
, dh_params
);
376 n
= gnutls_rsa_params_init (&rsa_params
);
377 if (n
!= GNUTLS_E_SUCCESS
)
379 log_write ("%s", gnutls_strerror (n
));
383 n
= gnutls_rsa_params_generate2 (rsa_params
, 512);
384 if (n
!= GNUTLS_E_SUCCESS
)
386 log_write ("%s", gnutls_strerror (n
));
390 gnutls_certificate_set_rsa_export_params (x509_cred
, rsa_params
);
391 gnutls_certificate_set_params_function (x509_cred
, tls_get_params
);
403 gnutls_dh_params_deinit (dh_params
);
409 gnutls_rsa_params_deinit (rsa_params
);
415 gnutls_certificate_free_credentials (x509_cred
);
421 do_tls_validate_access (struct client_s
*client
, const char *section
)
423 char **access
= config_get_list (section
, "tls_access");
427 if (!access
|| !*access
)
428 return GPG_ERR_INV_USER_ID
;
430 for (p
= access
; p
&& *p
; p
++)
435 if (*fp
&& *fp
== '+' && *(fp
+ 1) == 0) // allow all connections
441 if (*fp
== '!' || *fp
== '-')
449 if (*fp
== '#') // avoid confusion with ACL's: allow prefixing the hash
452 if (!strcasecmp (client
->thd
->tls
->fp
, fp
))
453 allowed
= not ? 0 : 1;
457 return allowed
? 0 : GPG_ERR_INV_USER_ID
;
461 tls_validate_access (struct client_s
*client
, const char *filename
)
463 gpg_error_t rc
= do_tls_validate_access (client
, "global");
466 rc
= do_tls_validate_access (client
, filename
);