-This is not radsecproxy, it's libradsec (see lib/) sharing the
-radsecproxy repository.
+This is libradsec, not radsecproxy, sharing repository with
+radsecproxy.
For radsecproxy, see branch 'master'.
-The repository ca be found at http://git.nordu.net/?p=radsecproxy.git;a=summary .
+The source code can be found at
+http://git.nordu.net/?p=radsecproxy.git;a=summary .
--- /dev/null
+Changes between 0.0.4 and 0.0.5
+
+ - When POSIX thread support is detected at configure and build time
+ libradsec will be more safe to use by programs that call it from
+ more than one thread simultaneously.
+
+ - The initialisation of the OpenSSL PRNG has been improved.
+
+User visible changes between 0.0.1 and 0.0.4
+
+ - TLS support is now enabled by default. Use --disable-tls to
+ disable it.
+
+ - Support for TLS-PSK has been added (--enable-tls-psk).
+
+ - The RADIUS dictionaries are now compiled into the library and are
+ no longer read from disk.
HACKING file for libradsec (in Emacs -*- org -*- mode).
-Status as of libradsec-0.0.4.dev (2013-05-06).
+Status as of libradsec-0.0.5 (2014-02-03).
* Build instructions
sh autogen.sh
-./configure #--enable-tls
+./configure
make
examples/client -r examples/client.conf blocking-tls; echo $?
- Application chooses allocation regime.
-Note that as of 0.0.2.dev libradsec suffers from way too much focus on
+Note that as of 0.0.4 libradsec suffers from way too much focus on
the behaviour of a blocking client and is totally useless as a server.
Not only does it lack most of the functions needed for writing a
server but it also contains at least one architectural mishap which
SUBDIRS = radius radsecproxy include . examples
DIST_SUBDIRS = $(SUBDIRS) tests
-INCLUDES = -I$(srcdir)/include
+AM_CPPFLAGS = -I$(srcdir)/include
AM_CFLAGS = -Wall -Werror -g
lib_LTLIBRARIES = libradsec.la
udp.h \
util.h
-EXTRA_DIST = HACKING LICENSE libradsec.spec radsec.sym
+EXTRA_DIST = CHANGES HACKING LICENSE libradsec.spec radsec.sym
EXTRA_libradsec_la_DEPENDENCIES = radsec.sym
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-tls --enable-tls-psk
Libradsec depends on
- libconfuse
- libevent2
-- openssl (if configured with --enable-tls)
+- openssl (unless configured with --disable-tls)
To compile the library and the examples, do something like
./configure --help
-for the full list. Worth mentioning here is --enable-tls and
---enable-tls-psk.
+for the full list. Worth mentioning here is --enable-tls-psk.
If the preprocessor has a hard time finding some of the header files
are, try setting environment variable CPPFLAGS at configure
pskhexstr = STRING # Transport pre-shared key, ASCII hex form.
pskid = STRING
pskex = "PSK"|"DHE_PSK"|"RSA_PSK"
- disable_hostname_check = yes|no
+ disable_hostname_check = "yes"|"no"
}
# client specific realm config options
# -*- Autoconf -*- script for libradsec.
AC_PREREQ([2.63])
-AC_INIT([libradsec], [0.0.4.dev], [linus+libradsec@nordu.net])
+AC_INIT([libradsec], [0.0.5], [linus+libradsec@nordu.net])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([radsec.c])
AC_CONFIG_AUX_DIR([build-aux])
AC_MSG_ERROR([required library libconfuse not found]))
AC_CHECK_LIB([event_core], [event_get_version],,
AC_MSG_ERROR([required library libevent_core not found]))
+AH_TEMPLATE([HAVE_PTHREADS], [POSIX threads are available on this system])
+AC_SEARCH_LIBS([pthread_create], [pthread], AC_DEFINE([HAVE_PTHREADS]))
# Enable-knobs.
-## Enable TLS (RadSec).
+## Enable TLS (RadSec), default on.
+want_tls=yes
AH_TEMPLATE([RS_ENABLE_TLS], [TLS (RadSec) enabled])
-AH_TEMPLATE([RADPROT_TLS], []) dnl Legacy.
-AC_ARG_ENABLE([tls], AS_HELP_STRING([--enable-tls], [enable TLS (RadSec)]),
- [AC_CHECK_LIB([event_openssl], [bufferevent_openssl_socket_new],,
- AC_MSG_ERROR([required library event_openssl not found]))
- AC_DEFINE([RS_ENABLE_TLS])
- AC_DEFINE([RADPROT_TLS])]) dnl Legacy.
-AM_CONDITIONAL([RS_ENABLE_TLS], [test "${enable_tls+set}" = set])
-### Define WITHOUT_OPENSSL for radius/client.h.
-if test -z "$enable_tls"; then
+AH_TEMPLATE([RADPROT_TLS], [])
+AC_ARG_ENABLE([tls],
+ AS_HELP_STRING([--disable-tls], [disable TLS (RadSec)]),
+ [want_tls=$enableval])
+AM_CONDITIONAL([RS_ENABLE_TLS], [test $want_tls = yes])
+if test $want_tls = yes; then
+ AC_CHECK_LIB([event_openssl], [bufferevent_openssl_socket_new],,
+ AC_MSG_ERROR([required library event_openssl not found]))
+ AC_DEFINE([RS_ENABLE_TLS])
+ AC_DEFINE([RADPROT_TLS])
+else
+ # Define WITHOUT_OPENSSL for radius/client.h.
CPPFLAGS="$CPPFLAGS -DWITHOUT_OPENSSL"
fi
## Enable TLS-PSK (preshared keys).
# Checks for header files.
AC_CHECK_HEADERS(
[sys/time.h time.h netdb.h netinet/in.h stdint.h stdlib.h strings.h string.h \
- sys/socket.h unistd.h syslog.h sys/select.h fcntl.h arpa/inet.h])
+ sys/socket.h unistd.h syslog.h sys/select.h fcntl.h arpa/inet.h pthread.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
evutil_gai_strerror (err));
rs_debug (("%s: event loop done\n", __func__));
- if ((pkt->flags & RS_PACKET_RECEIVED) == 0
- || (req_msg
- && packet_verify_response (pkt->conn, pkt, req_msg) != RSE_OK))
+ if ((pkt->flags & RS_PACKET_RECEIVED) != 0)
{
- if (rs_err_conn_peek_code (pkt->conn) == RSE_OK)
- /* No packet and no error on the stack _should_ mean that the
- server hung up on us. */
- rs_err_conn_push (pkt->conn, RSE_DISCO, "no response");
- return rs_err_conn_peek_code (conn);
+ /* If the caller passed a request, check the response. */
+ if (req_msg)
+ err = packet_verify_response (pkt->conn, pkt, req_msg);
+
+ /* If the response was OK and the caller wants it, hand it
+ over, else free it. */
+ if (err == RSE_OK && pkt_out)
+ *pkt_out = pkt;
+ else
+ rs_packet_destroy (pkt);
}
+ else
+ err = rs_err_conn_peek_code (pkt->conn);
- if (pkt_out)
- *pkt_out = pkt;
- return RSE_OK;
+ return err;
}
void
rs_debug (("%s: retransmission timeout on %p (fd %d) sending to %p\n",
__func__, conn, conn->fd, conn->active_peer));
rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
+
+ /* Disable/delete read and write events. Timing out on reading
+ might f.ex. trigger resending of a message. It'd be
+ surprising to end up reading without having enabled/created a
+ read event in that case. */
+ if (conn->bev) /* TCP. */
+ bufferevent_disable (conn->bev, EV_WRITE|EV_READ);
+ else /* UDP. */
+ {
+ if (conn->wev)
+ event_del (conn->wev);
+ if (conn->rev)
+ event_del (conn->rev);
+ }
+
event_loopbreak (conn);
}
}
#if defined (RS_ENABLE_TLS)
else if (conn->realm->type == RS_CONN_TYPE_TLS)
{
- if (rs_tls_init (conn))
+ if (tls_init_conn (conn))
return -1;
/* Would be convenient to pass BEV_OPT_CLOSE_ON_FREE but things
seem to break when be_openssl_ctrl() (in libevent) calls
AUTOMAKE_OPTIONS = foreign
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)
AM_CFLAGS = -Wall -Werror -g
noinst_PROGRAMS = client
r = rs_context_create (&h);
if (r)
{
- assert(r == RSE_NOMEM);
- assert (!"out of RAM -- unable to create libradsec context");
+ assert (!"unable to create libradsec context");
}
#if !defined (USE_CONFIG_FILE)
that the context must not be freed before all other libradsec
objects have been freed.
+ If support for POSIX threads was detected at configure and build
+ time \a rs_context_create will use mutexes to protect multiple
+ threads from stomping on each other in OpenSSL.
+
\a ctx Address of pointer to a struct rs_context. This is the
output of this function.
- \return RSE_OK (0) on success or RSE_NOMEM on out of memory. */
+ \return RSE_OK (0) on success, RSE_SSLERR on TLS library
+ initialisation error and RSE_NOMEM on out of memory. */
int rs_context_create(struct rs_context **ctx);
/** Free a context. Note that the context must not be freed before
ACLOCAL_AMFLAGS = -I m4
BUILT_SOURCES = dictionaries.c
-
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)
AM_CFLAGS = -Wall -g
noinst_LTLIBRARIES = libradsec-radius.la
#include "debug.h"
#include "radsecproxy/debug.h"
#if defined (RS_ENABLE_TLS)
+#include "tls.h"
#include <regex.h>
#include "radsecproxy/list.h"
#include "radsecproxy/radsecproxy.h"
{
struct rs_context *h;
+#if defined (RS_ENABLE_TLS)
+ if (tls_init ())
+ return RSE_SSLERR;
+#endif
+
h = calloc (1, sizeof(*h));
if (h == NULL)
return RSE_NOMEM;
-#if defined (RS_ENABLE_TLS)
- ssl_init ();
-#endif
-
debug_init ("libradsec"); /* radsecproxy compat, FIXME: remove */
if (ctx != NULL)
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)
AM_CFLAGS = -Wall -Werror -g
noinst_LTLIBRARIES = libradsec-radsecproxy.la
#include "hostport_types.h"
#include "radsecproxy.h"
-static struct hash *tlsconfs = NULL;
-
-void ssl_init(void) {
- time_t t;
- pid_t pid;
-
- SSL_load_error_strings();
- SSL_library_init();
-
- while (!RAND_status()) {
- t = time(NULL);
- pid = getpid();
- RAND_seed((unsigned char *)&t, sizeof(time_t));
- RAND_seed((unsigned char *)&pid, sizeof(pid));
- }
-}
-
static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) {
int pwdlen = strlen(userdata);
if (rwflag != 0 || pwdlen > size) /* not for decryption or too large */
return ctx;
}
-struct tls *tlsgettls(char *alt1, char *alt2) {
- struct tls *t;
-
- t = hash_read(tlsconfs, alt1, strlen(alt1));
- if (!t)
- t = hash_read(tlsconfs, alt2, strlen(alt2));
- return t;
-}
-
SSL_CTX *tlsgetctx(uint8_t type, struct tls *t) {
struct timeval now;
return 0;
}
-/* this is a bit sloppy, should not always accept match to any */
-int certnamecheck(X509 *cert, struct list *hostports) {
- struct list_node *entry;
- struct hostportres *hp;
- int r;
- uint8_t type = 0; /* 0 for DNS, AF_INET for IPv4, AF_INET6 for IPv6 */
- struct in6_addr addr;
-
- for (entry = list_first(hostports); entry; entry = list_next(entry)) {
- hp = (struct hostportres *)entry->data;
- if (hp->prefixlen != 255) {
- /* we disable the check for prefixes */
- return 1;
- }
- if (inet_pton(AF_INET, hp->host, &addr))
- type = AF_INET;
- else if (inet_pton(AF_INET6, hp->host, &addr))
- type = AF_INET6;
- else
- type = 0;
-
- r = type ? subjectaltnameaddr(cert, type, &addr) : subjectaltnameregexp(cert, GEN_DNS, hp->host, NULL);
- if (r) {
- if (r > 0) {
- debug(DBG_DBG, "certnamecheck: Found subjectaltname matching %s %s", type ? "address" : "host", hp->host);
- return 1;
- }
- debug(DBG_WARN, "certnamecheck: No subjectaltname matching %s %s", type ? "address" : "host", hp->host);
- } else {
- if (cnregexp(cert, hp->host, NULL)) {
- debug(DBG_DBG, "certnamecheck: Found cn matching host %s", hp->host);
- return 1;
- }
- debug(DBG_WARN, "certnamecheck: cn not matching host %s", hp->host);
- }
- }
- return 0;
-}
-
-int verifyconfcert(X509 *cert, struct clsrvconf *conf) {
- if (conf->certnamecheck) {
- if (!certnamecheck(cert, conf->hostports)) {
- debug(DBG_WARN, "verifyconfcert: certificate name check failed");
- return 0;
- }
- debug(DBG_WARN, "verifyconfcert: certificate name check ok");
- }
- if (conf->certcnregex) {
- if (cnregexp(cert, NULL, conf->certcnregex) < 1) {
- debug(DBG_WARN, "verifyconfcert: CN not matching regex");
- return 0;
- }
- debug(DBG_DBG, "verifyconfcert: CN matching regex");
- }
- if (conf->certuriregex) {
- if (subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex) < 1) {
- debug(DBG_WARN, "verifyconfcert: subjectaltname URI not matching regex");
- return 0;
- }
- debug(DBG_DBG, "verifyconfcert: subjectaltname URI matching regex");
- }
- return 1;
-}
-
/* Local Variables: */
/* c-file-style: "stroustrup" */
/* End: */
};
#if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
-void ssl_init();
-struct tls *tlsgettls(char *alt1, char *alt2);
SSL_CTX *tlsgetctx(uint8_t type, struct tls *t);
X509 *verifytlscert(SSL *ssl);
int subjectaltnameaddr(X509 *cert, int family, const struct in6_addr *addr);
int subjectaltnameregexp(X509 *cert, int type, const char *exact, const regex_t *regex);
int cnregexp(X509 *cert, const char *exact, const regex_t *regex);
-int verifyconfcert(X509 *cert, struct clsrvconf *conf);
#endif
#if defined (__cplusplus)
resp_msg);
if (r == RSE_OK)
break; /* Success. */
-
- if (r != RSE_TIMEOUT_CONN && r != RSE_TIMEOUT_IO)
- break; /* Error. */
}
- else if (r != RSE_TIMEOUT_CONN && r != RSE_TIMEOUT_IO)
+ if (r != RSE_TIMEOUT_CONN && r != RSE_TIMEOUT_IO)
break; /* Error. */
+ /* Timing out reading or writing. Pop the timeout error from the
+ stack and continue the loop. */
+ rs_err_conn_pop (request->conn);
+
gettimeofday (&now, NULL);
if (++count > MRC || timercmp (&now, &end, >))
{
- r = RSE_TIMEOUT;
+ r = rs_err_conn_push_fl (request->conn, RSE_TIMEOUT,
+ __FILE__, __LINE__, NULL);
break; /* Timeout. */
}
AUTOMAKE_OPTIONS = foreign
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)
AM_CFLAGS = -Wall -Werror -g
TESTS = test-udp
#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 "radsecproxy/list.h"
#include "radsecproxy/radsecproxy.h"
+#include "tls.h"
+
static struct tls *
_get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm)
{
}
#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
-rs_tls_init (struct rs_connection *conn)
+tls_init_conn (struct rs_connection *conn)
{
struct rs_context *ctx = NULL;
struct tls *tlsconf = NULL;
extern "C" {
#endif
-int rs_tls_init (struct rs_connection *conn);
+int tls_init (void);
+int tls_init_conn (struct rs_connection *conn);
int tls_verify_cert (struct rs_connection *conn);
+#define OPENSSL_VER(a,b,c,d,e) \
+ (((a)<<28) | \
+ ((b)<<20) | \
+ ((c)<<12) | \
+ ((d)<< 4) | \
+ (e))
+#define OPENSSL_V(a,b,c,d) \
+ OPENSSL_VER((a),(b),(c),(d)-'a'+1,0xf)
+
#if defined (__cplusplus)
}
#endif
{
/* FIXME: Really shouldn't happen since we've been told
that fd is readable! */
- rs_debug (("%s: EAGAIN reading UDP packet -- wot?"));
+ rs_debug (("%s: EAGAIN reading UDP packet -- wot?\n"));
goto err_out;
}
Don't touch it afterwards -- it might have been freed. */
if (pkt->conn->callbacks.received_cb)
pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data);
+ else
+ rs_debug (("%s: no received-callback -- dropping packet\n", __func__));
}
else if (what & EV_WRITE)
{