summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/HACKING12
-rw-r--r--lib/Makefile.am19
-rw-r--r--lib/conf.c91
-rw-r--r--lib/configure.ac12
-rw-r--r--lib/conn.c9
-rw-r--r--lib/err.c2
-rw-r--r--lib/event.c43
-rw-r--r--lib/event.h2
-rw-r--r--lib/examples/client-blocking.c24
-rw-r--r--lib/examples/client.conf4
-rw-r--r--lib/include/radsec/radsec-impl.h27
-rw-r--r--lib/include/radsec/radsec.h6
-rw-r--r--lib/packet.c4
-rw-r--r--lib/peer.c16
-rw-r--r--lib/radsec.c16
-rw-r--r--lib/radsec.h7
-rw-r--r--lib/rsp_tlscommon.c136
-rw-r--r--lib/rsp_tlscommon.h6
-rw-r--r--lib/tcp.c22
-rw-r--r--lib/tests/Makefile.am4
-rw-r--r--lib/tests/README30
-rw-r--r--lib/tests/test-udp.c28
-rw-r--r--lib/tests/test.conf8
-rw-r--r--lib/tls.c157
-rw-r--r--lib/tls.h1
-rw-r--r--lib/util.c19
-rw-r--r--lib/util.h4
27 files changed, 495 insertions, 214 deletions
diff --git a/lib/HACKING b/lib/HACKING
index 1494941..824cb77 100644
--- a/lib/HACKING
+++ b/lib/HACKING
@@ -20,6 +20,14 @@ examples/client -r examples/client.conf blocking-tls; echo $?
(a.k.a. on-your-own mode)
- User chooses allocation regime
+Note that as of 0.0.2.dev 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
+kills the server idea. A connection timeout (TCP) or a retransmit
+timeout (UDP) will result in the event loop being broken. The same is
+thing will happen if there's an error on a TCP connection, f.ex. a
+failing certificate validation (TLS).
* Dependencies
Details apply to Ubuntu 10.10.
@@ -38,13 +46,13 @@ Details apply to Ubuntu 10.10.
- [TCP] short read
- [TCP] short write
- [TLS] basic tls support
+- [TLS] preshared key support
+- [TLS] verification of CN
** Known issues
- error stack is only one entry deep
- custom allocation scheme is not used in all places
** Not implemented
- server failover
-- [TLS] verification of CN
-- [TLS] preshared key support
- [DTLS] support
* Found a bug?
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 51aebf9..9a74a7f 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,6 +1,22 @@
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
+# Shared library interface version, i.e. -version-info to Libtool,
+# expressed as three integers CURRENT:REVISION:AGE.
+
+# CURRENT is the version number of the current interface. Increment
+# CURRENT when the library interface changes.
+
+# REVISION is the version number of the _implementation_ of the
+# CURRENT interface. Set REVISION to 0 when CURRENT changes,
+# else increment.
+
+# AGE is the number of interfaces this library implements, i.e. how
+# many versions before CURRENT that are supported. Increment AGE
+# when the library interface is _extended_. Set AGE to 0 when the
+# library interface is _changed_.
+
+
SUBDIRS = radius . include examples
INCLUDES = -I$(srcdir)/include
@@ -22,7 +38,8 @@ libradsec_la_SOURCES = \
request.c \
send.c \
tcp.c \
- udp.c
+ udp.c \
+ util.c
libradsec_la_SOURCES += \
rsp_debug.c \
diff --git a/lib/conf.c b/lib/conf.c
index 84bd1a8..1cb7049 100644
--- a/lib/conf.c
+++ b/lib/conf.c
@@ -8,9 +8,11 @@
#include <confuse.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
#include "peer.h"
+#include "util.h"
#include "debug.h"
#if 0
@@ -25,6 +27,10 @@
#cacertpath = STRING
certfile = STRING
certkeyfile = STRING
+ pskstr = STRING # Transport pre-shared key, UTF-8 form.
+ pskhexstr = STRING # Transport pre-shared key, ASCII hex form.
+ pskid = STRING
+ pskex = "PSK"|"DHE_PSK"|"RSA_PSK"
}
# client specific realm config options
@@ -32,12 +38,12 @@
server {
hostname = STRING
service = STRING
- secret = STRING
+ secret = STRING # RADIUS secret
}
}
#endif
-/* FIXME: Leaking memory in error cases? */
+/* FIXME: Leaking memory in error cases. */
int
rs_context_read_config(struct rs_context *ctx, const char *config_file)
{
@@ -63,6 +69,10 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
/*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/
CFG_STR ("certfile", NULL, CFGF_NONE),
CFG_STR ("certkeyfile", NULL, CFGF_NONE),
+ CFG_STR ("pskstr", NULL, CFGF_NONE),
+ CFG_STR ("pskhexstr", NULL, CFGF_NONE),
+ CFG_STR ("pskid", NULL, CFGF_NONE),
+ CFG_STR ("pskex", "PSK", CFGF_NONE),
CFG_SEC ("server", server_opts, CFGF_MULTI),
CFG_END ()
};
@@ -101,6 +111,7 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
{
struct rs_realm *r = NULL;
const char *typestr;
+ char *pskstr = NULL, *pskhexstr = NULL;
r = rs_calloc (ctx, 1, sizeof(*r));
if (r == NULL)
@@ -115,14 +126,14 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
config->realms = r;
}
cfg_realm = cfg_getnsec (cfg, "realm", i);
- /* We use a copy of the return value of cfg_title() since it's const. */
s = cfg_title (cfg_realm);
if (s == NULL)
return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
"missing realm name");
- r->name = strdup (s);
+ /* We use a copy of the return value of cfg_title() since it's const. */
+ r->name = rs_strdup (ctx, s);
if (r->name == NULL)
- return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
+ return RSE_NOMEM;
typestr = cfg_getstr (cfg_realm, "type");
if (strcmp (typestr, "UDP") == 0)
@@ -134,8 +145,9 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
else if (strcmp (typestr, "DTLS") == 0)
r->type = RS_CONN_TYPE_DTLS;
else
- return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
- "invalid connection type: %s", typestr);
+ return rs_err_ctx_push (ctx, RSE_CONFIG,
+ "%s: invalid connection type: %s",
+ r->name, typestr);
r->timeout = cfg_getint (cfg_realm, "timeout");
r->retries = cfg_getint (cfg_realm, "retries");
@@ -144,6 +156,65 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
r->certfile = cfg_getstr (cfg_realm, "certfile");
r->certkeyfile = cfg_getstr (cfg_realm, "certkeyfile");
+ pskstr = cfg_getstr (cfg_realm, "pskstr");
+ pskhexstr = cfg_getstr (cfg_realm, "pskhexstr");
+ if (pskstr || pskhexstr)
+ {
+#if defined RS_ENABLE_TLS_PSK
+ char *kex = cfg_getstr (cfg_realm, "pskex");
+ rs_cred_type_t type = RS_CRED_NONE;
+ struct rs_credentials *cred = NULL;
+ assert (kex != NULL);
+
+ if (!strcmp (kex, "PSK"))
+ type = RS_CRED_TLS_PSK;
+ else
+ {
+ /* TODO: push a warning on the error stack:*/
+ /*rs_err_ctx_push (ctx, RSE_WARN, "%s: unsupported PSK key exchange"
+ " algorithm -- PSK not used", kex);*/
+ }
+
+ if (type != RS_CRED_NONE)
+ {
+ cred = rs_calloc (ctx, 1, sizeof (*cred));
+ if (cred == NULL)
+ return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
+ NULL);
+ cred->type = type;
+ cred->identity = cfg_getstr (cfg_realm, "pskid");
+ if (pskhexstr)
+ {
+ cred->secret_encoding = RS_KEY_ENCODING_ASCII_HEX;
+ cred->secret = pskhexstr;
+ if (pskstr)
+ ; /* TODO: warn that we're ignoring pskstr */
+ }
+ else
+ {
+ cred->secret_encoding = RS_KEY_ENCODING_UTF8;
+ cred->secret = pskstr;
+ }
+
+ r->transport_cred = cred;
+ }
+#else /* !RS_ENABLE_TLS_PSK */
+ /* TODO: push a warning on the error stack: */
+ /* rs_err_ctx_push (ctx, RSE_WARN, "libradsec wasn't configured with "
+ "support for TLS preshared keys, ignoring pskstr "
+ "and pskhexstr");*/
+#endif /* RS_ENABLE_TLS_PSK */
+ }
+
+ /* For TLS and DTLS realms, validate that we either have (i) CA
+ cert file or path or (ii) PSK. */
+ if ((r->type == RS_CONN_TYPE_TLS || r->type == RS_CONN_TYPE_DTLS)
+ && (r->cacertfile == NULL && r->cacertpath == NULL)
+ && r->transport_cred == NULL)
+ return rs_err_ctx_push (ctx, RSE_CONFIG,
+ "%s: missing both CA file/path and PSK",
+ r->name);
+
/* Add peers, one per server stanza. */
for (j = 0; j < cfg_size (cfg_realm, "server"); j++)
{
@@ -154,10 +225,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
p->realm = r;
cfg_server = cfg_getnsec (cfg_realm, "server", j);
- /* FIXME: Handle resolve errors, possibly by postponing name
- resolution. */
- rs_resolv (&p->addr, r->type, cfg_getstr (cfg_server, "hostname"),
- cfg_getstr (cfg_server, "service"));
+ p->hostname = cfg_getstr (cfg_server, "hostname");
+ p->service = cfg_getstr (cfg_server, "service");
p->secret = cfg_getstr (cfg_server, "secret");
}
}
diff --git a/lib/configure.ac b/lib/configure.ac
index 9b1d304..bb71a6a 100644
--- a/lib/configure.ac
+++ b/lib/configure.ac
@@ -19,14 +19,22 @@ AC_CHECK_LIB([event_core], [event_get_version],,
AC_MSG_ERROR([required library libevent_core not found]))
# Enable-knobs.
+## Enable TLS (RadSec).
AH_TEMPLATE([RS_ENABLE_TLS], [TLS (RadSec) enabled])
-AH_TEMPLATE([RADPROT_TLS], [])
+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])])
+ AC_DEFINE([RADPROT_TLS])]) dnl Legacy.
AM_CONDITIONAL([RS_ENABLE_TLS], [test "${enable_tls+set}" = set])
+## Enable TLS-PSK (preshared keys).
+AH_TEMPLATE([RS_ENABLE_TLS_PSK], [TLS-PSK (TLS preshared keys) enabled])
+AC_ARG_ENABLE([tls-psk], AS_HELP_STRING([--enable-tls-psk], [enable TLS-PSK (TLS preshared keys)]),
+ [AC_CHECK_LIB([ssl], [SSL_set_psk_client_callback],,
+ AC_MSG_ERROR([required library openssl with SSL_set_psk_client_callback() not found]))
+ AC_DEFINE([RS_ENABLE_TLS_PSK])])
+AM_CONDITIONAL([RS_ENABLE_TLS_PSK], [test "${enable_tls_psk+set}" = set])
# Checks for header files.
AC_CHECK_HEADERS(
diff --git a/lib/conn.c b/lib/conn.c
index 7d0a794..fa63727 100644
--- a/lib/conn.c
+++ b/lib/conn.c
@@ -224,7 +224,7 @@ rs_conn_receive_packet (struct rs_connection *conn,
assert (conn);
assert (conn->realm);
- assert (!conn_user_dispatch_p (conn)); /* Dispatching mode only. */
+ assert (!conn_user_dispatch_p (conn)); /* Blocking mode only. */
if (rs_packet_create (conn, &pkt))
return -1;
@@ -254,7 +254,7 @@ rs_conn_receive_packet (struct rs_connection *conn,
"event_add: %s",
evutil_gai_strerror (err));
- /* Activae retransmission timer. */
+ /* Activate retransmission timer. */
conn_activate_timeout (pkt->conn);
}
@@ -271,7 +271,10 @@ rs_conn_receive_packet (struct rs_connection *conn,
|| (req_msg
&& packet_verify_response (pkt->conn, pkt, req_msg) != RSE_OK))
{
- assert (rs_err_conn_peek_code (pkt->conn));
+ 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);
}
diff --git a/lib/err.c b/lib/err.c
index e318047..60ef82f 100644
--- a/lib/err.c
+++ b/lib/err.c
@@ -56,6 +56,8 @@ static const char *_errtxt[] = {
"response from the wrong source address", /* 40 RSE_INVALID_RESPONSE_SRC */
"no packet data", /* 41 RSE_NO_PACKET_DATA */
"vendor is unknown", /* 42 RSE_VENDOR_UNKNOWN */
+ "invalid credentials", /* 43 RSE_CRED */
+ "certificate validation error", /* 44 RSE_CERT */
};
#define ERRTXT_SIZE (sizeof(_errtxt) / sizeof(*_errtxt))
diff --git a/lib/event.c b/lib/event.c
index b2096bc..4f83394 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -22,6 +22,8 @@
#if defined (RS_ENABLE_TLS)
#include "tls.h"
#endif
+#include "err.h"
+#include "radsec.h"
#include "event.h"
#include "packet.h"
#include "conn.h"
@@ -100,9 +102,16 @@ event_init_socket (struct rs_connection *conn, struct rs_peer *p)
if (conn->fd != -1)
return RSE_OK;
- assert (p->addr);
- conn->fd = socket (p->addr->ai_family, p->addr->ai_socktype,
- p->addr->ai_protocol);
+ if (p->addr_cache == NULL)
+ {
+ struct rs_error *err =
+ rs_resolve (&p->addr_cache, p->realm->type, p->hostname, p->service);
+ if (err != NULL)
+ return err_conn_push_err (conn, err);
+ }
+
+ conn->fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype,
+ p->addr_cache->ai_protocol);
if (conn->fd < 0)
return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
"socket: %d (%s)",
@@ -171,8 +180,8 @@ event_do_connect (struct rs_connection *conn)
{
char host[80], serv[80];
- getnameinfo (p->addr->ai_addr,
- p->addr->ai_addrlen,
+ getnameinfo (p->addr_cache->ai_addr,
+ p->addr_cache->ai_addrlen,
host, sizeof(host), serv, sizeof(serv),
0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
rs_debug (("%s: connecting to %s:%s\n", __func__, host, serv));
@@ -182,8 +191,8 @@ event_do_connect (struct rs_connection *conn)
if (p->conn->bev) /* TCP */
{
conn_activate_timeout (conn); /* Connect timeout. */
- err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr,
- p->addr->ai_addrlen);
+ err = bufferevent_socket_connect (p->conn->bev, p->addr_cache->ai_addr,
+ p->addr_cache->ai_addrlen);
if (err < 0)
rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__,
"bufferevent_socket_connect: %s",
@@ -193,7 +202,9 @@ event_do_connect (struct rs_connection *conn)
}
else /* UDP */
{
- err = connect (p->conn->fd, p->addr->ai_addr, p->addr->ai_addrlen);
+ err = connect (p->conn->fd,
+ p->addr_cache->ai_addr,
+ p->addr_cache->ai_addrlen);
if (err < 0)
{
sockerr = evutil_socket_geterror (p->conn->fd);
@@ -228,10 +239,22 @@ event_on_disconnect (struct rs_connection *conn)
conn->callbacks.disconnected_cb (conn->user_data);
}
-void
+/** Internal connect event returning 0 on success or -1 on error. */
+int
event_on_connect (struct rs_connection *conn, struct rs_packet *pkt)
{
assert (!conn->is_connecting);
+
+#if defined (RS_ENABLE_TLS)
+ if (conn->realm->type == RS_CONN_TYPE_TLS
+ || conn->realm->type == RS_CONN_TYPE_DTLS)
+ if (tls_verify_cert (conn) != RSE_OK)
+ {
+ rs_debug (("%s: server cert verification failed\n", __func__));
+ return -1;
+ }
+#endif /* RS_ENABLE_TLS */
+
conn->is_connected = 1;
rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
@@ -240,6 +263,8 @@ event_on_connect (struct rs_connection *conn, struct rs_packet *pkt)
if (pkt)
packet_do_send (pkt);
+
+ return 0;
}
int
diff --git a/lib/event.h b/lib/event.h
index e042599..befbd0d 100644
--- a/lib/event.h
+++ b/lib/event.h
@@ -2,7 +2,7 @@
See the file COPYING for licensing information. */
void event_on_disconnect (struct rs_connection *conn);
-void event_on_connect (struct rs_connection *conn, struct rs_packet *pkt);
+int event_on_connect (struct rs_connection *conn, struct rs_packet *pkt);
int event_loopbreak (struct rs_connection *conn);
int event_init_eventbase (struct rs_connection *conn);
int event_init_socket (struct rs_connection *conn, struct rs_peer *p);
diff --git a/lib/examples/client-blocking.c b/lib/examples/client-blocking.c
index 2cfd617..7d3869a 100644
--- a/lib/examples/client-blocking.c
+++ b/lib/examples/client-blocking.c
@@ -4,10 +4,12 @@
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
+#include <assert.h>
#include <event2/event.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
#include <radsec/request.h>
+#include "err.h"
#include "debug.h" /* For rs_dump_packet(). */
#define SECRET "sikrit"
@@ -15,7 +17,8 @@
#define USER_PW "password"
struct rs_error *
-blocking_client (const char *av1, const char *av2, int use_request_object_flag)
+blocking_client (const char *config_fn, const char *configuration,
+ int use_request_object_flag)
{
struct rs_context *h = NULL;
struct rs_connection *conn = NULL;
@@ -24,7 +27,11 @@ blocking_client (const char *av1, const char *av2, int use_request_object_flag)
struct rs_error *err = NULL;
if (rs_context_create (&h))
- return NULL;
+ {
+ err = err_create (RSE_INTERNAL, NULL, 0, "unable to create context");
+ assert (err != NULL);
+ return err;
+ }
#if !defined (USE_CONFIG_FILE)
{
@@ -43,9 +50,9 @@ blocking_client (const char *av1, const char *av2, int use_request_object_flag)
goto cleanup;
}
#else /* defined (USE_CONFIG_FILE) */
- if (rs_context_read_config (h, av1))
+ if (rs_context_read_config (h, config_fn))
goto cleanup;
- if (rs_conn_create (h, &conn, av2))
+ if (rs_conn_create (h, &conn, configuration))
goto cleanup;
#endif /* defined (USE_CONFIG_FILE) */
@@ -93,6 +100,13 @@ blocking_client (const char *av1, const char *av2, int use_request_object_flag)
return err;
}
+void
+usage (int argc, char *argv[])
+{
+ fprintf (stderr, "usage: %s: [-r] config-file config-name\n", argv[0]);
+ exit (1);
+}
+
int
main (int argc, char *argv[])
{
@@ -105,6 +119,8 @@ main (int argc, char *argv[])
argc--;
argv++;
}
+ if (argc < 3)
+ usage (argc, argv);
err = blocking_client (argv[1], argv[2], use_request_object_flag);
if (err)
{
diff --git a/lib/examples/client.conf b/lib/examples/client.conf
index 47528c8..bf57434 100644
--- a/lib/examples/client.conf
+++ b/lib/examples/client.conf
@@ -16,6 +16,10 @@ realm blocking-tls {
cacertfile = "tests/demoCA/newcerts/01.pem"
certfile = "tests/demoCA/newcerts/02.pem"
certkeyfile = "tests/demoCA/private/c2key.pem"
+ #pskstr = "sikrit psk"
+ pskhexstr = "deadbeef4711"
+ pskid = "Client_identity"
+ pskex = "PSK"
server {
hostname = "localhost"
service = "2083"
diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h
index 2274a99..6339e74 100644
--- a/lib/include/radsec/radsec-impl.h
+++ b/lib/include/radsec/radsec-impl.h
@@ -18,10 +18,19 @@
/* Data types. */
enum rs_cred_type {
RS_CRED_NONE = 0,
- RS_CRED_TLS_PSK_RSA, /* RFC 4279. */
+ /* TLS pre-shared keys, RFC 4279. */
+ RS_CRED_TLS_PSK,
+ /* RS_CRED_TLS_DH_PSK, */
+ /* RS_CRED_TLS_RSA_PSK, */
};
typedef unsigned int rs_cred_type_t;
+enum rs_key_encoding {
+ RS_KEY_ENCODING_UTF8 = 1,
+ RS_KEY_ENCODING_ASCII_HEX = 2,
+};
+typedef unsigned int rs_key_encoding_t;
+
#if defined (__cplusplus)
extern "C" {
#endif
@@ -30,6 +39,8 @@ struct rs_credentials {
enum rs_cred_type type;
char *identity;
char *secret;
+ enum rs_key_encoding secret_encoding;
+ unsigned int secret_len;
};
struct rs_error {
@@ -41,8 +52,10 @@ struct rs_error {
struct rs_peer {
struct rs_connection *conn;
struct rs_realm *realm;
- struct evutil_addrinfo *addr;
- char *secret;
+ char *hostname;
+ char *service;
+ char *secret; /* RADIUS secret. */
+ struct evutil_addrinfo *addr_cache;
struct rs_peer *next;
};
@@ -56,6 +69,7 @@ struct rs_realm {
char *cacertpath;
char *certfile;
char *certkeyfile;
+ struct rs_credentials *transport_cred;
struct rs_peer *peers;
struct rs_realm *next;
};
@@ -77,7 +91,6 @@ struct rs_connection {
struct rs_realm *realm; /* Owned by ctx. */
struct event_base *evb; /* Event base. */
struct event *tev; /* Timeout event. */
- struct rs_credentials transport_credentials;
struct rs_conn_callbacks callbacks;
void *user_data;
struct rs_peer *peers;
@@ -118,12 +131,6 @@ struct rs_packet {
struct rs_packet *next; /* Used for UDP output queue. */
};
-/* Nonpublic functions (in radsec.c -- FIXME: move?). */
-struct rs_error *rs_resolv (struct evutil_addrinfo **addr,
- rs_conn_type_t type,
- const char *hostname,
- const char *service);
-
#if defined (__cplusplus)
}
#endif
diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h
index 6e967af..6c4f6a7 100644
--- a/lib/include/radsec/radsec.h
+++ b/lib/include/radsec/radsec.h
@@ -42,7 +42,7 @@ enum rs_error_code {
RSE_TIMEOUT_CONN = 16, /* Connection timeout. */
RSE_INVAL = 17, /* Invalid argument. */
RSE_TIMEOUT_IO = 18, /* I/O timeout. */
- RSE_TIMEOUT= 19, /* High level timeout. */
+ RSE_TIMEOUT = 19, /* High level timeout. */
RSE_DISCO = 20,
RSE_INUSE = 21,
RSE_PACKET_TOO_SMALL = 22,
@@ -66,7 +66,9 @@ enum rs_error_code {
RSE_INVALID_RESPONSE_SRC = 40,
RSE_NO_PACKET_DATA = 41,
RSE_VENDOR_UNKNOWN = 42,
- RSE_MAX = RSE_VENDOR_UNKNOWN
+ RSE_CRED = 43,
+ RSE_CERT = 44,
+ RSE_MAX = RSE_CERT
};
enum rs_conn_type {
diff --git a/lib/packet.c b/lib/packet.c
index 86a16c3..ce68bea 100644
--- a/lib/packet.c
+++ b/lib/packet.c
@@ -92,8 +92,8 @@ packet_do_send (struct rs_packet *pkt)
{
char host[80], serv[80];
- getnameinfo (pkt->conn->active_peer->addr->ai_addr,
- pkt->conn->active_peer->addr->ai_addrlen,
+ getnameinfo (pkt->conn->active_peer->addr_cache->ai_addr,
+ pkt->conn->active_peer->addr_cache->ai_addrlen,
host, sizeof(host), serv, sizeof(serv),
0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv));
diff --git a/lib/peer.c b/lib/peer.c
index 256ff76..01dc243 100644
--- a/lib/peer.c
+++ b/lib/peer.c
@@ -13,6 +13,7 @@
#include <radsec/radsec-impl.h>
#include "err.h"
#include "peer.h"
+#include "util.h"
struct rs_peer *
peer_pick_peer (struct rs_connection *conn)
@@ -69,16 +70,17 @@ rs_peer_create (struct rs_connection *conn, struct rs_peer **peer_out)
int
rs_peer_set_address (struct rs_peer *peer, const char *hostname,
- const char *service)
+ const char *service)
{
- struct rs_error *err;
-
assert (peer);
- assert (peer->realm);
+ assert (peer->conn);
+ assert (peer->conn->ctx);
+
+ peer->hostname = rs_strdup (peer->conn->ctx, hostname);
+ peer->service = rs_strdup (peer->conn->ctx, service);
+ if (peer->hostname == NULL || peer->service == NULL)
+ return RSE_NOMEM;
- err = rs_resolv (&peer->addr, peer->realm->type, hostname, service);
- if (err)
- return err_conn_push_err (peer->conn, err);
return RSE_OK;
}
diff --git a/lib/radsec.c b/lib/radsec.c
index 7421755..347a48b 100644
--- a/lib/radsec.c
+++ b/lib/radsec.c
@@ -49,10 +49,10 @@ rs_context_create (struct rs_context **ctx)
}
struct rs_error *
-rs_resolv (struct evutil_addrinfo **addr,
- rs_conn_type_t type,
- const char *hostname,
- const char *service)
+rs_resolve (struct evutil_addrinfo **addr,
+ rs_conn_type_t type,
+ const char *hostname,
+ const char *service)
{
int err;
struct evutil_addrinfo hints, *res = NULL;
@@ -102,12 +102,16 @@ rs_context_destroy (struct rs_context *ctx)
for (p = r->peers; p; )
{
struct rs_peer *tmp = p;
- if (p->addr)
- evutil_freeaddrinfo (p->addr);
+ if (p->addr_cache)
+ {
+ evutil_freeaddrinfo (p->addr_cache);
+ p->addr_cache = NULL;
+ }
p = p->next;
rs_free (ctx, tmp);
}
free (r->name);
+ rs_free (ctx, r->transport_cred);
r = r->next;
rs_free (ctx, tmp);
}
diff --git a/lib/radsec.h b/lib/radsec.h
new file mode 100644
index 0000000..9e64692
--- /dev/null
+++ b/lib/radsec.h
@@ -0,0 +1,7 @@
+/* Copyright 2012 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+struct rs_error *rs_resolve (struct evutil_addrinfo **addr,
+ rs_conn_type_t type,
+ const char *hostname,
+ const char *service);
diff --git a/lib/rsp_tlscommon.c b/lib/rsp_tlscommon.c
index a34fe33..abc395e 100644
--- a/lib/rsp_tlscommon.c
+++ b/lib/rsp_tlscommon.c
@@ -11,7 +11,6 @@
#endif
#include <sys/types.h>
-#if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -271,14 +270,15 @@ static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) {
}
}
- if (!tlsaddcacrl(ctx, conf)) {
- if (conf->vpm) {
- X509_VERIFY_PARAM_free(conf->vpm);
- conf->vpm = NULL;
- }
- SSL_CTX_free(ctx);
- return NULL;
- }
+ if (conf->cacertfile != NULL || conf->cacertpath != NULL)
+ if (!tlsaddcacrl(ctx, conf)) {
+ if (conf->vpm) {
+ X509_VERIFY_PARAM_free(conf->vpm);
+ conf->vpm = NULL;
+ }
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
debug(DBG_DBG, "tlscreatectx: created TLS context %s", conf->name);
return ctx;
@@ -352,7 +352,7 @@ X509 *verifytlscert(SSL *ssl) {
return cert;
}
-static int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) {
+int subjectaltnameaddr(X509 *cert, int family, const struct in6_addr *addr) {
int loc, i, l, n, r = 0;
char *v;
X509_EXTENSION *ex;
@@ -388,7 +388,7 @@ static int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) {
return r;
}
-static int subjectaltnameregexp(X509 *cert, int type, char *exact, regex_t *regex) {
+int subjectaltnameregexp(X509 *cert, int type, const char *exact, const regex_t *regex) {
int loc, i, l, n, r = 0;
char *s, *v;
X509_EXTENSION *ex;
@@ -441,7 +441,7 @@ static int subjectaltnameregexp(X509 *cert, int type, char *exact, regex_t *reg
return r;
}
-static int cnregexp(X509 *cert, char *exact, regex_t *regex) {
+int cnregexp(X509 *cert, const char *exact, const regex_t *regex) {
int loc, l;
char *v, *s;
X509_NAME *nm;
@@ -544,118 +544,6 @@ int verifyconfcert(X509 *cert, struct clsrvconf *conf) {
return 1;
}
-#if 0
-int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) {
- struct tls *conf;
- long int expiry = LONG_MIN;
-
- debug(DBG_DBG, "conftls_cb called for %s", block);
-
- conf = malloc(sizeof(struct tls));
- if (!conf) {
- debug(DBG_ERR, "conftls_cb: malloc failed");
- return 0;
- }
- memset(conf, 0, sizeof(struct tls));
-
- if (!getgenericconfig(cf, block,
- "CACertificateFile", CONF_STR, &conf->cacertfile,
- "CACertificatePath", CONF_STR, &conf->cacertpath,
- "CertificateFile", CONF_STR, &conf->certfile,
- "CertificateKeyFile", CONF_STR, &conf->certkeyfile,
- "CertificateKeyPassword", CONF_STR, &conf->certkeypwd,
- "CacheExpiry", CONF_LINT, &expiry,
- "CRLCheck", CONF_BLN, &conf->crlcheck,
- "PolicyOID", CONF_MSTR, &conf->policyoids,
- NULL
- )) {
- debug(DBG_ERR, "conftls_cb: configuration error in block %s", val);
- goto errexit;
- }
- if (!conf->certfile || !conf->certkeyfile) {
- debug(DBG_ERR, "conftls_cb: TLSCertificateFile and TLSCertificateKeyFile must be specified in block %s", val);
- goto errexit;
- }
- if (!conf->cacertfile && !conf->cacertpath) {
- debug(DBG_ERR, "conftls_cb: CA Certificate file or path need to be specified in block %s", val);
- goto errexit;
- }
- if (expiry != LONG_MIN) {
- if (expiry < 0) {
- debug(DBG_ERR, "error in block %s, value of option CacheExpiry is %ld, may not be negative", val, expiry);
- goto errexit;
- }
- conf->cacheexpiry = expiry;
- }
-
- conf->name = stringcopy(val, 0);
- if (!conf->name) {
- debug(DBG_ERR, "conftls_cb: malloc failed");
- goto errexit;
- }
-
- if (!tlsconfs)
- tlsconfs = hash_create();
- if (!hash_insert(tlsconfs, val, strlen(val), conf)) {
- debug(DBG_ERR, "conftls_cb: malloc failed");
- goto errexit;
- }
- if (!tlsgetctx(RAD_TLS, conf))
- debug(DBG_ERR, "conftls_cb: error creating ctx for TLS block %s", val);
- debug(DBG_DBG, "conftls_cb: added TLS block %s", val);
- return 1;
-
-errexit:
- free(conf->cacertfile);
- free(conf->cacertpath);
- free(conf->certfile);
- free(conf->certkeyfile);
- free(conf->certkeypwd);
- freegconfmstr(conf->policyoids);
- free(conf);
- return 0;
-}
-#endif
-
-int addmatchcertattr(struct clsrvconf *conf) {
- char *v;
- regex_t **r;
-
- if (!strncasecmp(conf->matchcertattr, "CN:/", 4)) {
- r = &conf->certcnregex;
- v = conf->matchcertattr + 4;
- } else if (!strncasecmp(conf->matchcertattr, "SubjectAltName:URI:/", 20)) {
- r = &conf->certuriregex;
- v = conf->matchcertattr + 20;
- } else
- return 0;
- if (!*v)
- return 0;
- /* regexp, remove optional trailing / if present */
- if (v[strlen(v) - 1] == '/')
- v[strlen(v) - 1] = '\0';
- if (!*v)
- return 0;
-
- *r = malloc(sizeof(regex_t));
- if (!*r) {
- debug(DBG_ERR, "malloc failed");
- return 0;
- }
- if (regcomp(*r, v, REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
- free(*r);
- *r = NULL;
- debug(DBG_ERR, "failed to compile regular expression %s", v);
- return 0;
- }
- return 1;
-}
-#else
-/* Just to makes file non-empty, should rather avoid compiling this file when not needed */
-static void tlsdummy() {
-}
-#endif
-
/* Local Variables: */
/* c-file-style: "stroustrup" */
/* End: */
diff --git a/lib/rsp_tlscommon.h b/lib/rsp_tlscommon.h
index 6819cd0..d96f553 100644
--- a/lib/rsp_tlscommon.h
+++ b/lib/rsp_tlscommon.h
@@ -6,6 +6,7 @@
* copyright notice and this permission notice appear in all copies.
*/
+#include <netinet/in.h>
#include <openssl/ssl.h>
#if defined (__cplusplus)
@@ -34,9 +35,10 @@ 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);
-int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val);
-int addmatchcertattr(struct clsrvconf *conf);
#endif
#if defined (__cplusplus)
diff --git a/lib/tcp.c b/lib/tcp.c
index 3a59d6f..841f905 100644
--- a/lib/tcp.c
+++ b/lib/tcp.c
@@ -154,24 +154,32 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
{
struct rs_packet *pkt = (struct rs_packet *) user_data;
struct rs_connection *conn = NULL;
- struct rs_peer *p = NULL;
int sockerr = 0;
#if defined (RS_ENABLE_TLS)
unsigned long tlserr = 0;
#endif
+#if defined (DEBUG)
+ struct rs_peer *p = NULL;
+#endif
assert (pkt);
assert (pkt->conn);
- assert (pkt->conn->active_peer);
conn = pkt->conn;
+#if defined (DEBUG)
+ assert (pkt->conn->active_peer);
p = conn->active_peer;
+#endif
conn->is_connecting = 0;
if (events & BEV_EVENT_CONNECTED)
{
if (conn->tev)
evtimer_del (conn->tev); /* Cancel connect timer. */
- event_on_connect (conn, pkt);
+ if (event_on_connect (conn, pkt))
+ {
+ event_on_disconnect (conn);
+ event_loopbreak (conn);
+ }
}
else if (events & BEV_EVENT_EOF)
{
@@ -181,7 +189,7 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
{
rs_debug (("%s: %p times out on %s\n", __func__, p,
(events & BEV_EVENT_READING) ? "read" : "write"));
- rs_err_conn_push_fl (pkt->conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
+ rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
}
else if (events & BEV_EVENT_ERROR)
{
@@ -189,13 +197,13 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
if (sockerr == 0) /* FIXME: True that errno == 0 means closed? */
{
event_on_disconnect (conn);
- rs_err_conn_push_fl (pkt->conn, RSE_DISCO, __FILE__, __LINE__, NULL);
+ rs_err_conn_push_fl (conn, RSE_DISCO, __FILE__, __LINE__, NULL);
}
else
{
rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr,
evutil_socket_error_to_string (sockerr)));
- rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
+ rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
"%d: %d (%s)", conn->fd, sockerr,
evutil_socket_error_to_string (sockerr));
}
@@ -208,7 +216,7 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
{
rs_debug (("%s: openssl error: %s\n", __func__,
ERR_error_string (tlserr, NULL)));
- rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__,
+ rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__,
ERR_error_string (tlserr, NULL));
}
}
diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am
index 526dcdc..7c5408b 100644
--- a/lib/tests/Makefile.am
+++ b/lib/tests/Makefile.am
@@ -2,7 +2,9 @@ AUTOMAKE_OPTIONS = foreign
INCLUDES = -I$(top_srcdir)/include
AM_CFLAGS = -Wall -g
-bin_PROGRAMS = test-udp udp-server
+TESTS = test-udp
+
+check_PROGRAMS = test-udp udp-server
test_udp_SOURCES = test-udp.c udp.c
test_udp_LDADD = ../libradsec.la -lcgreen -lm
diff --git a/lib/tests/README b/lib/tests/README
index 8280b9e..4d68bde 100644
--- a/lib/tests/README
+++ b/lib/tests/README
@@ -1,24 +1,34 @@
Build
-----
-In order to build the tests, you'll need libcgreen
-(http://www.lastcraft.com/cgreen.php).
+In order to build and run the tests, you'll need to have libcgreen
+installed (http://www.lastcraft.com/cgreen.php).
Run
---
-NOTE: To run the tests you need
-- a RADIUS server running at localhost:1820 with shared sekret
- "sikrit" configured (or whatever "test-udp-auth" in test.conf says)
-- a user "molgan" with password "password" present in the RADIUS
- database
-This requirement will disappear in the future.
+NOTE: To run the tests you currently need
+- a RADIUS server running at localhost:1820 with the shared RADIUS
+ secret "sikrit" configured (or whatever "test-udp-auth" in test.conf
+ says)
+- a user "molgan@PROJECT-MOONSHOT.ORG" with password "password"
+ present in the RADIUS database
+These requirements will be removed in a future libradsec release.
+
Run the tests by typing
- ./test-udp
+ make check
-The output should be something like
+The output should read something like
Completed "main": 32 passes, 0 failures, 0 exceptions.
+
+
+When trying to debug the test programs under GDB you might run into
+trouble with multiple threads being executed by the test framework.
+If so, make sure to run a single test rather than the full test suite.
+For example:
+
+ libtool --mode execute gdb --args test-udp test_auth
diff --git a/lib/tests/test-udp.c b/lib/tests/test-udp.c
index f66eebd..ccad607 100644
--- a/lib/tests/test-udp.c
+++ b/lib/tests/test-udp.c
@@ -34,11 +34,12 @@ send_more_than_one_msg_in_one_packet (struct rs_connection *conn)
assert_true (rs_packet_send (msg1, NULL) == 0);
}
+#if 0
static void
send_large_packet (struct rs_connection *conn)
{
struct rs_packet *msg0;
- struct rs_attr *attr_x;
+ struct radius_packet *frpkt = NULL;
char *buf;
int f;
@@ -51,11 +52,12 @@ send_large_packet (struct rs_connection *conn)
for (f = 0; f < 15; f++)
{
memset (buf, 'a' + f, 252);
- rs_attr_create (conn, &attr_x, "EAP-Message", buf);
- rs_packet_add_attr (msg0, attr_x);
+ //vp = pairmake ("EAP-Message", buf, T_OP_EQ);
+ assert_true (rs_packet_append_avp (msg0, fixme...) == RSE_OK);
}
assert_true (rs_packet_send (msg0, NULL) == 0);
}
+#endif /* 0 */
/* ************************************************************ */
static struct setup {
@@ -73,11 +75,12 @@ test_auth ()
setup.config_file = "test.conf";
setup.config_name = "test-udp-auth";
- setup.username = "molgan";
+ setup.username = "molgan@PROJECT-MOONSHOT.ORG";
setup.pw = "password";
- assert_true (rs_context_create (&ctx, NULL) == 0);
+ assert_true (rs_context_create (&ctx) == 0);
assert_true (rs_context_read_config (ctx, setup.config_file) == 0);
+ assert_true (rs_context_init_freeradius_dict (ctx, NULL) == 0);
assert_true (rs_conn_create (ctx, &conn, setup.config_name) == 0);
authenticate (conn, setup.username, setup.pw);
@@ -108,7 +111,7 @@ test_buffering ()
struct timeval timeout;
struct polldata *polldata;
- assert_true (rs_context_create (&ctx, NULL) == 0);
+ assert_true (rs_context_create (&ctx) == 0);
assert_true (rs_context_read_config (ctx, "test.conf") == 0);
assert_true (rs_conn_create (ctx, &conn, "test-udp-buffering") == 0);
@@ -121,8 +124,21 @@ test_buffering ()
assert_true (udp_poll (polldata) > 0);
assert_true (udp_poll (polldata) > 0);
+#if 0
+"
+send_large_packet() disabled, it's hanging after
+
+Sending Access-Request of id 1 to (null) port 0
+ Message-Authenticator = 0x00000000000000000000000000000000
+packet_do_send: about to send this to localhost:11820:
+ Code: 1, Identifier: 1, Lenght: 38
+rs_packet_send: entering event loop
+_evcb: fd=5 what = WRITE
+rs_packet_send: event loop done
+"
send_large_packet (conn);
assert_true (udp_poll (polldata) > 0);
+#endif /* 0 */
udp_free_polldata (polldata);
rs_conn_destroy (conn);
diff --git a/lib/tests/test.conf b/lib/tests/test.conf
index 9be968d..839fd75 100644
--- a/lib/tests/test.conf
+++ b/lib/tests/test.conf
@@ -1,4 +1,6 @@
-config test-udp-auth {
+dictionary = "/home/linus/usr/moonshot/share/freeradius/dictionary"
+
+realm test-udp-auth {
type = "UDP"
server {
hostname = "localhost"
@@ -7,7 +9,7 @@ config test-udp-auth {
}
}
-config test-udp-buffering {
+realm test-udp-buffering {
type = "UDP"
server {
hostname = "localhost"
@@ -16,7 +18,7 @@ config test-udp-buffering {
}
}
-config test-tls-test {
+realm test-tls-test {
type = "TLS"
cacertfile = "/home/linus/nordberg-ca.crt"
certfile = "/home/linus/p/radsecproxy/src/maatuska.nordberg.se.crt"
diff --git a/lib/tls.c b/lib/tls.c
index 6fcf5a0..610df98 100644
--- a/lib/tls.c
+++ b/lib/tls.c
@@ -8,6 +8,8 @@
#include <assert.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/x509v3.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
@@ -41,6 +43,74 @@ _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm)
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 */
+
int
rs_tls_init (struct rs_connection *conn)
{
@@ -56,7 +126,7 @@ rs_tls_init (struct rs_connection *conn)
tlsconf = _get_tlsconf (conn, conn->active_peer->realm);
if (!tlsconf)
return -1;
- ssl_ctx = tlsgetctx (RADPROT_TLS, tlsconf);
+ ssl_ctx = tlsgetctx (RAD_TLS, tlsconf);
if (!ssl_ctx)
{
for (sslerr = ERR_get_error (); sslerr; sslerr = ERR_get_error ())
@@ -73,8 +143,93 @@ rs_tls_init (struct rs_connection *conn)
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 (!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;
+}
diff --git a/lib/tls.h b/lib/tls.h
index d457cfd..0dc2ebd 100644
--- a/lib/tls.h
+++ b/lib/tls.h
@@ -6,6 +6,7 @@ extern "C" {
#endif
int rs_tls_init (struct rs_connection *conn);
+int tls_verify_cert (struct rs_connection *conn);
#if defined (__cplusplus)
}
diff --git a/lib/util.c b/lib/util.c
new file mode 100644
index 0000000..3c9fef6
--- /dev/null
+++ b/lib/util.c
@@ -0,0 +1,19 @@
+/* Copyright 2012 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+#include <string.h>
+#include <radsec/radsec.h>
+#include <radsec/radsec-impl.h>
+#include "util.h"
+
+char *
+rs_strdup (struct rs_context *ctx, const char *s)
+{
+ char *buf = rs_calloc (ctx, 1, strlen (s) + 1);
+
+ if (buf != NULL)
+ return strcpy (buf, s);
+
+ rs_err_ctx_push (ctx, RSE_NOMEM, NULL);
+ return NULL;
+}
diff --git a/lib/util.h b/lib/util.h
new file mode 100644
index 0000000..90c55d8
--- /dev/null
+++ b/lib/util.h
@@ -0,0 +1,4 @@
+/* Copyright 2012 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+char *rs_strdup (struct rs_context *ctx, const char *s);