summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordu.net>2010-11-11 10:30:35 +0100
committerLinus Nordberg <linus@nordu.net>2010-11-11 10:30:35 +0100
commit83e82dba47aced4a93f9e431b4d8bca94c2f8517 (patch)
tree7ff1779ea924d557b6ded9bd21c0cc8a65f062dd /lib
parentf9b25cad24ec4e3e89e818457beb29cbe08eed0c (diff)
Bringing up TLS connections working.
NOTE: Clean up of resources not yet sane. Expect resource leakages. NOTE: Most failure cases are not handled properly. With the wind at your back and the sun shining, it might work.
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am13
-rw-r--r--lib/attr.c4
-rw-r--r--lib/conf.c23
-rw-r--r--lib/configure.ac17
-rw-r--r--lib/conn.c7
-rw-r--r--lib/debug.c4
-rw-r--r--lib/err.c14
-rw-r--r--lib/examples/client.conf22
-rw-r--r--lib/include/radsec/radsec-impl.h12
-rw-r--r--lib/include/radsec/radsec.h2
-rw-r--r--lib/packet.c62
-rw-r--r--lib/radsec.c14
-rw-r--r--lib/request.c12
-rw-r--r--lib/rsp_debug.c217
-rw-r--r--lib/rsp_debug.h32
-rw-r--r--lib/rsp_hash.c136
-rw-r--r--lib/rsp_hash.h48
-rw-r--r--lib/rsp_list.c127
-rw-r--r--lib/rsp_list.h51
-rw-r--r--lib/rsp_tlscommon.c664
-rw-r--r--lib/rsp_tlscommon.h40
-rw-r--r--lib/rsp_util.c256
-rw-r--r--lib/rsp_util.h25
-rw-r--r--lib/tls.c73
-rw-r--r--lib/tls.h1
25 files changed, 1855 insertions, 21 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 5d7cd0a..ca7de1f 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -13,7 +13,16 @@ libradsec_la_SOURCES = \
debug.c \
err.c \
packet.c \
+ radsec.c \
request.c \
- radsec.c
+ tls.c
+
+libradsec_la_SOURCES += \
+ rsp_debug.c \
+ rsp_hash.c \
+ rsp_list.c \
+ rsp_tlscommon.c \
+ rsp_util.c
+
libradsec_la_LDFLAGS = -version-info 0:0:0
-libradsec_la_CFLAGS = $(CFLAGS) #-DDEBUG -DDEBUG_LEVENT
+libradsec_la_CFLAGS = $(CFLAGS) -DDEBUG -DDEBUG_LEVENT
diff --git a/lib/attr.c b/lib/attr.c
index 4e66235..29384d5 100644
--- a/lib/attr.c
+++ b/lib/attr.c
@@ -1,5 +1,9 @@
/* See the file COPYING for licensing information. */
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <freeradius/libradius.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
diff --git a/lib/conf.c b/lib/conf.c
index cedb84c..c235ffe 100644
--- a/lib/conf.c
+++ b/lib/conf.c
@@ -1,3 +1,9 @@
+/* See the file COPYING for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <confuse.h>
#include <string.h>
#include <radsec/radsec.h>
@@ -6,7 +12,11 @@
#if 0
# example of client config
config NAME {
- type = "UDP|TCP|TLS|DTLS"
+ type = "UDP"|"TCP"|"TLS"|"DTLS"
+ cacertfile = STRING
+ #cacertpath = STRING
+ certfile = STRING
+ certkeyfile = STRING
server {
hostname = STRING
service = STRING
@@ -33,6 +43,10 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
cfg_opt_t config_opts[] =
{
CFG_STR ("type", "UDP", CFGF_NONE),
+ CFG_STR ("cacertfile", NULL, CFGF_NONE),
+ /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/
+ CFG_STR ("certfile", NULL, CFGF_NONE),
+ CFG_STR ("certkeyfile", NULL, CFGF_NONE),
CFG_SEC ("server", server_opts, CFGF_MULTI),
CFG_END ()
};
@@ -62,6 +76,7 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
ctx->realms = r;
cfg_config = cfg_getnsec (cfg, "config", i);
r->name = strdup (cfg_title (cfg_config));
+
typestr = cfg_getstr (cfg_config, "type");
if (!strcmp (typestr, "UDP"))
r->type = RS_CONN_TYPE_UDP;
@@ -75,6 +90,11 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
"%s: invalid connection type", typestr);
+ r->cacertfile = cfg_getstr (cfg_config, "cacertfile");
+ /*r->cacertpath = cfg_getstr (cfg_config, "cacertpath");*/
+ r->certfile = cfg_getstr (cfg_config, "certfile");
+ r->certkeyfile = cfg_getstr (cfg_config, "certkeyfile");
+
/* Add peers, one per server stanza. */
for (j = 0; j < cfg_size (cfg_config, "server"); j++)
{
@@ -82,6 +102,7 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
if (!p)
return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
NULL);
+ p->realm = r;
cfg_server = cfg_getnsec (cfg_config, "server", j);
_rs_resolv (&p->addr, r->type, cfg_getstr (cfg_server, "hostname"),
diff --git a/lib/configure.ac b/lib/configure.ac
index 5ae729f..2039233 100644
--- a/lib/configure.ac
+++ b/lib/configure.ac
@@ -11,6 +11,13 @@ AC_PROG_LIBTOOL
# Checks for programs.
AC_PROG_CC
+# Enable-knobs.
+AH_TEMPLATE([RS_ENABLE_TLS], [TLS (RadSec) enabled])
+AH_TEMPLATE([RADPROT_TLS], [])
+AC_ARG_ENABLE([tls], AS_HELP_STRING([--enable-tls], [enable TLS (RadSec)]),
+ [AC_DEFINE([RS_ENABLE_TLS])
+ AC_DEFINE([RADPROT_TLS])])
+
# Checks for libraries.
AC_CHECK_LIB([confuse], [cfg_init],,
AC_MSG_ERROR([required library libconfuse not found]))
@@ -18,9 +25,15 @@ AC_CHECK_LIB([event_core], [event_get_version],,
AC_MSG_ERROR([required library libevent_core not found]))
AC_CHECK_LIB([freeradius-radius], [rad_alloc],,
AC_MSG_ERROR([required library libfreeradius-radius not found]))
+dnl TODO: Only do this if --enable-tls or --enable-dtls.
+#AC_CHECK_LIB([ssl], [SSL_new],,
+# AC_MSG_ERROR([required library libssl not found]))
+AC_CHECK_LIB([event_openssl], [bufferevent_openssl_socket_new],,
+ AC_MSG_ERROR([required library event_openssl not found]))
# Checks for header files.
-AC_CHECK_HEADERS([netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h unistd.h])
+AC_CHECK_HEADERS(
+ [netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
@@ -28,8 +41,6 @@ AC_TYPE_SSIZE_T
AC_TYPE_UINT8_T
# Checks for library functions.
-AC_FUNC_MALLOC
-AC_FUNC_REALLOC
AC_CHECK_FUNCS([memset socket strdup strerror strrchr])
AC_CONFIG_FILES([Makefile
diff --git a/lib/conn.c b/lib/conn.c
index 62105e6..b65b1da 100644
--- a/lib/conn.c
+++ b/lib/conn.c
@@ -1,6 +1,11 @@
/* See the file COPYING for licensing information. */
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <assert.h>
+#include <event2/event.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
@@ -49,7 +54,7 @@ _rs_resolv (struct evutil_addrinfo **addr, rs_conn_type_t type,
struct evutil_addrinfo hints, *res = NULL;
memset (&hints, 0, sizeof(struct evutil_addrinfo));
- hints.ai_family = AF_UNSPEC; /* v4 or v6. */
+ hints.ai_family = AF_INET; /* IPv4 only. TODO: Set AF_UNSPEC. */
hints.ai_flags = AI_ADDRCONFIG;
switch (type)
{
diff --git a/lib/debug.c b/lib/debug.c
index e6c6afe..604ab23 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -1,5 +1,9 @@
/* See the file COPYING for licensing information. */
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <freeradius/libradius.h>
#include <radsec/radsec.h>
diff --git a/lib/err.c b/lib/err.c
index 51e4421..8d26d3c 100644
--- a/lib/err.c
+++ b/lib/err.c
@@ -1,5 +1,9 @@
/* See the file COPYING for licensing information. */
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <string.h>
#include <assert.h>
@@ -8,8 +12,8 @@
const char *_errtxt[] = {
"SUCCESS", /* 0 RSE_OK */
- "NOMEM", /* 1 RSE_NOMEM */
- "NYI -- not yet implemented", /* 2 RSE_NOSYS */
+ "out of memory", /* 1 RSE_NOMEM */
+ "not yet implemented", /* 2 RSE_NOSYS */
"invalid handle" /* 3 RSE_INVALID_CTX */
"invalid connection" /* 4 RSE_INVALID_CONN */
"connection type mismatch" /* 5 RSE_CONN_TYPE_MISMATCH */
@@ -19,9 +23,9 @@ const char *_errtxt[] = {
"libevent error" /* 9 RSE_EVENT */
"connection error" /* 10 RSE_CONNERR */
"invalid configuration file" /* 11 RSE_CONFIG */
- "authentication failed" /* RSE_BADAUTH */
- "ERR 13" /* RSE_ */
- "ERR 14" /* RSE_ */
+ "authentication failed" /* 12 RSE_BADAUTH */
+ "internal error" /* 13 RSE_INTERNAL */
+ "SSL error" /* 14 RSE_SSLERR */
"ERR 15" /* RSE_ */
"ERR 16" /* RSE_ */
"ERR 17" /* RSE_ */
diff --git a/lib/examples/client.conf b/lib/examples/client.conf
index fe2ded5..e939756 100644
--- a/lib/examples/client.conf
+++ b/lib/examples/client.conf
@@ -1,4 +1,4 @@
-config blocking {
+config blocking-udp {
type = "UDP"
server {
hostname = "localhost"
@@ -8,3 +8,23 @@ config blocking {
tries = 10 /* optional */
}
}
+config blocking-tls {
+ type = "TLS"
+
+ cacertfile = "/home/linus/nordberg-ca.crt"
+ #cacertpath =
+ certfile = "/home/linus/p/radsecproxy/src/maatuska.nordberg.se.crt"
+ certkeyfile = "/home/linus/p/radsecproxy/src/maatuska.nordberg.se.key"
+ #certkeypwd = "passphrase"
+ #cacheexpiry = <seconds>
+ #crlcheck = "on" | "off"
+ #policyoids = ?
+
+ server {
+ hostname = "localhost"
+ service = "4433"
+ secret = "sikrit"
+ timeout = 1 /* optional */
+ tries = 10 /* optional */
+ }
+}
diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h
index 6e5ee83..2b3d878 100644
--- a/lib/include/radsec/radsec-impl.h
+++ b/lib/include/radsec/radsec-impl.h
@@ -5,6 +5,9 @@
#include <freeradius/libradius.h>
#include <event2/util.h>
+#if defined(RS_ENABLE_TLS)
+#include <openssl/ssl.h>
+#endif
/* Constants. */
#define RS_HEADER_LEN 4
@@ -32,6 +35,7 @@ struct rs_error {
struct rs_peer {
struct rs_connection *conn;
+ struct rs_realm *realm;
struct evutil_addrinfo *addr;
int fd; /* Socket. */
char is_connecting; /* FIXME: replace with a single state member */
@@ -45,6 +49,10 @@ struct rs_peer {
struct rs_realm {
char *name;
enum rs_conn_type type;
+ char *cacertfile;
+ char *cacertpath;
+ char *certfile;
+ char *certkeyfile;
struct rs_peer *peers;
struct rs_realm *next;
};
@@ -69,6 +77,10 @@ struct rs_connection {
struct rs_error *err;
int nextid;
int user_dispatch_flag : 1; /* User does the dispatching. */
+#if defined(RS_ENABLE_TLS)
+ SSL_CTX *tls_ctx;
+ SSL *tls_ssl;
+#endif
};
struct rs_packet {
diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h
index 8e97072..d80c296 100644
--- a/lib/include/radsec/radsec.h
+++ b/lib/include/radsec/radsec.h
@@ -18,6 +18,8 @@ enum rs_err_code {
RSE_CONNERR = 10,
RSE_CONFIG = 11,
RSE_BADAUTH = 12,
+ RSE_INTERNAL = 13,
+ RSE_SSLERR = 14,
RSE_SOME_ERROR = 21,
};
diff --git a/lib/packet.c b/lib/packet.c
index cdd094a..154abc8 100644
--- a/lib/packet.c
+++ b/lib/packet.c
@@ -1,12 +1,22 @@
/* See the file COPYING for licensing information. */
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <freeradius/libradius.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
+#if defined RS_ENABLE_TLS
+#include <event2/bufferevent_ssl.h>
+#include <openssl/err.h>
+#endif
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
+#include "tls.h"
#if defined DEBUG
#include <netdb.h>
#include <sys/socket.h>
@@ -89,7 +99,7 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)
struct rs_packet *pkt = (struct rs_packet *)ctx;
struct rs_connection *conn;
struct rs_peer *p;
- int err;
+ unsigned long err;
assert (pkt);
assert (pkt->conn);
@@ -103,7 +113,6 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)
p->is_connected = 1;
if (conn->callbacks.connected_cb)
conn->callbacks.connected_cb (conn->user_data);
-
#if defined (DEBUG)
fprintf (stderr, "%s: connected\n", __func__);
#endif
@@ -114,7 +123,24 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)
/* Packet will be freed in write callback. */
}
else if (events & BEV_EVENT_ERROR)
- rs_err_conn_push_fl (pkt->conn, RSE_CONNERR, __FILE__, __LINE__, NULL);
+ {
+#if defined RS_ENABLE_TLS
+ if (conn->tls_ssl) /* FIXME: correct check? */
+ {
+ for (err = bufferevent_get_openssl_error (conn->bev);
+ err;
+ err = bufferevent_get_openssl_error (conn->bev))
+ {
+ fprintf (stderr, "%s: openssl error: %s\n", __func__,
+ ERR_error_string (err, NULL)); /* DEBUG, until verified that pushed errors will actually be handled */
+ rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__,
+ "%d", err);
+ }
+ }
+#endif
+ rs_err_conn_push_fl (pkt->conn, RSE_CONNERR, __FILE__, __LINE__, NULL);
+ fprintf (stderr, "%s: BEV_EVENT_ERROR\n", __func__); /* DEBUG, until verified that pushed errors will actually be handled */
+ }
}
static void
@@ -301,13 +327,41 @@ _pick_peer (struct rs_connection *conn)
static int
_init_bev (struct rs_connection *conn, struct rs_peer *peer)
{
- if (!conn->bev)
+ if (conn->bev)
+ return RSE_OK;
+
+ switch (conn->type)
{
+ case RS_CONN_TYPE_UDP:
+ case RS_CONN_TYPE_TCP:
conn->bev = bufferevent_socket_new (conn->evb, peer->fd, 0);
if (!conn->bev)
return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
"bufferevent_socket_new");
+ break;
+ case RS_CONN_TYPE_TLS:
+ if (rs_tls_init (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
+ SSL_set_bio() after BIO_new_socket() with flag=1. */
+ conn->bev =
+ bufferevent_openssl_socket_new (conn->evb, peer->fd, conn->tls_ssl,
+ BUFFEREVENT_SSL_CONNECTING, 0);
+ if (!conn->bev)
+ return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "bufferevent_openssl_socket_new");
+
+ break;
+ case RS_CONN_TYPE_DTLS:
+ return rs_err_conn_push_fl (conn, RSE_NOSYS, __FILE__, __LINE__,
+ "%s: NYI", __func__);
+ default:
+ return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__,
+ "%s: invalid connection type: %d", __func__,
+ conn->type);
}
+
return RSE_OK;
}
diff --git a/lib/radsec.c b/lib/radsec.c
index b7ac9ba..40d14fc 100644
--- a/lib/radsec.c
+++ b/lib/radsec.c
@@ -1,5 +1,9 @@
/* See the file COPYING for licensing information. */
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
@@ -12,6 +16,12 @@
#include <event2/util.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
+#if defined RS_ENABLE_TLS
+#include <regex.h>
+#include "rsp_list.h"
+#include "../radsecproxy.h"
+#endif
+#include "rsp_debug.h"
int
rs_context_create(struct rs_context **ctx, const char *dict)
@@ -48,10 +58,14 @@ rs_context_create(struct rs_context **ctx, const char *dict)
}
free (buf1);
free (buf2);
+#if defined RS_ENABLE_TLS
+ ssl_init ();
+#endif
#if defined (DEBUG)
fr_log_fp = stderr;
fr_debug_flag = 1;
#endif
+ debug_init ("libradsec"); /* radsecproxy compat, FIXME: remove */
memset (h, 0, sizeof(struct rs_context));
fr_randinit (&h->fr_randctx, 0);
diff --git a/lib/request.c b/lib/request.c
index 5cb87bb..bc6f795 100644
--- a/lib/request.c
+++ b/lib/request.c
@@ -1,5 +1,9 @@
/* See the file COPYING for licensing information. */
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <time.h>
#include <assert.h>
#include <event2/event.h>
@@ -39,25 +43,25 @@ _timer_cb(evutil_socket_t fd, short what, void *arg)
static void
_rs_req_connected(void *user_data)
{
- struct rs_request *request = (struct rs_request *)user_data;
+ //struct rs_request *request = (struct rs_request *)user_data;
}
static void
_rs_req_disconnected(void *user_data)
{
- struct rs_request *request = (struct rs_request *)user_data;
+ //struct rs_request *request = (struct rs_request *)user_data;
}
static void
_rs_req_packet_received(const struct rs_packet *pkt, void *user_data)
{
- struct rs_request *request = (struct rs_request *)user_data;
+ //struct rs_request *request = (struct rs_request *)user_data;
}
static void
_rs_req_packet_sent(void *user_data)
{
- struct rs_request *request = (struct rs_request *)user_data;
+ //struct rs_request *request = (struct rs_request *)user_data;
}
int
diff --git a/lib/rsp_debug.c b/lib/rsp_debug.c
new file mode 100644
index 0000000..dd7c053
--- /dev/null
+++ b/lib/rsp_debug.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2007 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#ifndef SYS_SOLARIS9
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <errno.h>
+#include <assert.h>
+#include "rsp_debug.h"
+#include "rsp_util.h"
+
+static char *debug_ident = NULL;
+static uint8_t debug_level = DBG_INFO;
+static char *debug_filepath = NULL;
+static FILE *debug_file = NULL;
+static int debug_syslogfacility = 0;
+static uint8_t debug_timestamp = 0;
+
+void debug_init(char *ident) {
+ debug_file = stderr;
+ setvbuf(debug_file, NULL, _IONBF, 0);
+ debug_ident = ident;
+}
+
+void debug_set_level(uint8_t level) {
+ switch (level) {
+ case 1:
+ debug_level = DBG_ERR;
+ return;
+ case 2:
+ debug_level = DBG_WARN;
+ return;
+ case 3:
+ debug_level = DBG_NOTICE;
+ return;
+ case 4:
+ debug_level = DBG_INFO;
+ return;
+ case 5:
+ debug_level = DBG_DBG;
+ return;
+ }
+}
+
+void debug_timestamp_on() {
+ debug_timestamp = 1;
+}
+
+uint8_t debug_get_level() {
+ return debug_level;
+}
+
+int debug_set_destination(char *dest) {
+ static const char *facstrings[] = { "LOG_DAEMON", "LOG_MAIL", "LOG_USER", "LOG_LOCAL0",
+ "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4",
+ "LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", NULL };
+ static const int facvals[] = { LOG_DAEMON, LOG_MAIL, LOG_USER, LOG_LOCAL0,
+ LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
+ LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7 };
+ extern int errno;
+ int i;
+
+ if (!strncasecmp(dest, "file:///", 8)) {
+ debug_filepath = stringcopy(dest + 7, 0);
+ debug_file = fopen(debug_filepath, "a");
+ if (!debug_file) {
+ debug_file = stderr;
+ debugx(1, DBG_ERR, "Failed to open logfile %s\n%s",
+ debug_filepath, strerror(errno));
+ }
+ setvbuf(debug_file, NULL, _IONBF, 0);
+ return 1;
+ }
+ if (!strncasecmp(dest, "x-syslog://", 11)) {
+ dest += 11;
+ if (*dest == '/')
+ dest++;
+ if (*dest) {
+ for (i = 0; facstrings[i]; i++)
+ if (!strcasecmp(dest, facstrings[i]))
+ break;
+ if (!facstrings[i])
+ debugx(1, DBG_ERR, "Unknown syslog facility %s", dest);
+ debug_syslogfacility = facvals[i];
+ } else
+ debug_syslogfacility = LOG_DAEMON;
+ openlog(debug_ident, LOG_PID, debug_syslogfacility);
+ return 1;
+ }
+ debug(DBG_ERR, "Unknown log destination, exiting %s", dest);
+ exit(1);
+}
+
+void debug_reopen_log() {
+ extern int errno;
+
+ /* not a file, noop, return success */
+ if (!debug_filepath) {
+ debug(DBG_ERR, "skipping reopen");
+ return;
+ }
+
+ if (debug_file != stderr)
+ fclose(debug_file);
+
+ debug_file = fopen(debug_filepath, "a");
+ if (debug_file)
+ debug(DBG_ERR, "Reopened logfile %s", debug_filepath);
+ else {
+ debug_file = stderr;
+ debug(DBG_ERR, "Failed to open logfile %s, using stderr\n%s",
+ debug_filepath, strerror(errno));
+ }
+ setvbuf(debug_file, NULL, _IONBF, 0);
+}
+
+void debug_logit(uint8_t level, const char *format, va_list ap) {
+ struct timeval now;
+ char *timebuf;
+ int priority;
+
+ if (debug_syslogfacility) {
+ switch (level) {
+ case DBG_DBG:
+ priority = LOG_DEBUG;
+ break;
+ case DBG_INFO:
+ priority = LOG_INFO;
+ break;
+ case DBG_NOTICE:
+ priority = LOG_NOTICE;
+ break;
+ case DBG_WARN:
+ priority = LOG_WARNING;
+ break;
+ case DBG_ERR:
+ priority = LOG_ERR;
+ break;
+ default:
+ priority = LOG_DEBUG;
+ }
+ vsyslog(priority, format, ap);
+ } else {
+ if (debug_timestamp && (timebuf = malloc(256))) {
+ gettimeofday(&now, NULL);
+ ctime_r(&now.tv_sec, timebuf);
+ timebuf[strlen(timebuf) - 1] = '\0';
+ fprintf(debug_file, "%s: ", timebuf + 4);
+ free(timebuf);
+ }
+ vfprintf(debug_file, format, ap);
+ fprintf(debug_file, "\n");
+ }
+}
+
+void debug(uint8_t level, char *format, ...) {
+ va_list ap;
+ if (level < debug_level)
+ return;
+ va_start(ap, format);
+ debug_logit(level, format, ap);
+ va_end(ap);
+}
+
+void debugx(int status, uint8_t level, char *format, ...) {
+ if (level >= debug_level) {
+ va_list ap;
+ va_start(ap, format);
+ debug_logit(level, format, ap);
+ va_end(ap);
+ }
+ exit(status);
+}
+
+void debugerrno(int err, uint8_t level, char *format, ...) {
+ if (level >= debug_level) {
+ va_list ap;
+ size_t len = strlen(format);
+ char *tmp = malloc(len + 1024 + 2);
+ assert(tmp);
+ strcpy(tmp, format);
+ tmp[len++] = ':';
+ tmp[len++] = ' ';
+ if (strerror_r(err, tmp + len, 1024))
+ tmp = format;
+ va_start(ap, format);
+ debug_logit(level, tmp, ap);
+ va_end(ap);
+ }
+}
+
+void debugerrnox(int err, uint8_t level, char *format, ...) {
+ if (level >= debug_level) {
+ va_list ap;
+ va_start(ap, format);
+ debugerrno(err, level, format, ap);
+ va_end(ap);
+ }
+ exit(err);
+}
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_debug.h b/lib/rsp_debug.h
new file mode 100644
index 0000000..803f406
--- /dev/null
+++ b/lib/rsp_debug.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#ifndef SYS_SOLARIS9
+#include <stdint.h>
+#endif
+
+#define DBG_DBG 8
+#define DBG_INFO 16
+#define DBG_NOTICE 32
+#define DBG_WARN 64
+#define DBG_ERR 128
+
+void debug_init(char *ident);
+void debug_set_level(uint8_t level);
+void debug_timestamp_on();
+uint8_t debug_get_level();
+void debug(uint8_t level, char *format, ...);
+void debugx(int status, uint8_t level, char *format, ...);
+void debugerrno(int err, uint8_t level, char *format, ...);
+void debugerrnox(int err, uint8_t level, char *format, ...);
+int debug_set_destination(char *dest);
+void debug_reopen_log();
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_hash.c b/lib/rsp_hash.c
new file mode 100644
index 0000000..cc37e7f
--- /dev/null
+++ b/lib/rsp_hash.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include "rsp_list.h"
+#include "rsp_hash.h"
+
+/* allocates and initialises hash structure; returns NULL if malloc fails */
+struct hash *hash_create() {
+ struct hash *h = malloc(sizeof(struct hash));
+ if (!h)
+ return NULL;
+ h->hashlist = list_create();
+ if (!h->hashlist) {
+ free(h);
+ return NULL;
+ }
+ pthread_mutex_init(&h->mutex, NULL);
+ return h;
+}
+
+/* frees all memory associated with the hash */
+void hash_destroy(struct hash *h) {
+ struct list_node *ln;
+
+ if (!h)
+ return;
+ for (ln = list_first(h->hashlist); ln; ln = list_next(ln)) {
+ free(((struct hash_entry *)ln->data)->key);
+ free(((struct hash_entry *)ln->data)->data);
+ }
+ list_destroy(h->hashlist);
+ pthread_mutex_destroy(&h->mutex);
+}
+
+/* insert entry in hash; returns 1 if ok, 0 if malloc fails */
+int hash_insert(struct hash *h, void *key, uint32_t keylen, void *data) {
+ struct hash_entry *e;
+
+ if (!h)
+ return 0;
+ e = malloc(sizeof(struct hash_entry));
+ if (!e)
+ return 0;
+ memset(e, 0, sizeof(struct hash_entry));
+ e->key = malloc(keylen);
+ if (!e->key) {
+ free(e);
+ return 0;
+ }
+ memcpy(e->key, key, keylen);
+ e->keylen = keylen;
+ e->data = data;
+ pthread_mutex_lock(&h->mutex);
+ if (!list_push(h->hashlist, e)) {
+ pthread_mutex_unlock(&h->mutex);
+ free(e->key);
+ free(e);
+ return 0;
+ }
+ pthread_mutex_unlock(&h->mutex);
+ return 1;
+}
+
+/* reads entry from hash */
+void *hash_read(struct hash *h, void *key, uint32_t keylen) {
+ struct list_node *ln;
+ struct hash_entry *e;
+
+ if (!h)
+ return 0;
+ pthread_mutex_lock(&h->mutex);
+ for (ln = list_first(h->hashlist); ln; ln = list_next(ln)) {
+ e = (struct hash_entry *)ln->data;
+ if (e->keylen == keylen && !memcmp(e->key, key, keylen)) {
+ pthread_mutex_unlock(&h->mutex);
+ return e->data;
+ }
+ }
+ pthread_mutex_unlock(&h->mutex);
+ return NULL;
+}
+
+/* extracts entry from hash */
+void *hash_extract(struct hash *h, void *key, uint32_t keylen) {
+ struct list_node *ln;
+ struct hash_entry *e;
+
+ if (!h)
+ return 0;
+ pthread_mutex_lock(&h->mutex);
+ for (ln = list_first(h->hashlist); ln; ln = list_next(ln)) {
+ e = (struct hash_entry *)ln->data;
+ if (e->keylen == keylen && !memcmp(e->key, key, keylen)) {
+ free(e->key);
+ list_removedata(h->hashlist, e);
+ free(e);
+ pthread_mutex_unlock(&h->mutex);
+ return e->data;
+ }
+ }
+ pthread_mutex_unlock(&h->mutex);
+ return NULL;
+}
+
+/* returns first entry */
+struct hash_entry *hash_first(struct hash *hash) {
+ struct list_node *ln;
+ struct hash_entry *e;
+ if (!hash || !((ln = list_first(hash->hashlist))))
+ return NULL;
+ e = (struct hash_entry *)ln->data;
+ e->next = ln->next;
+ return e;
+}
+
+/* returns the next node after the argument */
+struct hash_entry *hash_next(struct hash_entry *entry) {
+ struct hash_entry *e;
+ if (!entry || !entry->next)
+ return NULL;
+ e = (struct hash_entry *)entry->next->data;
+ e->next = (struct list_node *)entry->next->next;
+ return e;
+}
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_hash.h b/lib/rsp_hash.h
new file mode 100644
index 0000000..48f54a3
--- /dev/null
+++ b/lib/rsp_hash.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#ifndef SYS_SOLARIS9
+#include <stdint.h>
+#endif
+
+struct hash {
+ struct list *hashlist;
+ pthread_mutex_t mutex;
+};
+
+struct hash_entry {
+ void *key;
+ uint32_t keylen;
+ void *data;
+ struct list_node *next; /* used when walking through hash */
+};
+
+/* allocates and initialises hash structure; returns NULL if malloc fails */
+struct hash *hash_create();
+
+/* frees all memory associated with the hash */
+void hash_destroy(struct hash *hash);
+
+/* insert entry in hash; returns 1 if ok, 0 if malloc fails */
+int hash_insert(struct hash *hash, void *key, uint32_t keylen, void *data);
+
+/* reads entry from hash */
+void *hash_read(struct hash *hash, void *key, uint32_t keylen);
+
+/* extracts (read and remove) entry from hash */
+void *hash_extract(struct hash *hash, void *key, uint32_t keylen);
+
+/* returns first entry */
+struct hash_entry *hash_first(struct hash *hash);
+
+/* returns the next entry after the argument */
+struct hash_entry *hash_next(struct hash_entry *entry);
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_list.c b/lib/rsp_list.c
new file mode 100644
index 0000000..b6cfe33
--- /dev/null
+++ b/lib/rsp_list.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "rsp_list.h"
+
+/* allocates and initialises list structure; returns NULL if malloc fails */
+struct list *list_create() {
+ struct list *list = malloc(sizeof(struct list));
+ if (list)
+ memset(list, 0, sizeof(struct list));
+ return list;
+}
+
+/* frees all memory associated with the list */
+void list_destroy(struct list *list) {
+ struct list_node *node, *next;
+
+ if (!list)
+ return;
+
+ for (node = list->first; node; node = next) {
+ free(node->data);
+ next = node->next;
+ free(node);
+ }
+ free(list);
+}
+
+/* appends entry to list; returns 1 if ok, 0 if malloc fails */
+int list_push(struct list *list, void *data) {
+ struct list_node *node;
+
+ node = malloc(sizeof(struct list_node));
+ if (!node)
+ return 0;
+
+ node->next = NULL;
+ node->data = data;
+
+ if (list->first)
+ list->last->next = node;
+ else
+ list->first = node;
+ list->last = node;
+
+ list->count++;
+ return 1;
+}
+
+/* removes first entry from list and returns data */
+void *list_shift(struct list *list) {
+ struct list_node *node;
+ void *data;
+
+ if (!list || !list->first)
+ return NULL;
+
+ node = list->first;
+ list->first = node->next;
+ if (!list->first)
+ list->last = NULL;
+ data = node->data;
+ free(node);
+ list->count--;
+ return data;
+}
+
+/* removes all entries with matching data pointer */
+void list_removedata(struct list *list, void *data) {
+ struct list_node *node, *t;
+
+ if (!list || !list->first)
+ return;
+
+ node = list->first;
+ while (node->data == data) {
+ list->first = node->next;
+ free(node);
+ list->count--;
+ node = list->first;
+ if (!node) {
+ list->last = NULL;
+ return;
+ }
+ }
+ for (; node->next; node = node->next)
+ if (node->next->data == data) {
+ t = node->next;
+ node->next = t->next;
+ free(t);
+ list->count--;
+ if (!node->next) { /* we removed the last one */
+ list->last = node;
+ return;
+ }
+ }
+}
+
+/* returns first node */
+struct list_node *list_first(struct list *list) {
+ return list ? list->first : NULL;
+}
+
+/* returns the next node after the argument */
+struct list_node *list_next(struct list_node *node) {
+ return node->next;
+}
+
+/* returns number of nodes */
+uint32_t list_count(struct list *list) {
+ return list->count;
+}
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_list.h b/lib/rsp_list.h
new file mode 100644
index 0000000..80c0128
--- /dev/null
+++ b/lib/rsp_list.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#ifdef SYS_SOLARIS9
+#include <sys/inttypes.h>
+#else
+#include <stdint.h>
+#endif
+
+struct list_node {
+ struct list_node *next;
+ void *data;
+};
+
+struct list {
+ struct list_node *first, *last;
+ uint32_t count;
+};
+
+/* allocates and initialises list structure; returns NULL if malloc fails */
+struct list *list_create();
+
+/* frees all memory associated with the list */
+void list_destroy(struct list *list);
+
+/* appends entry to list; returns 1 if ok, 0 if malloc fails */
+int list_push(struct list *list, void *data);
+
+/* removes first entry from list and returns data */
+void *list_shift(struct list *list);
+
+/* removes first entry with matching data pointer */
+void list_removedata(struct list *list, void *data);
+
+/* returns first node */
+struct list_node *list_first(struct list *list);
+
+/* returns the next node after the argument */
+struct list_node *list_next(struct list_node *node);
+
+/* returns number of nodes */
+uint32_t list_count(struct list *list);
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_tlscommon.c b/lib/rsp_tlscommon.c
new file mode 100644
index 0000000..6002c41
--- /dev/null
+++ b/lib/rsp_tlscommon.c
@@ -0,0 +1,664 @@
+/*
+ * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#ifdef SYS_SOLARIS9
+#include <fcntl.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <regex.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <openssl/ssl.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/md5.h>
+#include <openssl/x509v3.h>
+#include "rsp_debug.h"
+#include "rsp_list.h"
+#include "rsp_hash.h"
+#include "rsp_util.h"
+#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 0;
+ memcpy(buf, userdata, pwdlen);
+ return pwdlen;
+}
+
+static int verify_cb(int ok, X509_STORE_CTX *ctx) {
+ char *buf = NULL;
+ X509 *err_cert;
+ int err, depth;
+
+ err_cert = X509_STORE_CTX_get_current_cert(ctx);
+ err = X509_STORE_CTX_get_error(ctx);
+ depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ if (depth > MAX_CERT_DEPTH) {
+ ok = 0;
+ err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ X509_STORE_CTX_set_error(ctx, err);
+ }
+
+ if (!ok) {
+ if (err_cert)
+ buf = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0);
+ debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf ? buf : "");
+ free(buf);
+ buf = NULL;
+
+ switch (err) {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ if (err_cert) {
+ buf = X509_NAME_oneline(X509_get_issuer_name(err_cert), NULL, 0);
+ if (buf) {
+ debug(DBG_WARN, "\tIssuer=%s", buf);
+ free(buf);
+ buf = NULL;
+ }
+ }
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ debug(DBG_WARN, "\tCertificate not yet valid");
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ debug(DBG_WARN, "Certificate has expired");
+ break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ debug(DBG_WARN, "Certificate no longer valid (after notAfter)");
+ break;
+ case X509_V_ERR_NO_EXPLICIT_POLICY:
+ debug(DBG_WARN, "No Explicit Certificate Policy");
+ break;
+ }
+ }
+#ifdef DEBUG
+ printf("certificate verify returns %d\n", ok);
+#endif
+ return ok;
+}
+
+#ifdef DEBUG
+static void ssl_info_callback(const SSL *ssl, int where, int ret) {
+ const char *s;
+ int w;
+
+ w = where & ~SSL_ST_MASK;
+
+ if (w & SSL_ST_CONNECT)
+ s = "SSL_connect";
+ else if (w & SSL_ST_ACCEPT)
+ s = "SSL_accept";
+ else
+ s = "undefined";
+
+ if (where & SSL_CB_LOOP)
+ debug(DBG_DBG, "%s:%s\n", s, SSL_state_string_long(ssl));
+ else if (where & SSL_CB_ALERT) {
+ s = (where & SSL_CB_READ) ? "read" : "write";
+ debug(DBG_DBG, "SSL3 alert %s:%s:%s\n", s, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+ }
+ else if (where & SSL_CB_EXIT) {
+ if (ret == 0)
+ debug(DBG_DBG, "%s:failed in %s\n", s, SSL_state_string_long(ssl));
+ else if (ret < 0)
+ debug(DBG_DBG, "%s:error in %s\n", s, SSL_state_string_long(ssl));
+ }
+}
+#endif
+
+static X509_VERIFY_PARAM *createverifyparams(char **poids) {
+ X509_VERIFY_PARAM *pm;
+ ASN1_OBJECT *pobject;
+ int i;
+
+ pm = X509_VERIFY_PARAM_new();
+ if (!pm)
+ return NULL;
+
+ for (i = 0; poids[i]; i++) {
+ pobject = OBJ_txt2obj(poids[i], 0);
+ if (!pobject) {
+ X509_VERIFY_PARAM_free(pm);
+ return NULL;
+ }
+ X509_VERIFY_PARAM_add0_policy(pm, pobject);
+ }
+
+ X509_VERIFY_PARAM_set_flags(pm, X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY);
+ return pm;
+}
+
+static int tlsaddcacrl(SSL_CTX *ctx, struct tls *conf) {
+ STACK_OF(X509_NAME) *calist;
+ X509_STORE *x509_s;
+ unsigned long error;
+
+ if (!SSL_CTX_load_verify_locations(ctx, conf->cacertfile, conf->cacertpath)) {
+ while ((error = ERR_get_error()))
+ debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
+ debug(DBG_ERR, "tlsaddcacrl: Error updating TLS context %s", conf->name);
+ return 0;
+ }
+
+ calist = conf->cacertfile ? SSL_load_client_CA_file(conf->cacertfile) : NULL;
+
+ if (!conf->cacertfile || calist) {
+ if (conf->cacertpath) {
+ if (!calist)
+ calist = sk_X509_NAME_new_null();
+ if (!SSL_add_dir_cert_subjects_to_stack(calist, conf->cacertpath)) {
+ sk_X509_NAME_free(calist);
+ calist = NULL;
+ }
+ }
+ }
+ if (!calist) {
+ while ((error = ERR_get_error()))
+ debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
+ debug(DBG_ERR, "tlsaddcacrl: Error adding CA subjects in TLS context %s", conf->name);
+ return 0;
+ }
+ ERR_clear_error(); /* add_dir_cert_subj returns errors on success */
+ SSL_CTX_set_client_CA_list(ctx, calist);
+
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
+ SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1);
+
+ if (conf->crlcheck || conf->vpm) {
+ x509_s = SSL_CTX_get_cert_store(ctx);
+ if (conf->crlcheck)
+ X509_STORE_set_flags(x509_s, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+ if (conf->vpm)
+ X509_STORE_set1_param(x509_s, conf->vpm);
+ }
+
+ debug(DBG_DBG, "tlsaddcacrl: updated TLS context %s", conf->name);
+ return 1;
+}
+
+static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) {
+ SSL_CTX *ctx = NULL;
+ unsigned long error;
+
+ switch (type) {
+#ifdef RADPROT_TLS
+ case RAD_TLS:
+ ctx = SSL_CTX_new(TLSv1_method());
+ break;
+#endif
+#ifdef RADPROT_DTLS
+ case RAD_DTLS:
+ ctx = SSL_CTX_new(DTLSv1_method());
+ SSL_CTX_set_read_ahead(ctx, 1);
+ break;
+#endif
+ }
+ if (!ctx) {
+ debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name);
+ while ((error = ERR_get_error()))
+ debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
+ return NULL;
+ }
+#ifdef DEBUG
+ SSL_CTX_set_info_callback(ctx, ssl_info_callback);
+#endif
+
+ if (conf->certkeypwd) {
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->certkeypwd);
+ SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
+ }
+ if (conf->certfile || conf->certkeyfile) {
+ if (!SSL_CTX_use_certificate_chain_file(ctx, conf->certfile) ||
+ !SSL_CTX_use_PrivateKey_file(ctx, conf->certkeyfile, SSL_FILETYPE_PEM) ||
+ !SSL_CTX_check_private_key(ctx)) {
+ while ((error = ERR_get_error()))
+ debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
+ debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS (certfile issues) in TLS context %s", conf->name);
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+ }
+
+ if (conf->policyoids) {
+ if (!conf->vpm) {
+ conf->vpm = createverifyparams(conf->policyoids);
+ if (!conf->vpm) {
+ debug(DBG_ERR, "tlscreatectx: Failed to add policyOIDs in TLS context %s", conf->name);
+ SSL_CTX_free(ctx);
+ return 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;
+}
+
+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;
+
+ if (!t)
+ return NULL;
+ gettimeofday(&now, NULL);
+
+ switch (type) {
+#ifdef RADPROT_TLS
+ case RAD_TLS:
+ if (t->tlsexpiry && t->tlsctx) {
+ if (t->tlsexpiry < now.tv_sec) {
+ t->tlsexpiry = now.tv_sec + t->cacheexpiry;
+ tlsaddcacrl(t->tlsctx, t);
+ }
+ }
+ if (!t->tlsctx) {
+ t->tlsctx = tlscreatectx(RAD_TLS, t);
+ if (t->cacheexpiry)
+ t->tlsexpiry = now.tv_sec + t->cacheexpiry;
+ }
+ return t->tlsctx;
+#endif
+#ifdef RADPROT_DTLS
+ case RAD_DTLS:
+ if (t->dtlsexpiry && t->dtlsctx) {
+ if (t->dtlsexpiry < now.tv_sec) {
+ t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
+ tlsaddcacrl(t->dtlsctx, t);
+ }
+ }
+ if (!t->dtlsctx) {
+ t->dtlsctx = tlscreatectx(RAD_DTLS, t);
+ if (t->cacheexpiry)
+ t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
+ }
+ return t->dtlsctx;
+#endif
+ }
+ return NULL;
+}
+
+X509 *verifytlscert(SSL *ssl) {
+ X509 *cert;
+ unsigned long error;
+
+ if (SSL_get_verify_result(ssl) != X509_V_OK) {
+ debug(DBG_ERR, "verifytlscert: basic validation failed");
+ while ((error = ERR_get_error()))
+ debug(DBG_ERR, "verifytlscert: TLS: %s", ERR_error_string(error, NULL));
+ return NULL;
+ }
+
+ cert = SSL_get_peer_certificate(ssl);
+ if (!cert)
+ debug(DBG_ERR, "verifytlscert: failed to obtain certificate");
+ return cert;
+}
+
+static int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) {
+ int loc, i, l, n, r = 0;
+ char *v;
+ X509_EXTENSION *ex;
+ STACK_OF(GENERAL_NAME) *alt;
+ GENERAL_NAME *gn;
+
+ debug(DBG_DBG, "subjectaltnameaddr");
+
+ loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
+ if (loc < 0)
+ return r;
+
+ ex = X509_get_ext(cert, loc);
+ alt = X509V3_EXT_d2i(ex);
+ if (!alt)
+ return r;
+
+ n = sk_GENERAL_NAME_num(alt);
+ for (i = 0; i < n; i++) {
+ gn = sk_GENERAL_NAME_value(alt, i);
+ if (gn->type != GEN_IPADD)
+ continue;
+ r = -1;
+ v = (char *)ASN1_STRING_data(gn->d.ia5);
+ l = ASN1_STRING_length(gn->d.ia5);
+ if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr)))
+ && !memcmp(v, &addr, l)) {
+ r = 1;
+ break;
+ }
+ }
+ GENERAL_NAMES_free(alt);
+ return r;
+}
+
+static int subjectaltnameregexp(X509 *cert, int type, char *exact, regex_t *regex) {
+ int loc, i, l, n, r = 0;
+ char *s, *v;
+ X509_EXTENSION *ex;
+ STACK_OF(GENERAL_NAME) *alt;
+ GENERAL_NAME *gn;
+
+ debug(DBG_DBG, "subjectaltnameregexp");
+
+ loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
+ if (loc < 0)
+ return r;
+
+ ex = X509_get_ext(cert, loc);
+ alt = X509V3_EXT_d2i(ex);
+ if (!alt)
+ return r;
+
+ n = sk_GENERAL_NAME_num(alt);
+ for (i = 0; i < n; i++) {
+ gn = sk_GENERAL_NAME_value(alt, i);
+ if (gn->type != type)
+ continue;
+ r = -1;
+ v = (char *)ASN1_STRING_data(gn->d.ia5);
+ l = ASN1_STRING_length(gn->d.ia5);
+ if (l <= 0)
+ continue;
+#ifdef DEBUG
+ printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, v, l);
+#endif
+ if (exact) {
+ if (memcmp(v, exact, l))
+ continue;
+ } else {
+ s = stringcopy((char *)v, l);
+ if (!s) {
+ debug(DBG_ERR, "malloc failed");
+ continue;
+ }
+ if (regexec(regex, s, 0, NULL, 0)) {
+ free(s);
+ continue;
+ }
+ free(s);
+ }
+ r = 1;
+ break;
+ }
+ GENERAL_NAMES_free(alt);
+ return r;
+}
+
+static int cnregexp(X509 *cert, char *exact, regex_t *regex) {
+ int loc, l;
+ char *v, *s;
+ X509_NAME *nm;
+ X509_NAME_ENTRY *e;
+ ASN1_STRING *t;
+
+ nm = X509_get_subject_name(cert);
+ loc = -1;
+ for (;;) {
+ loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc);
+ if (loc == -1)
+ break;
+ e = X509_NAME_get_entry(nm, loc);
+ t = X509_NAME_ENTRY_get_data(e);
+ v = (char *) ASN1_STRING_data(t);
+ l = ASN1_STRING_length(t);
+ if (l < 0)
+ continue;
+ if (exact) {
+ if (l == strlen(exact) && !strncasecmp(exact, v, l))
+ return 1;
+ } else {
+ s = stringcopy((char *)v, l);
+ if (!s) {
+ debug(DBG_ERR, "malloc failed");
+ continue;
+ }
+ if (regexec(regex, s, 0, NULL, 0)) {
+ free(s);
+ continue;
+ }
+ free(s);
+ return 1;
+ }
+ }
+ 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;
+}
+
+#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
new file mode 100644
index 0000000..d7e0930
--- /dev/null
+++ b/lib/rsp_tlscommon.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include <openssl/ssl.h>
+
+struct tls {
+ char *name;
+ char *cacertfile;
+ char *cacertpath;
+ char *certfile;
+ char *certkeyfile;
+ char *certkeypwd;
+ uint8_t crlcheck;
+ char **policyoids;
+ uint32_t cacheexpiry;
+ uint32_t tlsexpiry;
+ uint32_t dtlsexpiry;
+ X509_VERIFY_PARAM *vpm;
+ SSL_CTX *tlsctx;
+ SSL_CTX *dtlsctx;
+};
+
+#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 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
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_util.c b/lib/rsp_util.c
new file mode 100644
index 0000000..22b8352
--- /dev/null
+++ b/lib/rsp_util.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+/* Code contributions from:
+ *
+ * Stefan Winter <stefan.winter@restena.lu>
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <stdarg.h>
+#include "rsp_debug.h"
+#include "rsp_util.h"
+
+char *stringcopy(const char *s, int len) {
+ char *r;
+ if (!s)
+ return NULL;
+ if (!len)
+ len = strlen(s);
+ r = malloc(len + 1);
+ if (!r)
+ debug(DBG_ERR, "stringcopy: malloc failed");
+ memcpy(r, s, len);
+ r[len] = '\0';
+ return r;
+}
+
+void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len) {
+ int i;
+ unsigned char *s = (unsigned char *)chars;
+ if (prefix)
+ printf(prefixfmt ? prefixfmt : "%s: ", prefix);
+ for (i = 0; i < len; i++)
+ printf(charfmt ? charfmt : "%c", s[i]);
+ printf("\n");
+}
+
+void port_set(struct sockaddr *sa, uint16_t port) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)sa)->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
+ break;
+ }
+}
+
+struct sockaddr *addr_copy(struct sockaddr *in) {
+ struct sockaddr *out = NULL;
+
+ switch (in->sa_family) {
+ case AF_INET:
+ out = malloc(sizeof(struct sockaddr_in));
+ if (out) {
+ memset(out, 0, sizeof(struct sockaddr_in));
+ ((struct sockaddr_in *)out)->sin_addr = ((struct sockaddr_in *)in)->sin_addr;
+ }
+ break;
+ case AF_INET6:
+ out = malloc(sizeof(struct sockaddr_in6));
+ if (out) {
+ memset(out, 0, sizeof(struct sockaddr_in6));
+ ((struct sockaddr_in6 *)out)->sin6_addr = ((struct sockaddr_in6 *)in)->sin6_addr;
+ }
+ break;
+ }
+ out->sa_family = in->sa_family;
+#ifdef SIN6_LEN
+ out->sa_len = in->sa_len;
+#endif
+ return out;
+}
+
+char *addr2string(struct sockaddr *addr) {
+ struct sockaddr_in6 *sa6;
+ struct sockaddr_in sa4;
+ static char addr_buf[2][INET6_ADDRSTRLEN];
+ static int i = 0;
+ i = !i;
+ if (addr->sa_family == AF_INET6) {
+ sa6 = (struct sockaddr_in6 *)addr;
+ if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ sa4.sin_port = sa6->sin6_port;
+ memcpy(&sa4.sin_addr, &sa6->sin6_addr.s6_addr[12], 4);
+ addr = (struct sockaddr *)&sa4;
+ }
+ }
+ if (getnameinfo(addr, SOCKADDRP_SIZE(addr), addr_buf[i], sizeof(addr_buf[i]),
+ NULL, 0, NI_NUMERICHOST)) {
+ debug(DBG_WARN, "getnameinfo failed");
+ return "getnameinfo_failed";
+ }
+ return addr_buf[i];
+}
+
+#if 0
+/* not in use */
+int connectport(int type, char *host, char *port) {
+ struct addrinfo hints, *res0, *res;
+ int s = -1;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = type;
+ hints.ai_family = AF_UNSPEC;
+
+ if (getaddrinfo(host, port, &hints, &res0) != 0) {
+ debug(DBG_ERR, "connectport: can't resolve host %s port %s", host, port);
+ return -1;
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s < 0) {
+ debug(DBG_WARN, "connectport: socket failed");
+ continue;
+ }
+ if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+ debug(DBG_WARN, "connectport: connect failed");
+ close(s);
+ s = -1;
+ }
+ freeaddrinfo(res0);
+ return s;
+}
+#endif
+
+/* Disable the "Don't Fragment" bit for UDP sockets. It is set by default, which may cause an "oversized"
+ RADIUS packet to be discarded on first attempt (due to Path MTU discovery).
+*/
+
+void disable_DF_bit(int socket, struct addrinfo *res) {
+ if ((res->ai_family == AF_INET) && (res->ai_socktype == SOCK_DGRAM)) {
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ /*
+ * Turn off Path MTU discovery on IPv4/UDP sockets, Linux variant.
+ */
+ int r, action;
+ debug(DBG_INFO, "disable_DF_bit: disabling DF bit (Linux variant)");
+ action = IP_PMTUDISC_DONT;
+ r = setsockopt(socket, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action));
+ if (r == -1)
+ debug(DBG_WARN, "Failed to set IP_MTU_DISCOVER");
+#else
+ debug(DBG_INFO, "Non-Linux platform, unable to unset DF bit for UDP. You should check with tcpdump whether radsecproxy will send its UDP packets with DF bit set!");
+#endif
+ }
+}
+
+int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
+ int s, on = 1;
+ struct addrinfo *res;
+
+ for (res = addrinfo; res; res = res->ai_next) {
+ if (family != AF_UNSPEC && family != res->ai_family)
+ continue;
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s < 0) {
+ debug(DBG_WARN, "bindtoaddr: socket failed");
+ continue;
+ }
+
+ disable_DF_bit(s,res);
+
+ if (reuse)
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+#ifdef IPV6_V6ONLY
+ if (v6only)
+ setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+#endif
+ if (!bind(s, res->ai_addr, res->ai_addrlen))
+ return s;
+ debug(DBG_WARN, "bindtoaddr: bind failed");
+ close(s);
+ }
+ return -1;
+}
+
+int connectnonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout) {
+ int origflags, error = 0, r = -1;
+ fd_set writefds;
+ socklen_t len;
+
+ origflags = fcntl(s, F_GETFL, 0);
+ fcntl(s, F_SETFL, origflags | O_NONBLOCK);
+ if (!connect(s, addr, addrlen)) {
+ r = 0;
+ goto exit;
+ }
+ if (errno != EINPROGRESS)
+ goto exit;
+
+ FD_ZERO(&writefds);
+ FD_SET(s, &writefds);
+ if (select(s + 1, NULL, &writefds, NULL, timeout) < 1)
+ goto exit;
+
+ len = sizeof(error);
+ if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&error, &len) && !error)
+ r = 0;
+
+exit:
+ fcntl(s, F_SETFL, origflags);
+ return r;
+}
+
+int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) {
+ int s;
+ struct addrinfo *res;
+ struct timeval to;
+
+ s = -1;
+ if (timeout) {
+ if (addrinfo && addrinfo->ai_next && timeout > 5)
+ timeout = 5;
+ to.tv_sec = timeout;
+ to.tv_usec = 0;
+ }
+
+ for (res = addrinfo; res; res = res->ai_next) {
+ s = bindtoaddr(src, res->ai_family, 1, 1);
+ if (s < 0) {
+ debug(DBG_WARN, "connecttoserver: socket failed");
+ continue;
+ }
+ if ((timeout
+ ? connectnonblocking(s, res->ai_addr, res->ai_addrlen, &to)
+ : connect(s, res->ai_addr, res->ai_addrlen)) == 0)
+ break;
+ debug(DBG_WARN, "connecttoserver: connect failed");
+ close(s);
+ s = -1;
+ }
+ return s;
+}
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/rsp_util.h b/lib/rsp_util.h
new file mode 100644
index 0000000..d8d002c
--- /dev/null
+++ b/lib/rsp_util.h
@@ -0,0 +1,25 @@
+#include <sys/socket.h>
+#include <netdb.h>
+
+#define SOCKADDR_SIZE(addr) ((addr).ss_family == AF_INET ? \
+ sizeof(struct sockaddr_in) : \
+ sizeof(struct sockaddr_in6))
+
+#define SOCKADDRP_SIZE(addr) ((addr)->sa_family == AF_INET ? \
+ sizeof(struct sockaddr_in) : \
+ sizeof(struct sockaddr_in6))
+
+char *stringcopy(const char *s, int len);
+char *addr2string(struct sockaddr *addr);
+struct sockaddr *addr_copy(struct sockaddr *in);
+void port_set(struct sockaddr *sa, uint16_t port);
+
+void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len);
+void disable_DF_bit(int socket, struct addrinfo *res);
+int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only);
+int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout);
+
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */
diff --git a/lib/tls.c b/lib/tls.c
new file mode 100644
index 0000000..15929d2
--- /dev/null
+++ b/lib/tls.c
@@ -0,0 +1,73 @@
+/* See the file COPYING for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <openssl/ssl.h>
+#include <radsec/radsec.h>
+#include <radsec/radsec-impl.h>
+
+#include <regex.h>
+#include "rsp_list.h"
+#include "../radsecproxy.h"
+
+static struct tls *
+_get_tlsconf (const struct rs_context *ctx, const struct rs_realm *realm)
+{
+ struct tls *c = rs_malloc (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 */
+ }
+
+ return c;
+}
+
+int
+rs_tls_init (struct rs_connection *conn)
+{
+ struct rs_context *ctx;
+ struct tls *tlsconf;
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ assert (conn->ctx);
+ ctx = conn->ctx;
+
+ tlsconf = _get_tlsconf (ctx, conn->active_peer->realm);
+ assert (tlsconf);
+ ssl_ctx = tlsgetctx (RADPROT_TLS, tlsconf);
+ if (!ssl_ctx)
+ {
+ /* TODO: check radsecproxy error */
+ return rs_err_conn_push_fl (conn, RSE_SOME_ERROR, __FILE__, __LINE__,
+ NULL);
+ }
+
+ ssl = SSL_new (ssl_ctx);
+ if (!ssl)
+ {
+ /* TODO: check and report SSL error */
+ /* TODO: free ssl_ctx */
+ return rs_err_conn_push_fl (conn, RSE_SOME_ERROR, __FILE__, __LINE__,
+ NULL);
+ }
+
+ conn->tls_ctx = ssl_ctx;
+ conn->tls_ssl = ssl;
+ rs_free (ctx, tlsconf);
+ return RSE_OK;
+}
diff --git a/lib/tls.h b/lib/tls.h
new file mode 100644
index 0000000..7e10a46
--- /dev/null
+++ b/lib/tls.h
@@ -0,0 +1 @@
+int rs_tls_init (struct rs_connection *conn);