diff options
Diffstat (limited to 'lib/tls.c')
-rw-r--r-- | lib/tls.c | 372 |
1 files changed, 0 insertions, 372 deletions
diff --git a/lib/tls.c b/lib/tls.c deleted file mode 100644 index ba3cab5..0000000 --- a/lib/tls.c +++ /dev/null @@ -1,372 +0,0 @@ -/* Copyright 2010-2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ - -#if defined HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdlib.h> -#include <unistd.h> -#include <assert.h> -#include <fcntl.h> -#include <limits.h> -#if defined HAVE_PTHREAD_H -#include <pthread.h> -#endif -#include <openssl/ssl.h> -#include <openssl/err.h> -#include <openssl/bn.h> -#include <openssl/x509v3.h> -#include <openssl/rand.h> -#include <openssl/crypto.h> -#include <radsec/radsec.h> -#include <radsec/radsec-impl.h> - -#include <regex.h> -#include "radsecproxy/list.h" -#include "radsecproxy/radsecproxy.h" - -#include "tls.h" - -static struct tls * -_get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm) -{ - struct tls *c = rs_malloc (conn->ctx, sizeof (struct tls)); - - if (c) - { - memset (c, 0, sizeof (struct tls)); - /* TODO: Make sure old radsecproxy code doesn't free these all - of a sudden, or strdup them. */ - c->name = realm->name; - c->cacertfile = realm->cacertfile; - c->cacertpath = NULL; /* NYI */ - c->certfile = realm->certfile; - c->certkeyfile = realm->certkeyfile; - c->certkeypwd = NULL; /* NYI */ - c->cacheexpiry = 0; /* NYI */ - c->crlcheck = 0; /* NYI */ - c->policyoids = (char **) NULL; /* NYI */ - } - else - rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL); - - return c; -} - -#if defined RS_ENABLE_TLS_PSK -static unsigned int -psk_client_cb (SSL *ssl, - const char *hint, - char *identity, - unsigned int max_identity_len, - unsigned char *psk, - unsigned int max_psk_len) -{ - struct rs_connection *conn = NULL; - struct rs_credentials *cred = NULL; - - conn = SSL_get_ex_data (ssl, 0); - assert (conn != NULL); - cred = conn->active_peer->realm->transport_cred; - assert (cred != NULL); - /* NOTE: Ignoring identity hint from server. */ - - if (strlen (cred->identity) + 1 > max_identity_len) - { - rs_err_conn_push (conn, RSE_CRED, "PSK identity longer than max %d", - max_identity_len - 1); - return 0; - } - strcpy (identity, cred->identity); - - switch (cred->secret_encoding) - { - case RS_KEY_ENCODING_UTF8: - cred->secret_len = strlen (cred->secret); - if (cred->secret_len > max_psk_len) - { - rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d", - max_psk_len); - return 0; - } - memcpy (psk, cred->secret, cred->secret_len); - break; - case RS_KEY_ENCODING_ASCII_HEX: - { - BIGNUM *bn = NULL; - - if (BN_hex2bn (&bn, cred->secret) == 0) - { - rs_err_conn_push (conn, RSE_CRED, "Unable to convert pskhexstr"); - if (bn != NULL) - BN_clear_free (bn); - return 0; - } - if ((unsigned int) BN_num_bytes (bn) > max_psk_len) - { - rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d", - max_psk_len); - BN_clear_free (bn); - return 0; - } - cred->secret_len = BN_bn2bin (bn, psk); - BN_clear_free (bn); - } - break; - default: - assert (!"unknown psk encoding"); - } - - return cred->secret_len; -} -#endif /* RS_ENABLE_TLS_PSK */ - -/** Read \a buf_len bytes from one of the random devices into \a - buf. Return 0 on success and -1 on failure. */ -static int -load_rand_ (uint8_t *buf, size_t buf_len) -{ - static const char *fns[] = {"/dev/urandom", "/dev/random", NULL}; - int i; - - if (buf_len > SSIZE_MAX) - return -1; - - for (i = 0; fns[i] != NULL; i++) - { - size_t nread = 0; - int fd = open (fns[i], O_RDONLY); - if (fd < 0) - continue; - while (nread != buf_len) - { - ssize_t r = read (fd, buf + nread, buf_len - nread); - if (r < 0) - return -1; - if (r == 0) - break; - nread += r; - } - close (fd); - if (nread != buf_len) - return -1; - return 0; - } - return -1; -} - -/** Initialise OpenSSL's PRNG by possibly invoking RAND_poll() and by - feeding RAND_seed() data from one of the random devices. If either - succeeds, we're happy and return 0. */ -static int -init_openssl_rand_ (void) -{ - long openssl_version = 0; - int openssl_random_init_flag = 0; - int our_random_init_flag = 0; - uint8_t buf[32]; - - /* Older OpenSSL has a crash bug in RAND_poll (when a file it opens - gets a file descriptor with a number higher than FD_SETSIZE) so - use it only for newer versions. */ - openssl_version = SSLeay (); - if (openssl_version >= OPENSSL_V (0,9,8,'c')) - openssl_random_init_flag = RAND_poll (); - - our_random_init_flag = !load_rand_ (buf, sizeof(buf)); - if (our_random_init_flag) - RAND_seed (buf, sizeof(buf)); - memset (buf, 0, sizeof(buf)); /* FIXME: What if memset() is optimised out? */ - - if (!openssl_random_init_flag && !our_random_init_flag) - return -1; - if (!RAND_bytes (buf, sizeof(buf))) - return -1; - return 0; -} - -#if defined HAVE_PTHREADS -/** Array of pthread_mutex_t for OpenSSL. Allocated and initialised in - \a init_locking_ and never freed. */ -static pthread_mutex_t *s_openssl_mutexes = NULL; -/** Number of pthread_mutex_t's allocated at s_openssl_mutexes. */ -static int s_openssl_mutexes_count = 0; - -/** Callback for OpenSSL when a lock is to be held or released. */ -static void -openssl_locking_cb_ (int mode, int i, const char *file, int line) -{ - if (s_openssl_mutexes == NULL || i >= s_openssl_mutexes_count) - return; - if (mode & CRYPTO_LOCK) - pthread_mutex_lock (&s_openssl_mutexes[i]); - else - pthread_mutex_unlock (&s_openssl_mutexes[i]); -} - -/** Initialise any locking needed for being thread safe. Libradsec has - all its own state in one or more struct rs_context and doesn't - need locks but libraries used by libradsec may need protection. */ -static int -init_locking_ () -{ - int i, n; - n = CRYPTO_num_locks (); - - s_openssl_mutexes = calloc (n, sizeof(pthread_mutex_t)); - if (s_openssl_mutexes == NULL) - return -RSE_NOMEM; - for (i = 0; i < n; i++) - pthread_mutex_init (&s_openssl_mutexes[i], NULL); - s_openssl_mutexes_count = n; - - return 0; -} -#endif /* HAVE_PTHREADS */ - -/** Initialise the TLS library. Return 0 on success, -1 on failure. */ -int -tls_init () -{ - SSL_load_error_strings (); -#if defined HAVE_PTHREADS - if (CRYPTO_get_locking_callback () == NULL) - { - assert (s_openssl_mutexes_count == 0); - /* Allocate and initialise mutexes. We will never free - these. FIXME: Is there a portable way of having a function - invoked when a solib is unloaded? -ln */ - if (init_locking_ ()) - return -1; - CRYPTO_set_locking_callback (openssl_locking_cb_); - } -#endif /* HAVE_PTHREADS */ - SSL_library_init (); - return init_openssl_rand_ (); -} - -int -tls_init_conn (struct rs_connection *conn) -{ - struct rs_context *ctx = NULL; - struct tls *tlsconf = NULL; - SSL_CTX *ssl_ctx = NULL; - SSL *ssl = NULL; - unsigned long sslerr = 0; - - assert (conn->ctx); - ctx = conn->ctx; - - tlsconf = _get_tlsconf (conn, conn->active_peer->realm); - if (!tlsconf) - return -1; - ssl_ctx = tlsgetctx (RAD_TLS, tlsconf); - if (!ssl_ctx) - { - for (sslerr = ERR_get_error (); sslerr; sslerr = ERR_get_error ()) - rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__, - ERR_error_string (sslerr, NULL)); - return -1; - } - ssl = SSL_new (ssl_ctx); - if (!ssl) - { - for (sslerr = ERR_get_error (); sslerr; sslerr = ERR_get_error ()) - rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__, - ERR_error_string (sslerr, NULL)); - return -1; - } - -#if defined RS_ENABLE_TLS_PSK - if (conn->active_peer->realm->transport_cred != NULL) - { - SSL_set_psk_client_callback (ssl, psk_client_cb); - SSL_set_ex_data (ssl, 0, conn); - } -#endif /* RS_ENABLE_TLS_PSK */ - - conn->tls_ctx = ssl_ctx; - conn->tls_ssl = ssl; - rs_free (ctx, tlsconf); - return RSE_OK; -} - -/* draft-ietf-radext-radsec-11.txt - - * Certificate validation MUST include the verification rules as - per [RFC5280]. - - * Implementations SHOULD indicate their acceptable Certification - Authorities as per section 7.4.4 (server side) and x.y.z - ["Trusted CA Indication"] (client side) of [RFC5246] (see - Section 3.2) - - * Implementations SHOULD allow to configure a list of acceptable - certificates, identified via certificate fingerprint. When a - fingerprint configured, the fingerprint is prepended with an - ASCII label identifying the hash function followed by a colon. - Implementations MUST support SHA-1 as the hash algorithm and - use the ASCII label "sha-1" to identify the SHA-1 algorithm. - The length of a SHA-1 hash is 20 bytes and the length of the - corresponding fingerprint string is 65 characters. An example - certificate fingerprint is: sha- - 1:E1:2D:53:2B:7C:6B:8A:29:A2:76:C8:64:36:0B:08:4B:7A:F1:9E:9D - - * Peer validation always includes a check on whether the locally - configured expected DNS name or IP address of the server that - is contacted matches its presented certificate. DNS names and - IP addresses can be contained in the Common Name (CN) or - subjectAltName entries. For verification, only one of these - entries is to be considered. The following precedence - applies: for DNS name validation, subjectAltName:DNS has - precedence over CN; for IP address validation, subjectAltName: - iPAddr has precedence over CN. - - * Implementations SHOULD allow to configure a set of acceptable - values for subjectAltName:URI. - */ -int -tls_verify_cert (struct rs_connection *conn) -{ - int err = 0; - int success = 0; - X509 *peer_cert = NULL; - struct in6_addr addr; - const char *hostname = NULL; - - assert (conn->active_peer->conn == conn); - assert (conn->active_peer->hostname != NULL); - hostname = conn->active_peer->hostname; - - /* verifytlscert() performs basic verification as described by - OpenSSL VERIFY(1), i.e. verification of the certificate chain. */ - peer_cert = verifytlscert (conn->tls_ssl); - if (peer_cert == NULL) - { - err = rs_err_conn_push (conn, RSE_SSLERR, - "basic certificate validation failed"); - goto out; - } - - if (inet_pton (AF_INET, hostname, &addr)) - success = (subjectaltnameaddr (peer_cert, AF_INET, &addr) == 1); - else if (inet_pton (AF_INET6, hostname, &addr)) - success = (subjectaltnameaddr (peer_cert, AF_INET6, &addr) == 1); - else - success = (subjectaltnameregexp (peer_cert, GEN_DNS, hostname, NULL) == 1); - - if (!success) - success = (cnregexp (peer_cert, hostname, NULL) == 1); - - if (conn->realm->disable_hostname_check) - success = 1; - if (!success) - err = rs_err_conn_push (conn, RSE_CERT, "server certificate doesn't " - "match configured hostname \"%s\"", hostname); - - out: - if (peer_cert != NULL) - X509_free (peer_cert); - return err; -} |