2 * Copyright (C) 2017 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 #include <sys/types.h>
53 #include <gnutls/gnutls.h>
55 static int crypto_auth
;
56 #define CRYPTO_AUTH_CERTIFICATES 1
57 #define CRYPTO_AUTH_PSK 2
59 static gnutls_certificate_credentials_t x509_creds
;
60 static gnutls_psk_server_credentials_t psk_creds
;
62 static void print_gnutls_error (int err
, const char *fs
, ...)
63 __attribute__((format (printf
, 2, 3)));
66 print_gnutls_error (int err
, const char *fs
, ...)
70 fprintf (stderr
, "%s: GnuTLS error: ", program_name
);
73 vfprintf (stderr
, fs
, args
);
76 fprintf (stderr
, ": %s\n", gnutls_strerror (err
));
79 /* Try to load certificates from 'path'. Returns true if successful.
80 * If it's not a certicate directory it returns false. Exits on
84 load_certificates (const char *path
)
86 CLEANUP_FREE
char *ca_cert_filename
= NULL
;
87 CLEANUP_FREE
char *server_cert_filename
= NULL
;
88 CLEANUP_FREE
char *server_key_filename
= NULL
;
89 CLEANUP_FREE
char *ca_crl_filename
= NULL
;
92 if (asprintf (&ca_cert_filename
, "%s/ca-cert.pem", path
) == -1) {
96 if (asprintf (&server_cert_filename
, "%s/server-cert.pem", path
) == -1) {
100 if (asprintf (&server_key_filename
, "%s/server-key.pem", path
) == -1) {
104 if (asprintf (&ca_crl_filename
, "%s/ca-crl.pem", path
) == -1) {
109 /* Our test for a certificate directory is that ca-cert.pem,
110 * server-cert.pem and server-key.pem must all exist in the path.
112 if (access (ca_cert_filename
, R_OK
) == -1)
114 if (access (server_cert_filename
, R_OK
) == -1)
116 if (access (server_key_filename
, R_OK
) == -1)
119 /* Any problem past here is a hard error. */
121 err
= gnutls_certificate_allocate_credentials (&x509_creds
);
123 print_gnutls_error (err
, "allocating credentials");
126 err
= gnutls_certificate_set_x509_trust_file (x509_creds
, ca_cert_filename
,
127 GNUTLS_X509_FMT_PEM
);
129 print_gnutls_error (err
, "loading %s", ca_cert_filename
);
133 if (access (ca_crl_filename
, R_OK
) == 0) {
134 err
= gnutls_certificate_set_x509_crl_file (x509_creds
, ca_crl_filename
,
135 GNUTLS_X509_FMT_PEM
);
137 print_gnutls_error (err
, "loading %s", ca_crl_filename
);
142 err
= gnutls_certificate_set_x509_key_file (x509_creds
,
143 server_cert_filename
,
145 GNUTLS_X509_FMT_PEM
);
147 print_gnutls_error (err
, "loading server certificate and key (%s, %s)",
148 server_cert_filename
, server_key_filename
);
152 debug ("successfully loaded TLS certificates from %s", path
);
157 start_certificates (void)
159 /* Try to locate the certificates directory and load them. */
160 if (tls_certificates_dir
== NULL
) {
162 CLEANUP_FREE
char *path
= NULL
;
164 if (geteuid () != 0) {
165 home
= getenv ("HOME");
167 if (asprintf (&path
, "%s/.pki/%s", home
, PACKAGE_NAME
) == -1) {
171 if (load_certificates (path
))
172 goto found_certificates
;
174 if (asprintf (&path
, "%s/.config/pki/%s", home
, PACKAGE_NAME
) == -1) {
178 if (load_certificates (path
))
179 goto found_certificates
;
182 else { /* geteuid () == 0 */
183 if (load_certificates (root_tls_certificates_dir
))
184 goto found_certificates
;
188 if (load_certificates (tls_certificates_dir
))
189 goto found_certificates
;
194 #ifdef HAVE_GNUTLS_CERTIFICATE_SET_KNOWN_DH_PARAMS
195 gnutls_certificate_set_known_dh_params (x509_creds
, GNUTLS_SEC_PARAM_MEDIUM
);
204 CLEANUP_FREE
char *abs_psk_file
= NULL
;
206 /* Make sure the path to the PSK file is absolute. */
207 abs_psk_file
= realpath (tls_psk
, NULL
);
208 if (abs_psk_file
== NULL
) {
213 err
= gnutls_psk_allocate_server_credentials (&psk_creds
);
215 print_gnutls_error (err
, "allocating PSK credentials");
219 /* Note that this function makes a copy of the string.
220 * CLEANUP_FREE macro above will free abs_psk_file when
221 * we return, but this is safe.
223 gnutls_psk_set_server_credentials_file (psk_creds
, abs_psk_file
);
228 /* Initialize crypto. This also handles the command line parameters
229 * and loading the server certificate.
232 crypto_init (bool tls_set_on_cli
)
237 err
= gnutls_global_init ();
239 print_gnutls_error (err
, "initializing GnuTLS");
243 if (tls
== 0) /* --tls=off */
246 /* --tls-psk overrides certificates. */
247 if (tls_psk
!= NULL
) {
248 what
= "Pre-Shared Keys (PSK)";
251 crypto_auth
= CRYPTO_AUTH_PSK
;
254 what
= "X.509 certificates";
255 r
= start_certificates ();
257 crypto_auth
= CRYPTO_AUTH_CERTIFICATES
;
261 debug ("TLS enabled using: %s", what
);
265 /* If we get here, we didn't manage to load the PSK file /
266 * certificates. If --tls=require was given on the command line
267 * then that's a problem.
269 if (tls
== 2) { /* --tls=require */
271 "%s: --tls=require but could not load TLS certificates.\n"
272 "Try setting ‘--tls-certificates=/path/to/certificates’ or read\n"
273 "the \"TLS\" section in nbdkit(1).\n",
278 /* If --tls=on was given on the command line, warn before we turn
281 if (tls
== 1 && tls_set_on_cli
) { /* explicit --tls=on */
283 "%s: warning: --tls=on but could not load TLS certificates.\n"
284 "TLS will be disabled and TLS connections will be rejected.\n"
285 "Try setting ‘--tls-certificates=/path/to/certificates’ or read\n"
286 "the \"TLS\" section in nbdkit(1).\n",
291 debug ("TLS disabled: could not load TLS certificates");
298 switch (crypto_auth
) {
299 case CRYPTO_AUTH_CERTIFICATES
:
300 gnutls_certificate_free_credentials (x509_creds
);
302 case CRYPTO_AUTH_PSK
:
303 gnutls_psk_free_server_credentials (psk_creds
);
308 gnutls_global_deinit ();
311 /* Read buffer from GnuTLS and either succeed completely
312 * (returns > 0), read an EOF (returns 0), or fail (returns -1).
315 crypto_recv (struct connection
*conn
, void *vbuf
, size_t len
)
317 gnutls_session_t session
= conn
->crypto_session
;
320 bool first_read
= true;
322 assert (session
!= NULL
);
325 r
= gnutls_record_recv (session
, buf
, len
);
327 if (r
== GNUTLS_E_INTERRUPTED
|| r
== GNUTLS_E_AGAIN
)
329 nbdkit_error ("gnutls_record_recv: %s", gnutls_strerror (r
));
336 /* Partial record read. This is an error. */
348 /* If this send()'s length is so large that it is going to require
349 * multiple TCP segments anyway, there's no need to try and merge it
350 * with any corked data from a previous send that used SEND_MORE.
352 #define MAX_SEND_MORE_LEN (64 * 1024)
354 /* Write buffer to GnuTLS and either succeed completely
355 * (returns 0) or fail (returns -1). flags is ignored for now.
358 crypto_send (struct connection
*conn
, const void *vbuf
, size_t len
, int flags
)
360 gnutls_session_t session
= conn
->crypto_session
;
361 const char *buf
= vbuf
;
364 assert (session
!= NULL
);
366 if (len
+ gnutls_record_check_corked (session
) > MAX_SEND_MORE_LEN
) {
367 if (gnutls_record_uncork (session
, GNUTLS_RECORD_WAIT
) < 0)
370 else if (flags
& SEND_MORE
)
371 gnutls_record_cork (session
);
374 r
= gnutls_record_send (session
, buf
, len
);
376 if (r
== GNUTLS_E_INTERRUPTED
|| r
== GNUTLS_E_AGAIN
)
384 if (!(flags
& SEND_MORE
) &&
385 gnutls_record_uncork (session
, GNUTLS_RECORD_WAIT
) < 0)
391 /* There's no place in the NBD protocol to send back errors from
392 * close, so this function ignores errors.
395 crypto_close (struct connection
*conn
)
397 gnutls_session_t session
= conn
->crypto_session
;
400 assert (session
!= NULL
);
402 gnutls_transport_get_int2 (session
, &sockin
, &sockout
);
404 gnutls_bye (session
, GNUTLS_SHUT_RDWR
);
408 if (sockout
>= 0 && sockin
!= sockout
)
411 gnutls_deinit (session
);
412 conn
->crypto_session
= NULL
;
415 /* Upgrade an existing connection to TLS. Also this should do access
416 * control if enabled. The protocol code ensures this function can
417 * only be called once per connection.
420 crypto_negotiate_tls (struct connection
*conn
, int sockin
, int sockout
)
422 gnutls_session_t session
;
423 CLEANUP_FREE
char *priority
= NULL
;
426 /* Create the GnuTLS session. */
427 err
= gnutls_init (&session
, GNUTLS_SERVER
);
429 nbdkit_error ("gnutls_init: %s", gnutls_strerror (err
));
434 switch (crypto_auth
) {
435 case CRYPTO_AUTH_CERTIFICATES
:
436 /* Associate the session with the server credentials (key, cert). */
437 err
= gnutls_credentials_set (session
, GNUTLS_CRD_CERTIFICATE
,
440 nbdkit_error ("gnutls_credentials_set: %s", gnutls_strerror (err
));
444 /* If verify peer is enabled, tell GnuTLS to request the client
445 * certificates. (Note the default is to not request or verify
448 if (tls_verify_peer
) {
449 #ifdef HAVE_GNUTLS_SESSION_SET_VERIFY_CERT
450 gnutls_certificate_server_set_request (session
, GNUTLS_CERT_REQUEST
);
451 gnutls_session_set_verify_cert (session
, NULL
, 0);
453 nbdkit_error ("--tls-verify-peer: "
454 "GnuTLS >= 3.4.6 is required for this feature");
459 priority
= strdup (TLS_PRIORITY
);
460 if (priority
== NULL
) {
461 nbdkit_error ("strdup: %m");
466 case CRYPTO_AUTH_PSK
:
467 /* Associate the session with the server PSK credentials. */
468 err
= gnutls_credentials_set (session
, GNUTLS_CRD_PSK
, psk_creds
);
470 nbdkit_error ("gnutls_credentials_set: %s", gnutls_strerror (err
));
474 if (asprintf (&priority
,
475 "%s:+ECDHE-PSK:+DHE-PSK:+PSK", TLS_PRIORITY
) == -1) {
476 nbdkit_error ("asprintf: %m");
485 assert (priority
!= NULL
);
486 err
= gnutls_priority_set_direct (session
, priority
, NULL
);
488 nbdkit_error ("failed to set TLS session priority to %s: %s",
489 priority
, gnutls_strerror (err
));
493 /* Set up GnuTLS so it reads and writes on the raw sockets. */
494 gnutls_transport_set_int2 (session
, sockin
, sockout
);
496 /* Perform the handshake. */
497 debug ("starting TLS handshake");
498 gnutls_handshake_set_timeout (session
,
499 GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT
);
502 err
= gnutls_handshake (session
);
503 } while (err
< 0 && gnutls_error_is_fatal (err
) == 0);
505 gnutls_handshake_description_t in
, out
;
507 /* Get some additional debug information about where in the
508 * handshake protocol it failed. You have to look up these codes in
511 in
= gnutls_handshake_get_last_in (session
);
512 out
= gnutls_handshake_get_last_out (session
);
513 nbdkit_error ("gnutls_handshake: %s (%d/%d)",
514 gnutls_strerror (err
), (int) in
, (int) out
);
517 debug ("TLS handshake completed");
519 /* Set up the connection recv/send/close functions so they call
520 * GnuTLS wrappers instead.
522 conn
->crypto_session
= session
;
523 conn
->recv
= crypto_recv
;
524 conn
->send
= crypto_send
;
525 conn
->close
= crypto_close
;
529 gnutls_deinit (session
);
533 #else /* !HAVE_GNUTLS */
535 /* GnuTLS was not available at compile time. These are stub versions
536 * of the above functions which either do nothing or report errors as
541 crypto_init (bool tls_set_on_cli
)
545 "%s: TLS cannot be enabled because "
546 "this binary was compiled without GnuTLS.\n",
552 debug ("TLS disabled: nbdkit was not compiled with GnuTLS support");
562 crypto_negotiate_tls (struct connection
*conn
, int sockin
, int sockout
)
564 /* Should never be called because tls == 0. */
568 #endif /* !HAVE_GNUTLS */