2 Copyright (C) 2012, 2013
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of libpwmd.
7 Libpwmd 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 Libpwmd 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 Libpwmd. If not, see <http://www.gnu.org/licenses/>.
29 #include <sys/types.h>
30 #include <sys/socket.h>
40 #define WAIT_INTERVAL 50000
41 #define TEST_TIMEOUT(timeout, ret, start, rc) \
43 if (ret == GNUTLS_E_AGAIN) \
45 time_t now = time (NULL); \
46 if (timeout && now - start >= timeout) \
48 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
52 if (ret != GNUTLS_E_INTERRUPTED) \
54 struct timeval tv = { 0, WAIT_INTERVAL }; \
55 select (0, NULL, NULL, NULL, &tv); \
60 tls_free (struct tls_s
*tls
)
66 pwmd_free (tls
->cert
);
68 pwmd_free (tls
->priority
);
69 pwmd_free (tls
->server_fp
);
74 time_t start
= time (NULL
);
80 ret
= gnutls_bye (tls
->session
, GNUTLS_SHUT_WR
);
81 TEST_TIMEOUT (tls
->timeout
, ret
, start
, rc
);
85 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
87 gnutls_deinit (tls
->session
);
91 gnutls_certificate_free_credentials (tls
->x509
);
99 read_hook_tls (struct tls_s
*tls
, assuan_fd_t fd
, void *data
, size_t len
)
105 time_t start
= time (NULL
);
112 ret
= gnutls_record_recv (tls
->session
, data
, len
);
113 if (ret
== GNUTLS_E_REHANDSHAKE
)
115 ret
= gnutls_rehandshake (tls
->session
);
116 if (ret
== GNUTLS_E_GOT_APPLICATION_DATA
)
118 else if (ret
!= GNUTLS_E_SUCCESS
)
126 ret
= gnutls_handshake (tls
->session
);
128 TEST_TIMEOUT (tls
->timeout
, ret
, start
, rc
);
137 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
145 TEST_TIMEOUT (tls
->timeout
, ret
, start
, rc
);
154 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
156 return ret
<= 0 ? 0 : ret
;
160 write_hook_tls (struct tls_s
* tls
, assuan_fd_t fd
, const void *data
,
163 /* This is probably the BYE command after a previous call timed
164 * out. If not done, the time(2) call seems to hang.*/
169 time_t start
= time (NULL
);
175 ret
= gnutls_record_send (tls
->session
, data
, len
);
176 TEST_TIMEOUT (tls
->timeout
, ret
, start
, rc
);
185 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
187 return ret
<= 0 ? 0 : ret
;
191 tls_fingerprint (gnutls_session_t ses
, char **result
)
193 gnutls_x509_crt_t crt
;
194 const gnutls_datum_t
*cert_list
;
196 unsigned char buf
[20];
197 size_t len
= sizeof (buf
);
200 gnutls_x509_crt_init (&crt
);
202 return GPG_ERR_ENOMEM
;
204 cert_list
= gnutls_certificate_get_peers (ses
, &count
);
207 gnutls_x509_crt_import (crt
, &cert_list
[0], GNUTLS_X509_FMT_DER
);
208 gnutls_x509_crt_get_fingerprint (crt
, GNUTLS_DIG_SHA1
, buf
, &len
);
211 gnutls_x509_crt_deinit (crt
);
212 *result
= bin2hex (buf
, len
);
213 return *result
? 0 : GPG_ERR_ENOMEM
;
217 verify_certificate (pwm_t
* pwm
)
220 const gnutls_datum_t
*cert_list
;
221 unsigned int cert_list_size
;
223 gnutls_x509_crt_t cert
;
226 i
= gnutls_certificate_verify_peers2 (pwm
->tcp
->tls
->session
, &status
);
230 return GPG_ERR_BAD_CERT
;
233 if (status
& GNUTLS_CERT_INVALID
)
234 return GPG_ERR_BAD_CERT
;
236 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
237 return GPG_ERR_BAD_SIGNATURE
;
239 if (status
& GNUTLS_CERT_REVOKED
)
240 return GPG_ERR_CERT_REVOKED
;
242 if (gnutls_certificate_type_get (pwm
->tcp
->tls
->session
) != GNUTLS_CRT_X509
)
243 return GPG_ERR_UNSUPPORTED_CERT
;
245 if (gnutls_x509_crt_init (&cert
) < 0)
246 return gpg_error_from_errno (ENOMEM
);
248 cert_list
= gnutls_certificate_get_peers (pwm
->tcp
->tls
->session
,
252 rc
= GPG_ERR_MISSING_CERT
;
256 for (i
= 0; i
< cert_list_size
; i
++)
258 if (gnutls_x509_crt_import (cert
, &cert_list
[i
], GNUTLS_X509_FMT_DER
) <
261 rc
= GPG_ERR_BAD_CERT_CHAIN
;
265 if (gnutls_x509_crt_get_expiration_time (cert
) < time (0))
267 rc
= GPG_ERR_CERT_EXPIRED
;
271 if (gnutls_x509_crt_get_activation_time (cert
) > time (0))
273 rc
= GPG_ERR_CERT_TOO_YOUNG
;
277 if (pwm
->tcp
->tls
->verify
)
279 if (!gnutls_x509_crt_check_hostname (cert
, pwm
->tcp
->host
))
281 rc
= GPG_ERR_BAD_CERT_CHAIN
;
287 if (pwm
->tcp
->tls
->server_fp
)
291 rc
= tls_fingerprint (pwm
->tcp
->tls
->session
, &result
);
294 if (!result
|| !*result
||
295 strcasecmp (result
, pwm
->tcp
->tls
->server_fp
))
296 rc
= GPG_ERR_BAD_CERT
;
303 gnutls_x509_crt_deinit (cert
);
308 tls_init (pwm_t
* pwm
)
314 ret
= gnutls_certificate_allocate_credentials (&pwm
->tcp
->tls
->x509
);
316 return gpg_error_from_errno (ENOMEM
);
318 /* The client certificate must be signed by the CA of the pwmd server
319 * certificate in order for the client to authenticate successfully. Man in
320 * the middle attacks are still possible if the attacker is running a pwmd
321 * that doesn't require client certificate authentication. So require the
322 * client to verify the server certificate.
324 ret
= gnutls_certificate_set_x509_trust_file (pwm
->tcp
->tls
->x509
,
326 GNUTLS_X509_FMT_PEM
);
330 rc
= GPG_ERR_INV_CERT_OBJ
;
334 ret
= gnutls_certificate_set_x509_key_file (pwm
->tcp
->tls
->x509
,
337 GNUTLS_X509_FMT_PEM
);
338 if (ret
!= GNUTLS_E_SUCCESS
)
341 rc
= GPG_ERR_INV_CERT_OBJ
;
345 ret
= gnutls_init (&pwm
->tcp
->tls
->session
, GNUTLS_CLIENT
);
346 if (ret
!= GNUTLS_E_SUCCESS
)
349 rc
= GPG_ERR_INV_CERT_OBJ
;
353 ret
= gnutls_priority_set_direct (pwm
->tcp
->tls
->session
,
354 pwm
->tcp
->tls
->priority
? pwm
->tcp
->tls
->
355 priority
: "SECURE256", &errstr
);
356 if (ret
!= GNUTLS_E_SUCCESS
)
359 rc
= GPG_ERR_INV_CERT_OBJ
;
363 ret
= gnutls_credentials_set (pwm
->tcp
->tls
->session
, GNUTLS_CRD_CERTIFICATE
,
364 pwm
->tcp
->tls
->x509
);
365 if (ret
!= GNUTLS_E_SUCCESS
)
368 rc
= GPG_ERR_INV_CERT_OBJ
;
372 gnutls_transport_set_ptr (pwm
->tcp
->tls
->session
,
373 (gnutls_transport_ptr_t
) pwm
->fd
);
374 fcntl (pwm
->fd
, F_SETFL
, O_NONBLOCK
);
375 time_t start
= time (NULL
);
378 ret
= gnutls_handshake (pwm
->tcp
->tls
->session
);
380 TEST_TIMEOUT (pwm
->tcp
->tls
->timeout
, ret
, start
, rc
);
384 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
386 if (ret
!= GNUTLS_E_SUCCESS
)
389 rc
= GPG_ERR_INV_CERT_OBJ
;
393 rc
= verify_certificate (pwm
);
406 _do_tls_connect (pwm_t
* pwm
, const char *host
, int port
,
407 const char *cert
, const char *key
, const char *cacert
,
408 const char *prio
, const char *server_fp
, int verify
)
410 struct tcp_s
*tcp
= pwmd_calloc (1, sizeof (struct tcp_s
));
414 return GPG_ERR_ENOMEM
;
416 if (!cert
|| !key
|| !cacert
)
419 return GPG_ERR_INV_ARG
;
424 tcp
->host
= pwmd_strdup (host
);
431 tcp
->tls
= pwmd_calloc (1, sizeof (struct tls_s
));
438 tcp
->tls
->timeout
= pwm
->socket_timeout
;
439 tcp
->tls
->verify
= verify
;
440 tcp
->tls
->cert
= pwmd_strdup (cert
);
447 tcp
->tls
->key
= pwmd_strdup (key
);
454 tcp
->tls
->ca
= pwmd_strdup (cacert
);
463 tcp
->tls
->priority
= pwmd_strdup (prio
);
464 if (!tcp
->tls
->priority
)
473 tcp
->tls
->server_fp
= pwmd_strdup (server_fp
);
474 if (!tcp
->tls
->server_fp
)
481 rc
= tcp_connect_common (pwm
);
487 pwm
->tcp
->fd
= pwm
->tcp
->tls
->fd
= &pwm
->fd
;
488 rc
= assuan_socket_connect_fd (pwm
->ctx
, pwm
->fd
, 0);
503 * tls[46]://[hostname][:port]
506 _parse_tls_url (const char *str
, char **host
, int *port
)
509 return parse_hostname_common (str
, host
, port
);