2 Copyright (C) 2012-2018 Ben Kibbey <bjk@luxsci.net>
4 This file is part of libpwmd.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
30 #include <sys/types.h>
38 #include <sys/socket.h>
44 #define DEFAULT_TLS_PRIORITY "SECURE256:SECURE192:SECURE128:" \
45 "-VERS-SSL3.0:-VERS-TLS1.0"
46 #define WAIT_INTERVAL 50000
47 #define TEST_TIMEOUT(pwm, timeout, ret, start, rc) \
50 if (ret == GNUTLS_E_AGAIN || pwm->cancel) \
52 time_t now = time (NULL); \
53 if (pwm->cancel || (timeout && now - start >= timeout)) \
56 rc = gpg_error (GPG_ERR_CANCELED); \
58 rc = gpg_error (GPG_ERR_ETIMEDOUT); \
59 ret = GNUTLS_E_TIMEDOUT; \
62 struct timeval totv = { 0, WAIT_INTERVAL }; \
63 select (0, NULL, NULL, NULL, &totv); \
65 if (ret != GNUTLS_E_INTERRUPTED) \
73 struct tls_s
*tls
= pwm
->tcp
->tls
;
80 pwmd_free (tls
->cert
);
82 pwmd_free (tls
->priority
);
83 pwmd_free (tls
->server_fp
);
87 time_t start
= time (NULL
);
93 ret
= gnutls_bye (tls
->session
, GNUTLS_SHUT_WR
);
94 TEST_TIMEOUT (pwm
, tls
->timeout
, ret
, start
, rc
);
98 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
100 gnutls_deinit (tls
->session
);
102 else if (pwm
->fd
!= ASSUAN_INVALID_FD
)
104 __assuan_close (pwm
->ctx
, pwm
->fd
);
105 pwm
->fd
= ASSUAN_INVALID_FD
;
109 gnutls_certificate_free_credentials (tls
->x509
);
114 pwm
->tls_error
= ret
;
118 tls_read_hook (pwm_t
*pwm
, assuan_fd_t fd
, void *data
, size_t len
)
120 struct tls_s
*tls
= pwm
->tcp
->tls
;
122 time_t start
= time (NULL
);
124 if (pwm
->fd
== ASSUAN_INVALID_FD
)
131 struct timeval tv
= { 0, 0 };
136 FD_SET (HANDLE2SOCKET (pwm
->fd
), &fds
);
137 n
= select (HANDLE2SOCKET (pwm
->fd
)+1, &fds
, NULL
, NULL
, &tv
);
138 if (n
== 0 && tls
->nl
)
142 memcpy (data
, c
, sizeof(c
));
147 ret
= gnutls_record_recv (tls
->session
, data
, len
);
148 if (ret
== GNUTLS_E_REHANDSHAKE
)
152 ret
= gnutls_handshake (tls
->session
);
153 TEST_TIMEOUT (pwm
, tls
->timeout
, ret
, start
, rc
);
157 while (ret
< 0 && gnutls_error_is_fatal (ret
) == 0);
163 memcpy (data
, c
, sizeof(c
));
170 ret
= GNUTLS_E_AGAIN
;
173 TEST_TIMEOUT (pwm
, tls
->timeout
, ret
, start
, rc
);
177 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
180 pwm
->tls_error
= ret
;
184 else if (ret
== GNUTLS_E_PREMATURE_TERMINATION
)
188 tls
->nl
= ((char *)data
)[ret
-1] == '\n';
190 return ret
< 0 ? -1 : ret
;
194 tls_write_hook (pwm_t
*pwm
, assuan_fd_t fd
, const void *data
, size_t len
)
196 struct tls_s
*tls
= pwm
->tcp
->tls
;
198 /* This is probably the BYE command after a previous call timed
199 * out. If not done, the time(2) call seems to hang.*/
200 if (pwm
->fd
== ASSUAN_INVALID_FD
)
204 time_t start
= time (NULL
);
211 ret
= gnutls_record_send (tls
->session
, data
, len
);
212 TEST_TIMEOUT (pwm
, tls
->timeout
, ret
, start
, rc
);
216 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
219 pwm
->tls_error
= ret
;
223 else if (ret
== GNUTLS_E_PREMATURE_TERMINATION
)
226 return ret
< 0 ? -1 : ret
;
230 tls_fingerprint (pwm_t
*pwm
, char **result
)
232 gnutls_session_t ses
= pwm
->tcp
->tls
->session
;
233 gnutls_x509_crt_t crt
;
234 const gnutls_datum_t
*cert_list
;
236 unsigned char buf
[32];
237 size_t len
= sizeof (buf
);
240 gnutls_x509_crt_init (&crt
);
242 return GPG_ERR_ENOMEM
;
244 cert_list
= gnutls_certificate_get_peers (ses
, &count
);
247 pwm
->tls_error
= gnutls_x509_crt_import (crt
, &cert_list
[0],
248 GNUTLS_X509_FMT_DER
);
250 gnutls_x509_crt_get_fingerprint (crt
, GNUTLS_DIG_SHA256
, buf
, &len
);
253 gnutls_x509_crt_deinit (crt
);
256 pwm
->tls_error
= GNUTLS_CERT_INVALID
;
257 return GPG_ERR_MISSING_CERT
;
261 *result
= bin2hex (buf
, len
);
263 return !pwm
->tls_error
? 0 : GPG_ERR_ENOMEM
;
267 verify_server_certificate (unsigned status
)
269 if (status
& GNUTLS_CERT_REVOKED
)
271 fprintf (stderr
, "server certificate is revoked\n");
272 return GPG_ERR_CERT_REVOKED
;
275 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
277 fprintf (stderr
, "server certificate has no signer\n");
278 return GPG_ERR_NO_SIGNATURE_SCHEME
;
281 if (status
& GNUTLS_CERT_SIGNATURE_FAILURE
)
283 fprintf (stderr
, "server certificate signature verification failed\n");
284 return GPG_ERR_BAD_SIGNATURE
;
287 if (status
& GNUTLS_CERT_EXPIRED
)
289 fprintf (stderr
, "server certificate expired\n");
290 return GPG_ERR_CERT_EXPIRED
;
293 if (status
& GNUTLS_CERT_SIGNER_NOT_CA
)
295 fprintf (stderr
, "server certificate signer is not from CA\n");
296 return GPG_ERR_BAD_CA_CERT
;
299 if (status
& GNUTLS_CERT_INSECURE_ALGORITHM
)
301 fprintf (stderr
, "server certificate has insecure algorithm\n");
302 return GPG_ERR_UNSUPPORTED_ALGORITHM
;
307 fprintf (stderr
, "server certificate is invalid: %u\n", status
);
308 return GPG_ERR_BAD_CERT
;
315 verify_certificate (pwm_t
*pwm
)
318 const gnutls_datum_t
*cert_list
;
319 unsigned int cert_list_size
;
321 gnutls_x509_crt_t cert
;
324 i
= gnutls_certificate_verify_peers2 (pwm
->tcp
->tls
->session
, &status
);
328 return GPG_ERR_BAD_CERT
;
331 rc
= verify_server_certificate (status
);
335 if (gnutls_certificate_type_get (pwm
->tcp
->tls
->session
) != GNUTLS_CRT_X509
)
336 return GPG_ERR_UNSUPPORTED_CERT
;
338 if (gnutls_x509_crt_init (&cert
) < 0)
339 return gpg_error_from_errno (ENOMEM
);
341 cert_list
= gnutls_certificate_get_peers (pwm
->tcp
->tls
->session
,
345 rc
= GPG_ERR_MISSING_CERT
;
349 for (i
= 0; i
< cert_list_size
; i
++)
351 pwm
->tls_error
= gnutls_x509_crt_import (cert
, &cert_list
[i
],
352 GNUTLS_X509_FMT_DER
);
353 if (pwm
->tls_error
< 0)
355 rc
= GPG_ERR_BAD_CERT_CHAIN
;
359 if (gnutls_x509_crt_get_expiration_time (cert
) < time (0))
361 rc
= GPG_ERR_CERT_EXPIRED
;
365 if (gnutls_x509_crt_get_activation_time (cert
) > time (0))
367 rc
= GPG_ERR_CERT_TOO_YOUNG
;
371 if (pwm
->tcp
->tls
->verify
)
373 if (!gnutls_x509_crt_check_hostname (cert
, pwm
->tcp
->host
))
375 rc
= GPG_ERR_BAD_CERT_CHAIN
;
381 if (pwm
->tcp
->tls
->server_fp
)
385 rc
= tls_fingerprint (pwm
, &result
);
388 if (!result
|| !*result
||
389 strcasecmp (result
, pwm
->tcp
->tls
->server_fp
))
390 rc
= GPG_ERR_BAD_CERT
;
397 gnutls_x509_crt_deinit (cert
);
402 tls_init (pwm_t
* pwm
)
408 ret
= gnutls_certificate_allocate_credentials (&pwm
->tcp
->tls
->x509
);
411 pwm
->tls_error
= ret
;
412 return gpg_error_from_errno (ENOMEM
);
415 /* The client certificate must be signed by the CA of the pwmd server
416 * certificate in order for the client to authenticate successfully. Man in
417 * the middle attacks are still possible if the attacker is running a pwmd
418 * that doesn't require client certificate authentication. So require the
419 * client to verify the server certificate.
421 ret
= gnutls_certificate_set_x509_trust_file (pwm
->tcp
->tls
->x509
,
423 GNUTLS_X509_FMT_PEM
);
426 rc
= GPG_ERR_INV_CERT_OBJ
;
430 ret
= gnutls_certificate_set_x509_key_file (pwm
->tcp
->tls
->x509
,
433 GNUTLS_X509_FMT_PEM
);
434 if (ret
!= GNUTLS_E_SUCCESS
)
436 pwm
->tls_error
= ret
;
437 rc
= GPG_ERR_INV_CERT_OBJ
;
441 ret
= gnutls_init (&pwm
->tcp
->tls
->session
, GNUTLS_CLIENT
);
442 if (ret
!= GNUTLS_E_SUCCESS
)
444 pwm
->tls_error
= ret
;
445 rc
= GPG_ERR_INV_CERT_OBJ
;
449 ret
= gnutls_priority_set_direct (pwm
->tcp
->tls
->session
,
450 pwm
->tcp
->tls
->priority
451 ? pwm
->tcp
->tls
->priority
452 : DEFAULT_TLS_PRIORITY
,
454 if (ret
!= GNUTLS_E_SUCCESS
)
456 pwm
->tls_error
= ret
;
457 rc
= GPG_ERR_INV_CERT_OBJ
;
461 ret
= gnutls_credentials_set (pwm
->tcp
->tls
->session
, GNUTLS_CRD_CERTIFICATE
,
462 pwm
->tcp
->tls
->x509
);
463 if (ret
!= GNUTLS_E_SUCCESS
)
465 pwm
->tls_error
= ret
;
466 rc
= GPG_ERR_INV_CERT_OBJ
;
470 gnutls_transport_set_ptr (pwm
->tcp
->tls
->session
,
471 (gnutls_transport_ptr_t
) HANDLE2SOCKET (pwm
->fd
));
472 rc
= set_non_blocking (pwm
->fd
, 1);
476 time_t start
= time (NULL
);
479 ret
= gnutls_handshake (pwm
->tcp
->tls
->session
);
480 TEST_TIMEOUT (pwm
, pwm
->tcp
->tls
->timeout
, ret
, start
, rc
);
484 while (ret
== GNUTLS_E_AGAIN
|| ret
== GNUTLS_E_INTERRUPTED
);
486 if (ret
!= GNUTLS_E_SUCCESS
)
488 pwm
->tls_error
= ret
;
489 rc
= GPG_ERR_INV_CERT_OBJ
;
493 rc
= verify_certificate (pwm
);
498 /* Keep the original error since it maybe overwritten during TLS
500 ret
= pwm
->tls_error
;
501 /* The session may not have completed a handshake. Will crash during
503 gnutls_deinit (pwm
->tcp
->tls
->session
);
504 pwm
->tcp
->tls
->session
= NULL
;
506 pwm
->tls_error
= ret
;
513 tls_connect (pwm_t
* pwm
, const char *host
, int port
, const char *cert
,
514 const char *key
, const char *cacert
, const char *prio
,
515 const char *server_fp
, int verify
)
520 if (!cert
|| !key
|| !cacert
)
521 return GPG_ERR_INV_ARG
;
523 tcp
= pwmd_calloc (1, sizeof (struct tcp_s
));
525 return GPG_ERR_ENOMEM
;
527 pthread_cond_init (&tcp
->dns_cond
, NULL
);
528 pthread_mutex_init (&tcp
->dns_mutex
, NULL
);
531 tcp
->host
= pwmd_strdup (host
);
538 tcp
->tls
= pwmd_calloc (1, sizeof (struct tls_s
));
545 tcp
->tls
->timeout
= pwm
->socket_timeout
;
546 tcp
->tls
->verify
= verify
;
547 tcp
->tls
->cert
= pwmd_strdup (cert
);
554 tcp
->tls
->key
= pwmd_strdup (key
);
561 tcp
->tls
->ca
= pwmd_strdup (cacert
);
570 tcp
->tls
->priority
= pwmd_strdup (prio
);
571 if (!tcp
->tls
->priority
)
580 tcp
->tls
->server_fp
= pwmd_strdup (server_fp
);
581 if (!tcp
->tls
->server_fp
)
588 rc
= tcp_connect_common (pwm
);
595 rc
= assuan_socket_connect_fd (pwm
->ctx
, pwm
->fh
, 0);
597 rc
= assuan_socket_connect_fd (pwm
->ctx
, pwm
->fd
, 0);
610 * tls[46]://[hostname][:port]
613 tls_parse_url (const char *str
, char **host
, int *port
)
616 return parse_hostname_common (str
, host
, port
);