From: Linus Nordberg Date: Wed, 15 May 2013 12:20:52 +0000 (+0200) Subject: WIP commit moving towards working server support. X-Git-Url: http://www.project-moonshot.org/gitweb/?p=libradsec.git;a=commitdiff_plain;h=fba1c7d1a6418221a94965d0431bf7df0a9a74a0 WIP commit moving towards working server support. --- diff --git a/lib/HACKING b/lib/HACKING index d312ce3..62da414 100644 --- a/lib/HACKING +++ b/lib/HACKING @@ -1,6 +1,6 @@ HACKING file for libradsec (in Emacs -*- org -*- mode). -Status as of libradsec-0.0.2.dev (2013-01-25). +Status as of libradsec-0.2.0.dev (2013-05-02). * Build instructions sh autogen.sh @@ -9,43 +9,6 @@ make examples/client -r examples/client.conf blocking-tls; echo $? -* Design of the API -- There are three usage modes: - - - Application uses blocking send and receive calls (blocking - mode). This is typically fine for a simple client. - - - Application registers callbacks with libradsec and runs the - libevent dispatch loop (a.k.a. user dispatch mode). This would - probably be how one would implement a server or a proxy. - - - Application runs its own event loop, using fd's for select and - performs I/O using libradsec send/receive functions - (a.k.a. on-your-own mode). Might be useful for an application - which already has an event loop that wants to add RadSec - functionality. - -- Apart from configuration and error handling, an application - shouldn't need to handle TCP and UDP connections - differently. Similarly, the use of TLS/DTLS or not shouldn't - influence the libradsec calls made by the application. - -- Configuration is done either by using the API or by pointing at a - configuration file which is parsed by libradsec. - -- Fully reentrant. - -- Application 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 -thing will happen if there's an error on a TCP connection, f.ex. a -failing certificate validation (TLS). - * Dependencies Details (within parentheses) apply to Debian Wheezy. @@ -66,6 +29,8 @@ Details (within parentheses) apply to Debian Wheezy. - [TLS] verification of CN ** Known issues +- error handling when server can't bind to given listen_addr+port +- configuration of listen_addr/service is per realm - error stack is only one entry deep - custom allocation scheme is not used in all places @@ -89,3 +54,74 @@ a crash, catching the crash in gdb and providing a backtrace is highly valuable for debugging. Contact: mailto:linus+libradsec@nordu.net + + +* Design of the API +- There are three usage modes: + + - Application uses blocking send and receive calls (blocking + mode). This is typically fine for a simple client. + + - Application registers callbacks with libradsec and runs the + libevent dispatch loop (a.k.a. user dispatch mode). This would + probably be how one would implement a server or a proxy. + + - Application runs its own event loop, using fd's for select and + performs I/O using libradsec send/receive functions + (a.k.a. on-your-own mode). Might be useful for an application + which already has an event loop that wants to add RadSec + functionality. + +- Apart from configuration and error handling, an application + shouldn't need to handle TCP and UDP connections + differently. Similarly, the use of TLS/DTLS or not shouldn't + influence the libradsec calls made by the application. + +- Configuration is done either by using the API or by pointing at a + configuration file which is parsed by libradsec. + +- Fully reentrant. + +- Application 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 +thing will happen if there's an error on a TCP connection, f.ex. a +failing certificate validation (TLS). + +* Notes on internals +** How to connect an outgoing connection? +Connecting is not done explicitly by the application but implicitly by +rs_message_send(). The application can treat connections in the same +way regardless of whether they're connection-oriented (i.e. TCP) or +not (UDP). + +rs_message_send(msg) + if msg->conn is open: mesasge_do_send(msg) + else: + -> _conn_open(conn, msg) + pick configured peer + event_init_socket(peer) + if TCP or TLS: + init tcp timers + event_init_bufferevent(conn, peer) + else: + init udp timers + if not connected and not connecting: + event_do_connect(conn) + + if TCP: + bufferevent_setcb() + bufferevent_enable() + else: + event_assign(write_ev) ; libevent func? + event_add(write_ev) + + if not in user-dispatch-mode: + event_base_dispatch() +** How to bind a listener and start listening for incoming connections? + diff --git a/lib/Makefile.am b/lib/Makefile.am index 6d62c97..6a96d32 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -53,10 +53,10 @@ else endif EXTRA_DIST = HACKING LICENSE +EXTRA_libradsec_la_DEPENDENCIES = radsec.sym libradsec_la_CFLAGS = \ - $(AM_CFLAGS) -DHAVE_CONFIG_H \ - -DDEBUG -DDEBUG_LEVENT + $(AM_CFLAGS) -DHAVE_CONFIG_H -DDEBUG -DDEBUG_LEVENT libradsec_la_LDFLAGS = \ -version-info $(VER_CUR):$(VER_REV):$(VER_AGE) \ -export-symbols radsec.sym diff --git a/lib/README b/lib/README index 8b8c25d..6401333 100644 --- a/lib/README +++ b/lib/README @@ -19,7 +19,7 @@ http://git.nordu.net/?p=radsecproxy.git;a=shortlog;h=refs/heads/libradsec The source code is licensed under a 3-clause BSD license. See LICENSE. -Libradsec depends on +Libradsec depends on - libconfuse - libevent2 - openssl (if configured with --enable-tls) diff --git a/lib/conf.c b/lib/conf.c index f75e9ed..d5a9d0d 100644 --- a/lib/conf.c +++ b/lib/conf.c @@ -1,5 +1,5 @@ /* Copyright 2010, 2011, 2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H #include @@ -23,7 +23,7 @@ retries = INT } - # realm configuration inherited by clients and servers + # realm configuration inherited (and overwritable) by clients and servers realm STRING { cacertfile = STRING #cacertpath = STRING @@ -46,6 +46,8 @@ # server configuration realm STRING { + listen_addr = STRING + listen_service = STRING client { hostname = STRING service = STRING # name or port number @@ -82,6 +84,8 @@ confload_peers (struct rs_context *ctx, const char *peer_type_str[] = {"", "client", "server"}; cfg_t *cfg_peer = NULL; int j; + char *def_listen_addr = cfg_getstr (cfg_realm, "listen_addr"); + char *def_listen_service = cfg_getstr (cfg_realm, "listen_service"); char *def_cacertfile = cfg_getstr (cfg_realm, "cacertfile"); /*char *def_cacertpath = cfg_getstr (cfg_realm, "cacertpath");*/ char *def_certfile = cfg_getstr (cfg_realm, "certfile"); @@ -105,6 +109,24 @@ confload_peers (struct rs_context *ctx, p->service = cfg_getstr (cfg_peer, "service"); p->secret = cfg_getstr (cfg_peer, "secret"); + if (type == RS_PEER_TYPE_CLIENT) + { + struct rs_peer *lp = peer_create (ctx, &r->local_addr); /* FIXME: this should be saved per peer, not realm */ + if (lp == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, + NULL); + lp->realm = r; + fprintf (stderr, " *** %s %s ***\n", def_listen_addr, def_listen_service); +#if 0 + CONFGET_STR (lp->hostname, cfg_peer, "listen_addr", def_listen_addr); + CONFGET_STR (lp->service, cfg_peer, "listen_service", + def_listen_service); +#else + lp->hostname = "127.0.0.1"; + lp->service = "4711"; +#endif + } + CONFGET_STR (p->cacertfile, cfg_peer, "cacertfile", def_cacertfile); CONFGET_STR (p->certfile, cfg_peer, "certfile", def_certfile); CONFGET_STR (p->certkeyfile, cfg_peer, "certkeyfile", def_certkeyfile); @@ -161,7 +183,7 @@ confload_peers (struct rs_context *ctx, #endif /* RS_ENABLE_TLS_PSK */ } - +#if defined (RS_ENABLE_TLS) /* For a TLS or DTLS client or server, validate that we have either of CA cert file/path or PSK. */ if ((r->type == RS_CONN_TYPE_TLS || r->type == RS_CONN_TYPE_DTLS) @@ -170,6 +192,7 @@ confload_peers (struct rs_context *ctx, return rs_err_ctx_push (ctx, RSE_CONFIG, "%s: missing both CA file/path and PSK", r->name); +#endif } return RSE_OK; @@ -189,6 +212,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) { CFG_STR ("hostname", NULL, CFGF_NONE), CFG_STR ("service", "2083", CFGF_NONE), + CFG_STR ("listen_addr", "127.0.0.1", CFGF_NONE), /* Clients only. */ + CFG_STR ("listen_service", "0", CFGF_NONE), /* Clients only. */ CFG_STR ("secret", "radsec", CFGF_NONE), CFG_STR ("cacertfile", NULL, CFGF_NONE), /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/ @@ -204,6 +229,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) cfg_opt_t realm_opts[] = { CFG_STR ("type", "UDP", CFGF_NONE), + CFG_STR ("listen_addr", "127.0.0.1", CFGF_NONE), + CFG_STR ("listen_service", "0", CFGF_NONE), CFG_INT ("timeout", 2, CFGF_NONE), /* FIXME: Remove? */ CFG_INT ("retries", 2, CFGF_NONE), /* FIXME: Remove? */ CFG_STR ("cacertfile", NULL, CFGF_NONE), @@ -283,10 +310,12 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) r->type = RS_CONN_TYPE_UDP; else if (strcmp (typestr, "TCP") == 0) r->type = RS_CONN_TYPE_TCP; +#if defined (RS_ENABLE_TLS) else if (strcmp (typestr, "TLS") == 0) r->type = RS_CONN_TYPE_TLS; else if (strcmp (typestr, "DTLS") == 0) r->type = RS_CONN_TYPE_DTLS; +#endif else return rs_err_ctx_push (ctx, RSE_CONFIG, "%s: invalid connection type: %s", diff --git a/lib/confutil.c b/lib/confutil.c index 3a1d639..7bd4a37 100644 --- a/lib/confutil.c +++ b/lib/confutil.c @@ -94,10 +94,17 @@ rs_context_print_config (struct rs_context *ctx, char **buf_out) for (r = cfg->realms; r != NULL; r = r->next) { if (pp (&out, &len, "realm %s {\n", r->name) - || pp (&out, &len, "\ttype = \"%s\"\n\ttimeout = %d\n\tretries = %d\n", + || pp (&out, &len, + "\ttype = \"%s\"\n" + "\ttimeout = %d\n" + "\tretries = %d\n" + "\tlisten_addr = \"%s\"\n" + "\tlisten_service = \"%s\"\n", realm_type[r->type], r->timeout, - r->retries)) + r->retries, + r->local_addr->hostname, + r->local_addr->service)) return -RSE_INTERNAL; for (p = r->peers; p != NULL; p = p->next) { diff --git a/lib/conn.c b/lib/conn.c index 95e65a4..4c9158c 100644 --- a/lib/conn.c +++ b/lib/conn.c @@ -13,6 +13,7 @@ #include #include #include +#include "err.h" #include "debug.h" #include "conn.h" #include "event.h" @@ -20,19 +21,6 @@ #include "tcp.h" int -conn_close (struct rs_connection **connp) -{ - int r = 0; - assert (connp); - assert (*connp); - if ((*connp)->state == RS_CONN_STATE_CONNECTED) - r = rs_conn_disconnect (*connp); - if (r == RSE_OK) - *connp = NULL; - return r; -} - -int conn_user_dispatch_p (const struct rs_connection *conn) { assert (conn); @@ -43,7 +31,6 @@ conn_user_dispatch_p (const struct rs_connection *conn) conn->callbacks.sent_cb); } - int conn_activate_timeout (struct rs_connection *conn) { @@ -62,23 +49,37 @@ conn_activate_timeout (struct rs_connection *conn) } int -conn_type_tls (const struct rs_connection *conn) +conn_type_tls_p (const struct rs_connection *conn) +{ + return TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_TLS + || TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_DTLS; +} + +int +baseconn_type_datagram_p (const struct rs_conn_base *connbase) { - assert (conn->base_.active_peer); - return conn->base_.realm->type == RS_CONN_TYPE_TLS - || conn->base_.realm->type == RS_CONN_TYPE_DTLS; + return connbase->transport == RS_CONN_TYPE_UDP + || connbase->transport == RS_CONN_TYPE_DTLS; +} + +int +baseconn_type_stream_p (const struct rs_conn_base *connbase) +{ + return connbase->transport == RS_CONN_TYPE_TCP + || connbase->transport == RS_CONN_TYPE_TLS; } int conn_cred_psk (const struct rs_connection *conn) { - assert (conn->base_.active_peer); - return conn->base_.active_peer->transport_cred && - conn->base_.active_peer->transport_cred->type == RS_CRED_TLS_PSK; + assert (conn); + assert (conn->active_peer); + return conn->active_peer->transport_cred && + conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK; } void -conn_init (struct rs_context *ctx, +conn_init (struct rs_context *ctx, /* FIXME: rename connbase_init? */ struct rs_conn_base *connbase, enum rs_conn_subtype type) { @@ -102,7 +103,7 @@ conn_init (struct rs_context *ctx, } int -conn_configure (struct rs_context *ctx, +conn_configure (struct rs_context *ctx, /* FIXME: rename conbbase_configure? */ struct rs_conn_base *connbase, const char *config) { @@ -121,15 +122,84 @@ conn_configure (struct rs_context *ctx, connbase->tryagain = r->retries; } } +#if 0 /* incoming connections don't have a realm (a config object), update: they do, but "somebody else" is setting this up <-- FIXME */ if (connbase->realm == NULL) { - connbase->realm = rs_calloc (ctx, 1, sizeof (struct rs_realm)); - if (connbase->realm == NULL) + struct rs_realm *r = rs_calloc (ctx, 1, sizeof (struct rs_realm)); + if (r == NULL) return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); + r->next = ctx->realms; + ctx->realms = connbase->realm = r; + } +#else + if (connbase->realm) + connbase->transport = connbase->realm->type; +#endif + + return RSE_OK; +} + +int +conn_add_read_event (struct rs_connection *conn, void *user_data) +{ + struct rs_conn_base *connbase = TO_BASE_CONN(conn); + int err; + + assert(connbase); + + if (connbase->bev) /* TCP (including TLS). */ + { + bufferevent_setwatermark (connbase->bev, EV_READ, RS_HEADER_LEN, 0); + bufferevent_setcb (connbase->bev, tcp_read_cb, NULL, tcp_event_cb, + user_data); + bufferevent_enable (connbase->bev, EV_READ); + } + else /* UDP. */ + { + /* Put fresh message in user_data for the callback and enable the + read event. */ + event_assign (connbase->rev, connbase->ctx->evb, + event_get_fd (connbase->rev), EV_READ, + event_get_callback (connbase->rev), + user_data); + err = event_add (connbase->rev, NULL); + if (err < 0) + return rs_err_connbase_push_fl (connbase, RSE_EVENT, __FILE__, __LINE__, + "event_add: %s", + evutil_gai_strerror (err)); + + /* Activate retransmission timer. */ + conn_activate_timeout (conn); } + return RSE_OK; } +/** Return !=0 if \a conn is an originating connection, i.e. if its + peer is a server. */ +int +conn_originating_p (const struct rs_connection *conn) +{ + return conn->active_peer->type == RS_PEER_TYPE_SERVER; +} + +int +baseconn_close (struct rs_conn_base *connbase) +{ + int err = 0; + assert (connbase); + + rs_debug (("%s: closing fd %d\n", __func__, connbase->fd)); + + err = evutil_closesocket (connbase->fd); + if (err) + err = rs_err_connbase_push (connbase, RSE_EVENT, + "evutil_closesocket: %d (%s)", + errno, strerror (errno)); + connbase->fd = -1; + return err; +} + /* Public functions. */ int rs_conn_create (struct rs_context *ctx, @@ -179,12 +249,8 @@ rs_conn_add_listener (struct rs_connection *conn, int rs_conn_disconnect (struct rs_connection *conn) { - int err = 0; - - assert (conn); - - err = evutil_closesocket (conn->base_.fd); - conn->base_.fd = -1; + int err = baseconn_close (TO_BASE_CONN (conn)); + conn->state = RS_CONN_STATE_UNDEFINED; return err; } @@ -229,9 +295,12 @@ rs_conn_set_eventbase (struct rs_connection *conn, struct event_base *eb) } void -rs_conn_set_callbacks (struct rs_connection *conn, struct rs_conn_callbacks *cb) +rs_conn_set_callbacks (struct rs_connection *conn, + struct rs_conn_callbacks *cb, + void *user_data) { assert (conn); + TO_BASE_CONN(conn)->user_data = user_data; memcpy (&conn->callbacks, cb, sizeof (conn->callbacks)); } @@ -243,7 +312,7 @@ rs_conn_del_callbacks (struct rs_connection *conn) } struct rs_conn_callbacks * -rs_conn_get_callbacks(struct rs_connection *conn) +rs_conn_get_callbacks (struct rs_connection *conn) { assert (conn); return &conn->callbacks; @@ -282,7 +351,6 @@ struct event_base int rs_conn_get_fd (struct rs_connection *conn) { assert (conn); - assert (conn->base_.active_peer); return conn->base_.fd; } @@ -294,9 +362,9 @@ _rcb (struct rs_message *message, void *user_data) assert (msg->conn); msg->flags |= RS_MESSAGE_RECEIVED; - if (msg->conn->base_.bev) + if (msg->conn->base_.bev) /* TCP -- disable bufferevent. */ bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ); - else + else /* UDP -- remove read event. */ event_del (msg->conn->base_.rev); } @@ -322,46 +390,29 @@ rs_conn_receive_message (struct rs_connection *conn, conn->base_.user_data = msg; msg->flags &= ~RS_MESSAGE_RECEIVED; - if (conn->base_.bev) /* TCP. */ - { - bufferevent_setwatermark (conn->base_.bev, EV_READ, RS_HEADER_LEN, 0); - bufferevent_setcb (conn->base_.bev, tcp_read_cb, NULL, tcp_event_cb, msg); - bufferevent_enable (conn->base_.bev, EV_READ); - } - else /* UDP. */ - { - /* Put fresh message in user_data for the callback and enable the - read event. */ - event_assign (conn->base_.rev, conn->base_.ctx->evb, - event_get_fd (conn->base_.rev), EV_READ, - event_get_callback (conn->base_.rev), msg); - err = event_add (conn->base_.rev, NULL); - if (err < 0) - return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__, - "event_add: %s", - evutil_gai_strerror (err)); - - /* Activate retransmission timer. */ - conn_activate_timeout (msg->conn); - } + err = conn_add_read_event (conn, msg); + if (err) + return err; rs_debug (("%s: entering event loop\n", __func__)); err = event_base_dispatch (conn->base_.ctx->evb); conn->callbacks.received_cb = NULL; if (err < 0) - return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__, + return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "event_base_dispatch: %s", evutil_gai_strerror (err)); rs_debug (("%s: event loop done\n", __func__)); - if ((msg->flags & RS_MESSAGE_RECEIVED) == 0 + if ((msg->flags & RS_MESSAGE_RECEIVED) == 0 /* No message. */ || (req_msg - && message_verify_response (msg->conn, msg, req_msg) != RSE_OK)) + && message_verify_response (conn, msg, req_msg) != RSE_OK)) { - if (rs_err_conn_peek_code (msg->conn) == RSE_OK) - /* No message and no error on the stack _should_ mean that the - server hung up on us. */ - rs_err_conn_push (msg->conn, RSE_DISCO, "no response"); + if (rs_err_conn_peek_code (conn) == RSE_OK) + { + /* No message and no error on the stack _should_ mean that the + server hung up on us. */ + rs_err_conn_push (conn, RSE_DISCO, "no response"); + } return rs_err_conn_peek_code (conn); } @@ -377,3 +428,10 @@ rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv) assert (tv); conn->base_.timeout = *tv; } + +struct rs_peer * +connbase_get_peers (const struct rs_conn_base *connbase) +{ + assert (connbase); + return connbase->peers; +} diff --git a/lib/conn.h b/lib/conn.h index c1543d9..3e34453 100644 --- a/lib/conn.h +++ b/lib/conn.h @@ -1,10 +1,8 @@ /* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ -int conn_close (struct rs_connection **connp); int conn_user_dispatch_p (const struct rs_connection *conn); int conn_activate_timeout (struct rs_connection *conn); -int conn_type_tls (const struct rs_connection *conn); int conn_cred_psk (const struct rs_connection *conn); int conn_configure (struct rs_context *ctx, struct rs_conn_base *connbase, @@ -12,3 +10,11 @@ int conn_configure (struct rs_context *ctx, void conn_init (struct rs_context *ctx, struct rs_conn_base *connbase, enum rs_conn_subtype type); +int conn_type_tls_p (const struct rs_connection *conn); +int baseconn_type_datagram_p (const struct rs_conn_base *connbase); +int baseconn_type_stream_p (const struct rs_conn_base *connbase); +struct rs_peer *connbase_get_peers (const struct rs_conn_base *connbase); +int conn_add_read_event (struct rs_connection *conn, void *user_data); +int conn_originating_p (const struct rs_connection *conn); +int baseconn_close (struct rs_conn_base *connbase); +struct bufferevent *baseconn_get_bev (struct rs_conn_base *connbase); diff --git a/lib/err.c b/lib/err.c index 413ab3e..a0fe8a0 100644 --- a/lib/err.c +++ b/lib/err.c @@ -121,6 +121,7 @@ _ctx_err_vpush_fl (struct rs_context *ctx, int code, const char *file, return RSE_NOMEM; /* TODO: Implement a stack. */ + assert (ctx); if (ctx->err) rs_err_free (ctx->err); ctx->err = err; @@ -158,11 +159,12 @@ rs_err_ctx_push_fl (struct rs_context *ctx, int code, const char *file, int err_connbase_push_err (struct rs_conn_base *connbase, struct rs_error *err) { - + assert (connbase); if (connbase->err) rs_err_free (connbase->err); connbase->err = err; /* FIXME: use a stack */ + assert (err); return err->code; } @@ -185,6 +187,7 @@ rs_err_connbase_push (struct rs_conn_base *connbase, int code, { int r = 0; + assert (connbase); va_list args; va_start (args, fmt); r = _connbase_err_vpush_fl (connbase, code, NULL, 0, fmt, args); @@ -198,6 +201,7 @@ rs_err_conn_push (struct rs_connection *conn, int code, const char *fmt, ...) { int r = 0; + assert (conn); va_list args; va_start (args, fmt); r = _connbase_err_vpush_fl (TO_BASE_CONN (conn), code, NULL, 0, fmt, args); @@ -249,18 +253,30 @@ rs_err_ctx_pop (struct rs_context *ctx) } struct rs_error * -rs_err_conn_pop (struct rs_connection *conn) +rs_err_connbase_pop (struct rs_conn_base *connbase) { struct rs_error *err; - if (!conn) + if (!connbase) return NULL; /* FIXME: RSE_INVALID_CONN */ - err = conn->base_.err; - conn->base_.err = NULL; + err = connbase->err; + connbase->err = NULL; return err; } +struct rs_error * +rs_err_conn_pop (struct rs_connection *conn) +{ + return rs_err_connbase_pop (TO_BASE_CONN (conn)); +} + +struct rs_error * +rs_err_listener_pop (struct rs_listener *l) +{ + return rs_err_connbase_pop (TO_BASE_CONN (l)); +} + int rs_err_conn_peek_code (struct rs_connection *conn) { diff --git a/lib/event.c b/lib/event.c index c651e4b..fadcc14 100644 --- a/lib/event.c +++ b/lib/event.c @@ -10,6 +10,7 @@ #include #include +#include #include #if defined (RS_ENABLE_TLS) #include @@ -27,6 +28,7 @@ #include "event.h" #include "message.h" #include "conn.h" +#include "listener.h" #include "debug.h" #if defined (DEBUG) @@ -72,7 +74,7 @@ event_conn_timeout_cb (int fd, short event, void *data) if (event & EV_TIMEOUT) { rs_debug (("%s: connection timeout on %p (fd %d) connecting to %p\n", - __func__, conn, conn->base_.fd, conn->base_.active_peer)); + __func__, conn, conn->base_.fd, conn->active_peer)); conn->state = RS_CONN_STATE_UNDEFINED; rs_err_conn_push_fl (conn, RSE_TIMEOUT_CONN, __FILE__, __LINE__, NULL); event_loopbreak (conn); @@ -90,70 +92,118 @@ event_retransmit_timeout_cb (int fd, short event, void *data) if (event & EV_TIMEOUT) { rs_debug (("%s: retransmission timeout on %p (fd %d) sending to %p\n", - __func__, conn, conn->base_.fd, conn->base_.active_peer)); + __func__, conn, conn->base_.fd, conn->active_peer)); rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL); event_loopbreak (conn); } } +/* FIXME: event_ is actually not such a great prefix given that we + link with libevent which exports 113 symbols prefixed 'event_'. */ int -event_init_socket (struct rs_connection *conn, struct rs_peer *p) +event_init_socket (struct rs_conn_base *connbase, struct rs_peer *p) { - if (conn->base_.fd != -1) + if (connbase->fd != -1) return RSE_OK; + assert (p); + assert (p->realm); + + /* Resolve potential DNS name for peer. */ 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_connbase_push_err (TO_BASE_CONN (conn), err); + return err_connbase_push_err (connbase, err); } - conn->base_.fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype, - p->addr_cache->ai_protocol); - if (conn->base_.fd < 0) - return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, - "socket: %d (%s)", - errno, strerror (errno)); - if (evutil_make_socket_nonblocking (conn->base_.fd) < 0) + /* Create the socket and make it non-blocking. */ + connbase->fd = socket (p->addr_cache->ai_family, + p->addr_cache->ai_socktype, + p->addr_cache->ai_protocol); + if (connbase->fd < 0) + return rs_err_connbase_push_fl (connbase, RSE_SOCKERR, __FILE__, __LINE__, + "socket: %d (%s)", + errno, strerror (errno)); + if (evutil_make_socket_nonblocking (connbase->fd) < 0) { - evutil_closesocket (conn->base_.fd); - conn->base_.fd = -1; - return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, - "evutil_make_socket_nonblocking: %d (%s)", - errno, strerror (errno)); + evutil_closesocket (connbase->fd); + connbase->fd = -1; + return rs_err_connbase_push_fl (connbase, RSE_SOCKERR, __FILE__, __LINE__, + "evutil_make_socket_nonblocking: %d (%s)", + errno, strerror (errno)); + } + + /* If we're inititalising the socket for a listener, bind to the + peer address. */ + if (connbase->magic == RS_CONN_MAGIC_LISTENER) + { + assert (p->realm->type == connbase->transport); + if (p->realm->type == RS_CONN_TYPE_TLS + || p->realm->type == RS_CONN_TYPE_TCP) + { + struct rs_listener *listener = TO_LISTENER_CONN (connbase); + listener->evlistener = + evconnlistener_new_bind (listener->base_.ctx->evb, + listener_accept_cb_, + listener, LEV_OPT_REUSEABLE, + LISTENER_BACKLOG, + p->addr_cache->ai_addr, + p->addr_cache->ai_addrlen); + if (listener->evlistener == NULL) + return rs_err_connbase_push (connbase, RSE_EVENT, + "evconnlistener_new_bind: %d (%s)", + errno, strerror (errno)); + + evconnlistener_set_error_cb (listener->evlistener, listener_err_cb_); + } + else + { + return rs_err_connbase_push_fl (connbase, RSE_NOSYS, + __FILE__, __LINE__, NULL); + } } + return RSE_OK; } int -event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) +event_init_bufferevent (struct rs_connection *conn) { - if (conn->base_.bev) + struct rs_conn_base *connbase = NULL; + assert (conn); + connbase = TO_BASE_CONN(conn); + + if (connbase->bev) return RSE_OK; - if (conn->base_.realm->type == RS_CONN_TYPE_TCP) + if (connbase->transport == RS_CONN_TYPE_TCP) { - conn->base_.bev = bufferevent_socket_new (conn->base_.ctx->evb, - conn->base_.fd, 0); - if (!conn->base_.bev) + connbase->bev = bufferevent_socket_new (connbase->ctx->evb, + connbase->fd, 0); + if (!connbase->bev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, - "bufferevent_socket_new"); + "bufferevent_socket_new"); } #if defined (RS_ENABLE_TLS) - else if (conn->base_.realm->type == RS_CONN_TYPE_TLS) + else if (connbase->transport == RS_CONN_TYPE_TLS) { + enum bufferevent_ssl_state bev_ssl_state; + 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->base_.bev = - bufferevent_openssl_socket_new (conn->base_.ctx->evb, conn->base_.fd, - conn->tls_ssl, - BUFFEREVENT_SSL_CONNECTING, 0); - if (!conn->base_.bev) + bev_ssl_state = conn_originating_p (conn) + ? BUFFEREVENT_SSL_CONNECTING : BUFFEREVENT_SSL_ACCEPTING; + + /* It would be convenient to pass BEV_OPT_CLOSE_ON_FREE in last + argument (options) but things seem to break when + be_openssl_ctrl() (in libevent) calls SSL_set_bio() after + BIO_new_socket() with flag=1. */ + connbase->bev = + bufferevent_openssl_socket_new (connbase->ctx->evb, connbase->fd, + conn->tls_ssl, bev_ssl_state, 0); + if (!connbase->bev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "bufferevent_openssl_socket_new"); } @@ -162,7 +212,7 @@ event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) { return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__, "%s: unknown connection type: %d", __func__, - conn->base_.realm->type); + connbase->transport); } return RSE_OK; @@ -176,10 +226,10 @@ event_do_connect (struct rs_connection *conn) size_t peer_addrlen; assert (conn); - assert (conn->base_.active_peer); - assert (conn->base_.active_peer->addr_cache); - peer_addr = conn->base_.active_peer->addr_cache->ai_addr; - peer_addrlen = conn->base_.active_peer->addr_cache->ai_addrlen; + assert (conn->active_peer); + assert (conn->active_peer->addr_cache); + peer_addr = conn->active_peer->addr_cache->ai_addr; + peer_addrlen = conn->active_peer->addr_cache->ai_addrlen; /* We don't connect listeners. */ assert (conn->base_.magic == RS_CONN_MAGIC_GENERIC); @@ -217,11 +267,9 @@ event_do_connect (struct rs_connection *conn) rs_debug (("%s: %d: connect: %d (%s)\n", __func__, conn->base_.fd, sockerr, evutil_socket_error_to_string (sockerr))); - rs_err_conn_push_fl (conn, RSE_SOCKERR, - __FILE__, __LINE__, - "%d: connect: %d (%s)", conn->base_.fd, - sockerr, - evutil_socket_error_to_string (sockerr)); + rs_err_conn_push (conn, RSE_SOCKERR, + "%d: connect: %d (%s)", conn->base_.fd, + sockerr, evutil_socket_error_to_string (sockerr)); } else conn->state = RS_CONN_STATE_CONNECTING; @@ -235,7 +283,7 @@ event_loopbreak (struct rs_connection *conn) if (err < 0) rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "event_base_loopbreak: %s", - evutil_gai_strerror (err)); + evutil_gai_strerror (err)); /* FIXME: really gai_strerror? */ return err; } @@ -244,21 +292,21 @@ void event_on_disconnect (struct rs_connection *conn) { conn->state = RS_CONN_STATE_UNDEFINED; - rs_debug (("%s: %p disconnected\n", __func__, - TO_BASE_CONN(conn)->active_peer)); + rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer)); if (conn->callbacks.disconnected_cb) conn->callbacks.disconnected_cb (conn->base_.user_data); } -/** Internal connect event returning 0 on success or -1 on error. */ +/** Internal connect event for originating connections. Returns 0 on + success and -1 on TLS certificate verification failure. */ int -event_on_connect (struct rs_connection *conn, struct rs_message *msg) +event_on_connect_orig (struct rs_connection *conn, struct rs_message *msg) { assert (conn->state == RS_CONN_STATE_CONNECTING); - assert (conn->base_.active_peer); + assert (conn->active_peer); #if defined (RS_ENABLE_TLS) - if (conn_type_tls(conn) && !conn_cred_psk(conn)) + if (conn_type_tls_p (conn) && !conn_cred_psk (conn)) if (tls_verify_cert (conn) != RSE_OK) { rs_debug (("%s: server cert verification failed\n", __func__)); @@ -267,7 +315,7 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg) #endif /* RS_ENABLE_TLS */ conn->state = RS_CONN_STATE_CONNECTED; - rs_debug (("%s: %p connected\n", __func__, TO_BASE_CONN(conn)->active_peer)); + rs_debug (("%s: %p connected\n", __func__, conn->active_peer)); if (conn->callbacks.connected_cb) conn->callbacks.connected_cb (conn->base_.user_data); @@ -278,12 +326,24 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg) return 0; } +/** FIXME: DOC */ int -event_init_eventbase (struct rs_connection *conn) +event_on_connect_term (struct rs_connection *conn, struct rs_message *msg) { - assert (conn); - assert (conn->base_.ctx); - if (conn->base_.ctx->evb) + /* TODO: verify client */ + conn->state = RS_CONN_STATE_CONNECTED; + rs_debug (("%s: WARNING: not checking client cert!!!\n", __func__)); + if (conn->callbacks.connected_cb) + conn->callbacks.connected_cb (conn->base_.user_data); + return 0; +} + +int +event_init_eventbase (struct rs_conn_base *connbase) +{ + assert (connbase); + assert (connbase->ctx); + if (connbase->ctx->evb) return RSE_OK; #if defined (DEBUG) @@ -291,10 +351,10 @@ event_init_eventbase (struct rs_connection *conn) event_enable_debug_mode (); #endif event_set_log_callback (_evlog_cb); - conn->base_.ctx->evb = event_base_new (); - if (!conn->base_.ctx->evb) - return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, - "event_base_new"); + connbase->ctx->evb = event_base_new (); + if (!connbase->ctx->evb) + return rs_err_connbase_push_fl (connbase, RSE_EVENT, __FILE__, __LINE__, + "event_base_new"); return RSE_OK; } diff --git a/lib/event.h b/lib/event.h index 3fcea61..63fccc5 100644 --- a/lib/event.h +++ b/lib/event.h @@ -1,12 +1,13 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ void event_on_disconnect (struct rs_connection *conn); -int event_on_connect (struct rs_connection *conn, struct rs_message *msg); +int event_on_connect_orig (struct rs_connection *conn, struct rs_message *msg); +int event_on_connect_term (struct rs_connection *conn, struct rs_message *msg); 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); -int event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer); +int event_init_eventbase (struct rs_conn_base *connbase); +int event_init_socket (struct rs_conn_base *connbase, struct rs_peer *p); +int event_init_bufferevent (struct rs_connection *conn); void event_do_connect (struct rs_connection *conn); void event_conn_timeout_cb (int fd, short event, void *data); void event_retransmit_timeout_cb (int fd, short event, void *data); diff --git a/lib/examples/Makefile.am b/lib/examples/Makefile.am index 8dc4f58..d86f4f8 100644 --- a/lib/examples/Makefile.am +++ b/lib/examples/Makefile.am @@ -1,6 +1,6 @@ AUTOMAKE_OPTIONS = foreign INCLUDES = -I$(top_srcdir)/include -AM_CFLAGS = -Wall -Werror -g +AM_CFLAGS = -Wall -Werror -g -DDEBUG -DDEBUG_LEVENT LDADD = ../libradsec.la #-lefence CFLAGS = $(AM_CFLAGS) -DUSE_CONFIG_FILE diff --git a/lib/examples/client-blocking.c b/lib/examples/client-blocking.c index d2ee9f4..82a4453 100644 --- a/lib/examples/client-blocking.c +++ b/lib/examples/client-blocking.c @@ -85,9 +85,9 @@ blocking_client (const char *av1, const char *av2, const char *av3, } else { - if (rs_message_create_authn_request (conn, &req, USER_NAME, USER_PW, SECRET)) + if (rs_message_create_authn_request (conn, &req, USER_NAME, USER_PW)) goto cleanup; - if (rs_message_send (req, NULL)) + if (rs_message_send (req)) goto cleanup; if (rs_conn_receive_message (conn, req, &resp)) goto cleanup; diff --git a/lib/examples/client-dispatch.c b/lib/examples/client-dispatch.c index e007654..8a80ec6 100644 --- a/lib/examples/client-dispatch.c +++ b/lib/examples/client-dispatch.c @@ -67,14 +67,13 @@ dispatching_client (struct rs_context *ctx) if (rs_conn_create(ctx, &conn, CONFIG)) goto out; - rs_conn_set_callbacks (conn, &cb); - if (rs_packet_create_authn_request (conn, &req_msg, - USER_NAME, USER_PW, SECRET)) + rs_conn_set_callbacks (conn, &cb, &state); + if (rs_packet_create_authn_request (conn, &req_msg, USER_NAME, USER_PW)) goto out; /* Doesn't really send the message but rather queues it for sending. msg_received_cb() will be invoked with user_data = &state when the message has been sent. */ - if (rs_packet_send (req_msg, &state)) + if (rs_packet_send (req_msg)) goto out; while (1) diff --git a/lib/examples/client.conf b/lib/examples/client.conf index 32af3c0..288a084 100644 --- a/lib/examples/client.conf +++ b/lib/examples/client.conf @@ -9,6 +9,17 @@ realm blocking-udp { } } +realm testcli-udp { + type = "UDP" + timeout = 2 + retries = 2 + server { + hostname = "srv1" + service = "4711" + secret = "sikrit" + } +} + realm blocking-tls { type = "TLS" timeout = 1 @@ -22,7 +33,22 @@ realm blocking-tls { #pskex = "PSK" server { hostname = "srv1" + # test setup: radsecproxy fronting freeradius on 2083 service = "2083" + # test setup: examples/server on 4711 + #service = "4711" + secret = "sikrit" + } +} + +realm testcli { + type = "TLS" + cacertfile = "/home/linus/p/radsecproxy/demoCA/newcerts/01.pem" + certfile = "/home/linus/p/radsecproxy/demoCA/newcerts/03.pem" + certkeyfile = "/home/linus/p/radsecproxy/demoCA/private/cli1.key" + server { + hostname = "srv1" + service = "4711" secret = "sikrit" } } diff --git a/lib/examples/server.c b/lib/examples/server.c index 8c304a0..fb51866 100644 --- a/lib/examples/server.c +++ b/lib/examples/server.c @@ -1,7 +1,7 @@ /* RADIUS/RadSec server using libradsec. */ /* Copyright 2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ #include #include @@ -11,49 +11,129 @@ #include #include "debug.h" /* For rs_dump_message(). */ -#define CONFIG_FILE "examples/test.conf" +#define CONFIG_FILE "examples/server.conf" #define CONFIG "tls" #define SECRET "sikrit" #define USER_NAME "molgan@PROJECT-MOONSHOT.ORG" #define USER_PW "password" -void +static struct rs_peer * +client_filter_cb (const struct rs_listener *listener, + void *user_data) +{ + printf ("DEBUG: listener %p (user_data=%p) asking for a client filter list\n", + listener, user_data); + return NULL; +} + +static void +disco_cb (void *user_data) +{ + struct rs_connection *conn = user_data; + assert (conn); + printf ("DEBUG: conn %p disconnected\n", conn); +} + +static void +read_cb (struct rs_message *message, void *user_data) +{ + struct rs_connection *conn = user_data; + assert (conn); + printf ("DEBUG: msg received on connection %p\n", conn); + rs_dump_message (message); + //if (message_verify_response (conn, fixme)) error; +} + +static void new_conn_cb (struct rs_connection *conn, void *user_data) { - printf ("new connection: fd=%d\n", -1); /* conn->fd */ + const struct rs_listener *l = user_data; + struct rs_conn_callbacks cb = {NULL, /* connected */ + disco_cb, + read_cb, + NULL}; /* msg sent */ + + printf ("DEBUG: new connection on listener %p: %p, fd=%d\n", + l, conn, rs_conn_get_fd (conn)); + rs_conn_set_callbacks (conn, &cb, conn); } +void +err_cb (struct rs_connection *conn, void *user_data) +{ + struct rs_listener *listener = user_data; + struct rs_error *err = NULL; + assert (conn); + err = rs_err_conn_pop (conn); + + printf ("DEBUG: error on conn %p, listener %p: %d (%s)\n", conn, listener, + rs_err_code (err, 0), rs_err_msg (err)); +} + +#if 0 +void +stdin_cb (evutil_socket_t s, short flags, void *user_data) +{ + struct rs_listener *l = user_data; + + printf ("DEBUG: got data on stdin, quitting\n"); + assert (event_base_loopbreak (rs_listener_get_eventbase (l)) == 0); +} +#endif + struct rs_error * server (struct rs_context *ctx) { int r = 0; struct rs_error *err = NULL; - struct rs_connection *conn = NULL; struct rs_listener *listener = NULL; - const struct rs_listener_callbacks cbs = {}; + const struct rs_listener_callbacks cbs = + {client_filter_cb, new_conn_cb, err_cb}; + struct event *read_event = NULL; if (rs_listener_create (ctx, &listener, CONFIG)) goto out; - rs_listener_set_callbacks (listener, &cbs); + rs_listener_set_callbacks (listener, &cbs, listener); + if (rs_listener_listen (listener)) + goto out; + +#if 0 + /* Listen on stdin too, for quitting the server nicely without + having to trap SIGKILL. */ + read_event = event_new (rs_listener_get_eventbase (listener), + fileno (stdin), + EV_READ, + stdin_cb, + listener); + assert (read_event != NULL); + assert (event_add (read_event, NULL) == 0); +#endif do - { - r = rs_listener_dispatch (listener); - printf ("DEBUG: rs_listener_dispatch done (r=%d)\n", r); - } + r = rs_listener_dispatch (listener); while (r == 0); + printf ("DEBUG: rs_listener_dispatch done (r=%d)\n", r); + if (r < 0) + printf ("DEBUG: libevent signals error: %s\n", evutil_gai_strerror (r)); + if (r == 1) + printf ("DEBUG: no events registered, exiting\n"); + out: err = rs_err_ctx_pop (ctx); if (err == NULL) - err = rs_err_conn_pop (conn); + err = rs_err_listener_pop (listener); -#if 0 + if (read_event) + event_free (read_event); + read_event = NULL; if (listener) - rs_listener_destroy (listener); + { + assert (rs_listener_close (listener) == RSE_OK); + //rs_listener_destroy (listener); + } listener = NULL; -#endif return err; } @@ -80,13 +160,15 @@ main (int argc, char *argv[]) err = server (ctx); out: - if (ctx) - rs_context_destroy (ctx); - if (err) { - fprintf (stderr, "error: %s: %d\n", rs_err_msg (err), rs_err_code (err, 0)); + fprintf (stderr, "%s: error: %s: %d\n", + argv[0], rs_err_msg (err), rs_err_code (err, 0)); return rs_err_code (err, 1); } + + if (ctx) + rs_context_destroy (ctx); + return 0; } diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h index 45ce7f6..28e9e4c 100644 --- a/lib/include/radsec/radsec-impl.h +++ b/lib/include/radsec/radsec-impl.h @@ -90,6 +90,7 @@ struct rs_realm { int timeout; int retries; struct rs_listener *listeners; + struct rs_peer *local_addr; struct rs_peer *peers; struct rs_realm *next; }; @@ -103,9 +104,10 @@ struct rs_config { /** Libradsec context. */ struct rs_context { struct rs_config *config; + struct rs_realm *realms; struct rs_alloc_scheme alloc_scheme; struct rs_error *err; - struct event_base *evb; /* Event base. */ + struct event_base *evb; }; /** Base class for a connection. */ @@ -113,11 +115,11 @@ struct rs_conn_base { uint32_t magic; /* Must be one of RS_CONN_MAGIC_*. */ struct rs_context *ctx; struct rs_realm *realm; /* Owned by ctx. */ + enum rs_conn_type transport; /** For a listener, allowed client addr/port pairs. - For an outgoing connection, set of servers. + For an outgoing connection, set of configured servers. For an incoming connection, the peer (as the only entry). */ - struct rs_peer *peers; /**< Configured peers. */ - struct rs_peer *active_peer; /**< The other end of the connection. */ + struct rs_peer *peers; struct timeval timeout; int tryagain; /* For server failover. */ void *user_data; @@ -137,16 +139,13 @@ enum rs_conn_state { RS_CONN_STATE_CONNECTED, }; -/** A "generic" connection. */ +/** A generic connection. */ struct rs_connection { struct rs_conn_base base_; struct event *tev; /* Timeout event. */ struct rs_conn_callbacks callbacks; enum rs_conn_state state; -#if 0 - char is_connecting; /* FIXME: replace with a single state member */ - char is_connected; /* FIXME: replace with a single state member */ -#endif /* 0 */ + struct rs_peer *active_peer; struct rs_message *out_queue; /* Queue for outgoing UDP packets. */ #if defined(RS_ENABLE_TLS) /* TLS specifics. */ @@ -156,7 +155,7 @@ struct rs_connection { }; /** A listening connection. Spawns generic connections when peers - * connect to it. */ + connect to it. */ struct rs_listener { struct rs_conn_base base_; struct evconnlistener *evlistener; diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h index cb98db7..2858f9e 100644 --- a/lib/include/radsec/radsec.h +++ b/lib/include/radsec/radsec.h @@ -22,6 +22,7 @@ #ifdef HAVE_STDINT_H #include #endif +#include "compat.h" enum rs_error_code { RSE_OK = 0, @@ -72,7 +73,7 @@ enum rs_error_code { RSE_MAX = RSE_CERT }; -enum rs_conn_type { +enum rs_conn_type { /* FIXME: Rename rs_transport_type? */ RS_CONN_TYPE_NONE = 0, RS_CONN_TYPE_UDP, RS_CONN_TYPE_TCP, @@ -174,10 +175,14 @@ struct rs_conn_callbacks { rs_conn_message_sent_cb sent_cb; }; -typedef void (*rs_listener_new_conn_cb) (struct rs_connection *conn, - void *user_data); -typedef void (*rs_listener_error_cb) (void *user_data); +typedef struct rs_peer *(*rs_listener_client_filter_cb) + (const struct rs_listener *listener, void *user_data); +typedef void (*rs_listener_new_conn_cb) + (struct rs_connection *conn, void *user_data); +typedef void (*rs_listener_error_cb) + (struct rs_connection *conn, void *user_data); struct rs_listener_callbacks { + rs_listener_client_filter_cb client_filter_cb; rs_listener_new_conn_cb new_conn_cb; rs_listener_error_cb error_cb; }; @@ -228,8 +233,13 @@ int rs_listener_create (struct rs_context *ctx, struct rs_listener **listener, const char *config); void rs_listener_set_callbacks (struct rs_listener *listener, - const struct rs_listener_callbacks *cb); + const struct rs_listener_callbacks *cb, + void *user_data); +int rs_listener_listen (struct rs_listener *listener); int rs_listener_dispatch (const struct rs_listener *listener); +int rs_listener_close (struct rs_listener *l); +struct event_base *rs_listener_get_eventbase (const struct rs_listener *l); +int rs_listener_get_fd (const struct rs_listener *l); /****************/ /* Connection. */ @@ -277,7 +287,8 @@ int rs_conn_set_eventbase(struct rs_connection *conn, struct event_base *eb); /** Register callbacks \a cb for connection \a conn. */ void rs_conn_set_callbacks(struct rs_connection *conn, - struct rs_conn_callbacks *cb); + struct rs_conn_callbacks *cb, + void *user_data); /** Remove callbacks for connection \a conn. */ void rs_conn_del_callbacks(struct rs_connection *conn); @@ -356,28 +367,27 @@ int rs_message_create(struct rs_connection *conn, struct rs_message **pkt_out); /** Free all memory allocated for message \a msg. */ void rs_message_destroy(struct rs_message *msg); -/** Send message \a msg on the connection associated with \a msg. - \a user_data is sent to the \a rs_conn_message_received_cb callback - registered with the connection. If no callback is registered with - the connection, the event loop is run by \a rs_message_send and it - blocks until the message has been succesfully sent. +/** Send \a msg on the connection associated with \a msg. + If no callback is registered with the connection + (\a rs_conn_set_callbacks), the event loop is run by + \a rs_message_send and it blocks until the message has been + succesfully sent. - \return On success, RSE_OK (0) is returned. On error, !0 is + \return On success, RSE_OK (0) is returned. On error, !0 is returned and a struct \a rs_error is pushed on the error stack for - the connection. The error can be accessed using \a - rs_err_conn_pop. */ -int rs_message_send(struct rs_message *msg, void *user_data); + the connection. The error can be accessed using \a + rs_err_conn_pop. */ +int rs_message_send(struct rs_message *msg); /** Create a RADIUS authentication request message associated with - connection \a conn. Optionally, User-Name and User-Password - attributes are added to the message using the data in \a user_name, - \a user_pw and \a secret where \secret is the RADIUS shared - secret. */ + connection \a conn. Optionally, User-Name and User-Password + attributes are added to the message using the data in \a user_name + and \a user_pw. + FIXME: describe what RADIUS shared secret is being used */ int rs_message_create_authn_request(struct rs_connection *conn, struct rs_message **msg, const char *user_name, - const char *user_pw, - const char *secret); + const char *user_pw); /*** Append \a tail to message \a msg. */ int @@ -439,15 +449,17 @@ int rs_err_conn_push_fl(struct rs_connection *conn, int line, const char *fmt, ...); -int rs_err_connbase_push_fl (struct rs_conn_base *connbase, - int code, - const char *file, - int line, - const char *fmt, - ...); +int rs_err_connbase_push_fl(struct rs_conn_base *connbase, + int code, + const char *file, + int line, + const char *fmt, + ...); /** Pop the first error from the error FIFO associated with connection - \a conn or NULL if there are no errors in the FIFO. */ + \a conn or NULL if there are no errors in the FIFO. */ struct rs_error *rs_err_conn_pop(struct rs_connection *conn); +struct rs_error *rs_err_connbase_pop(struct rs_conn_base *connbase); +struct rs_error *rs_err_listener_pop(struct rs_listener *l); int rs_err_conn_peek_code (struct rs_connection *conn); void rs_err_free(struct rs_error *err); diff --git a/lib/listener.c b/lib/listener.c new file mode 100644 index 0000000..877c162 --- /dev/null +++ b/lib/listener.c @@ -0,0 +1,236 @@ +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include "listener.h" +#include "conn.h" +#include "peer.h" +#include "event.h" +#include "debug.h" + +struct rs_listener * +listener_create (struct rs_context *ctx, struct rs_listener **rootp) +{ + struct rs_listener *listener; + + listener = rs_calloc (ctx, 1, sizeof (*listener)); + if (listener) + { + if (*rootp == NULL) + *rootp = listener; + else + { + listener->next = (*rootp)->next; + (*rootp)->next = listener; + } + } + return listener; +} + +void +listener_accept_cb_ (struct evconnlistener *evconnlistener, + evutil_socket_t newfd, + struct sockaddr *srcaddr_sa, + int srcaddr_len, + void *user_data) +{ + int err = RSE_OK; + struct rs_listener *l = NULL; + struct rs_connection *newconn = NULL; + struct rs_context *ctx = NULL; + struct rs_peer *clients = NULL; + + l = (struct rs_listener *) user_data; + assert (l); + assert (l->base_.magic == RS_CONN_MAGIC_LISTENER); + assert (l->evlistener == evconnlistener); + ctx = l->base_.ctx; + assert (ctx); + +#if defined (DEBUG) + { + char host[80], port[80]; + getnameinfo (srcaddr_sa, srcaddr_len, host, sizeof(host), + port, sizeof(port), 0); + rs_debug (("%s: incoming connection from %s:%s\n", __func__, host, port)); + } +#endif + +/* +Application needs to specify acceptable clients -- we need to verify +src addr in the UDP case and x509 client cert in the TLS case. A list +of peers with proper pointers to realms should be a good way of doing +this. + +Ask the application for a list of acceptable clients. Default to +accepting any potential configured client block in the realm of the +listener. Note that this for this to be an opption, the application +must have read a config file. If there is no configuration, reject the +client. +*/ + if (l->callbacks.client_filter_cb) + clients = l->callbacks.client_filter_cb (l, TO_BASE_CONN(l)->user_data); + if (clients == NULL) + clients = connbase_get_peers (TO_BASE_CONN(l)); + if (clients == NULL) + { + rs_debug (("%s: didn't get a client list for listener %p\n", + __func__, l)); + return; + } + rs_debug (("%s: using client list %p\n", __func__, clients)); + + err = rs_conn_create (ctx, &newconn, NULL); + if (err) + { + rs_debug (("%s: failed creating a new struct rs_connection: %d\n", + __func__, err)); + return; /* FIXME: Verify that this is handled. */ + } + + assert(clients); + /* TODO: Picking the very first peer is not really what we want to + do. For UDP, we can look at src ip and try to find a matching + peer. For TLS, it's worse because we don't have the certificate + until we've accepted the TCP connection. */ + TO_BASE_CONN(newconn)->realm = clients->realm; + newconn->active_peer = clients; + + TO_BASE_CONN(newconn)->fd = newfd; + TO_BASE_CONN(newconn)->transport = TO_BASE_CONN(l)->realm->type; + err = event_init_bufferevent (newconn); + if (err) + { + rs_debug (("%s: failed init bev: %d\n", __func__, err)); + goto errout; + } + + /* Create a message and set up a read event. This installs the + callback performing the TLS verification. */ + { /* FIXME */ + struct rs_message *msg = NULL; + err = rs_message_create (newconn, &msg); + if (err) + abort (); /* FIXME */ + conn_add_read_event (newconn, msg); + } + + if (l->callbacks.new_conn_cb) + l->callbacks.new_conn_cb (newconn, TO_BASE_CONN(l)->user_data); + return; /* Success. */ + + errout: + rs_conn_destroy (newconn); + if (l->callbacks.error_cb) + l->callbacks.error_cb (newconn, TO_BASE_CONN(l)->user_data); +} + +void +listener_err_cb_ (struct evconnlistener *listener, void *user_data) +{ + rs_debug (("%s: FIXME: handle error\n", __func__)); +} + +/* Public functions. */ +int +rs_listener_create (struct rs_context *ctx, + struct rs_listener **listener_out, + const char *config) +{ + int err = RSE_OK; + struct rs_listener *listener = NULL; + + assert (ctx); + + listener = rs_calloc (ctx, 1, sizeof (*listener)); + if (listener == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); + conn_init (ctx, TO_BASE_CONN (listener), RS_CONN_OBJTYPE_LISTENER); + err = conn_configure (ctx, TO_BASE_CONN (listener), config); + if (err) + goto errout; + + if (listener_out) + *listener_out = listener; + return RSE_OK; + + errout: + if (listener) + rs_free (ctx, listener); + return err; +} + +void +rs_listener_set_callbacks (struct rs_listener *listener, + const struct rs_listener_callbacks *cb, + void *user_data) +{ + assert (listener); + TO_BASE_CONN(listener)->user_data = user_data; + memcpy (&listener->callbacks, cb, sizeof (listener->callbacks)); +} + +int +rs_listener_listen (struct rs_listener *listener) +{ + int err = RSE_OK; + struct rs_conn_base *connbase = NULL; + assert (listener); + connbase = TO_BASE_CONN (listener); + + err = event_init_eventbase (connbase); + if (err) + return err; + err = event_init_socket (connbase, connbase->realm->local_addr); + if (err) + return err; +#if 0 + { + struct linger l; + l.l_onoff = 1; + l.l_linger = 0; + rs_debug (("%s: setting SO_LINGER 0s on fd %d\n", __func__, connbase->fd)); + assert (0 == setsockopt (connbase->fd, SOL_SOCKET, SO_LINGER, + (void*)&l, sizeof(l))); + } +#endif + return err; +} + +int +rs_listener_dispatch (const struct rs_listener *listener) +{ + assert (listener); + assert (TO_BASE_CONN(listener)->ctx); + return event_base_dispatch (TO_BASE_CONN(listener)->ctx->evb); +} + +int +rs_listener_close (struct rs_listener *l) +{ + int err = baseconn_close (TO_BASE_CONN (l)); + return err; +} + +struct event_base * +rs_listener_get_eventbase (const struct rs_listener *l) +{ + assert (TO_BASE_CONN (l)); + assert (TO_BASE_CONN (l)->ctx); + return TO_BASE_CONN (l)->ctx->evb; +} + +int +rs_listener_get_fd (const struct rs_listener *l) +{ + assert (TO_BASE_CONN (l)); + return TO_BASE_CONN (l)->fd; +} diff --git a/lib/listener.h b/lib/listener.h new file mode 100644 index 0000000..8f7c543 --- /dev/null +++ b/lib/listener.h @@ -0,0 +1,14 @@ +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +/** Maximum number of pending connection requests. */ +#define LISTENER_BACKLOG -1 + +void listener_accept_cb_(struct evconnlistener *evconnlistener, + evutil_socket_t fd, + struct sockaddr *sa, + int socklen, + void *data); +void listener_err_cb_(struct evconnlistener *listener, void *user_data); +struct rs_listener *listener_create(struct rs_context *ctx, + struct rs_listener **rootp); diff --git a/lib/message.c b/lib/message.c index 47590ca..a40e00b 100644 --- a/lib/message.c +++ b/lib/message.c @@ -28,15 +28,15 @@ message_verify_response (struct rs_connection *conn, int err; assert (conn); - assert (conn->base_.active_peer); - assert (conn->base_.active_peer->secret); + assert (conn->active_peer); + assert (conn->active_peer->secret); assert (response); assert (response->rpkt); assert (request); assert (request->rpkt); - response->rpkt->secret = conn->base_.active_peer->secret; - response->rpkt->sizeof_secret = strlen (conn->base_.active_peer->secret); + response->rpkt->secret = conn->active_peer->secret; + response->rpkt->sizeof_secret = strlen (conn->active_peer->secret); /* Verify header and message authenticator. */ err = nr_packet_verify (response->rpkt, request->rpkt); @@ -71,11 +71,11 @@ message_do_send (struct rs_message *msg) assert (msg); assert (msg->conn); - assert (msg->conn->base_.active_peer); - assert (msg->conn->base_.active_peer->secret); + assert (msg->conn->active_peer); + assert (msg->conn->active_peer->secret); assert (msg->rpkt); - msg->rpkt->secret = msg->conn->base_.active_peer->secret; + msg->rpkt->secret = msg->conn->active_peer->secret; msg->rpkt->sizeof_secret = strlen (msg->rpkt->secret); /* Encode message. */ @@ -89,16 +89,16 @@ message_do_send (struct rs_message *msg) return rs_err_conn_push_fl (msg->conn, -err, __FILE__, __LINE__, "nr_packet_sign"); #if defined (DEBUG) - { - char host[80], serv[80]; - - getnameinfo (msg->conn->base_.active_peer->addr_cache->ai_addr, - msg->conn->base_.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)); - rs_dump_message (msg); - } + if (msg->conn->active_peer->addr_cache) + { + char host[80], serv[80]; + getnameinfo (msg->conn->active_peer->addr_cache->ai_addr, + msg->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)); + rs_dump_message (msg); + } #endif /* Put message in output buffer. */ @@ -160,8 +160,7 @@ int rs_message_create_authn_request (struct rs_connection *conn, struct rs_message **msg_out, const char *user_name, - const char *user_pw, - const char *secret) + const char *user_pw) { struct rs_message *msg; int err; @@ -174,6 +173,7 @@ rs_message_create_authn_request (struct rs_connection *conn, if (user_name) { + /* FIXME: use new rs_message_add_avp() */ err = rs_message_append_avp (msg, PW_USER_NAME, 0, user_name, 0); if (err) return err; @@ -181,7 +181,9 @@ rs_message_create_authn_request (struct rs_connection *conn, if (user_pw) { - msg->rpkt->secret = secret; + /* FIXME: msg->rpkt->secret = secret; + FIXME: use new rs_message_add_avp() */ + msg->rpkt->secret = "sikrit"; /* FIXME */ err = rs_message_append_avp (msg, PW_USER_PASSWORD, 0, user_pw, 0); if (err) return err; diff --git a/lib/peer.c b/lib/peer.c index b6ec167..bbc3bf5 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -20,12 +20,12 @@ peer_pick_peer (struct rs_connection *conn) { assert (conn); - if (conn->base_.active_peer) - conn->base_.active_peer = conn->base_.active_peer->next; /* Next. */ - if (!conn->base_.active_peer) - conn->base_.active_peer = conn->base_.peers; /* From the top. */ + if (conn->active_peer) + conn->active_peer = conn->active_peer->next; /* Next. */ + if (conn->active_peer == NULL) + conn->active_peer = TO_BASE_CONN (conn)->peers; /* From the top. */ - return conn->base_.active_peer; + return conn->active_peer; } struct rs_peer * diff --git a/lib/peer.h b/lib/peer.h index 95be0b0..2a8b43c 100644 --- a/lib/peer.h +++ b/lib/peer.h @@ -1,5 +1,5 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ struct rs_peer *peer_create (struct rs_context *ctx, struct rs_peer **rootp); struct rs_peer *peer_pick_peer (struct rs_connection *conn); diff --git a/lib/radsec.sym b/lib/radsec.sym index 6ad1361..9dd7186 100644 --- a/lib/radsec.sym +++ b/lib/radsec.sym @@ -42,7 +42,7 @@ rs_conn_del_callbacks rs_conn_destroy rs_conn_disconnect rs_conn_dispatch -rs_conn_fd +rs_conn_get_fd rs_conn_get_callbacks rs_conn_get_current_peer rs_conn_receive_message @@ -66,9 +66,14 @@ rs_err_ctx_pop rs_err_ctx_push rs_err_ctx_push_fl rs_err_free +rs_err_listener_pop rs_err_msg rs_listener_create +rs_listener_close rs_listener_dispatch +rs_listener_get_eventbase +rs_listener_get_fd +rs_listener_listen rs_listener_set_callbacks rs_message_append_avp rs_message_avps diff --git a/lib/request.c b/lib/request.c index 17b36de..611cbdf 100644 --- a/lib/request.c +++ b/lib/request.c @@ -60,7 +60,7 @@ rs_request_create_authn (struct rs_connection *conn, if (rs_request_create (conn, &req)) return -1; - if (rs_message_create_authn_request (conn, &req->req_msg, user_name, user_pw, secret)) + if (rs_message_create_authn_request (conn, &req->req_msg, user_name, user_pw)) return -1; if (req_out) @@ -103,7 +103,7 @@ rs_request_send (struct rs_request *request, struct rs_message **resp_msg) if (!request || !request->conn || !request->req_msg || !resp_msg) return rs_err_conn_push_fl (conn, RSE_INVAL, __FILE__, __LINE__, NULL); conn = request->conn; - assert (!conn_user_dispatch_p (conn)); /* This function is high level. */ + assert (!conn_user_dispatch_p (conn)); /* This function is high level. */ gettimeofday (&end, NULL); end.tv_sec += MRD; @@ -112,7 +112,7 @@ rs_request_send (struct rs_request *request, struct rs_message **resp_msg) { rs_conn_set_timeout (conn, &rt); - r = rs_message_send (request->req_msg, NULL); + r = rs_message_send (request->req_msg); if (r == RSE_OK) { r = rs_conn_receive_message (request->conn, diff --git a/lib/send.c b/lib/send.c index fab89a9..3ed6c93 100644 --- a/lib/send.c +++ b/lib/send.c @@ -21,15 +21,15 @@ static int _conn_open (struct rs_connection *conn, struct rs_message *msg) { - if (event_init_eventbase (conn)) + if (event_init_eventbase (TO_BASE_CONN (conn))) return -1; - if (!conn->base_.active_peer) + if (!conn->active_peer) peer_pick_peer (conn); - if (!conn->base_.active_peer) + if (!conn->active_peer) return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL); - if (event_init_socket (conn, conn->base_.active_peer)) + if (event_init_socket (&conn->base_, conn->active_peer)) return -1; if (conn->base_.realm->type == RS_CONN_TYPE_TCP @@ -37,7 +37,7 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg) { if (tcp_init_connect_timer (conn)) return -1; - if (event_init_bufferevent (conn, conn->base_.active_peer)) + if (event_init_bufferevent (conn)) return -1; } else @@ -59,7 +59,7 @@ static int _conn_is_open_p (struct rs_connection *conn) { return conn->state == RS_CONN_STATE_CONNECTED - && conn->base_.active_peer != NULL; + && conn->active_peer != NULL; } /* User callback used when we're dispatching for user. */ @@ -76,7 +76,7 @@ _wcb (void *user_data) } int -rs_message_send (struct rs_message *msg, void *user_data) +rs_message_send (struct rs_message *msg) { struct rs_connection *conn = NULL; int err = 0; @@ -93,14 +93,13 @@ rs_message_send (struct rs_message *msg, void *user_data) assert (conn->base_.ctx); assert (conn->base_.ctx->evb); - assert (conn->base_.active_peer); + assert (conn->active_peer); assert (conn->base_.fd >= 0); - conn->base_.user_data = user_data; - if (conn->base_.bev) /* TCP */ { - bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb, msg); + bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb, + msg); bufferevent_enable (conn->base_.bev, EV_WRITE); } else /* UDP */ @@ -118,6 +117,7 @@ rs_message_send (struct rs_message *msg, void *user_data) /* Do dispatch, unless the user wants to do it herself. */ if (!conn_user_dispatch_p (conn)) { + /* Blocking mode. */ conn->callbacks.sent_cb = _wcb; conn->base_.user_data = msg; rs_debug (("%s: entering event loop\n", __func__)); diff --git a/lib/tcp.c b/lib/tcp.c index f5673f5..7264244 100644 --- a/lib/tcp.c +++ b/lib/tcp.c @@ -31,36 +31,27 @@ _read_header (struct rs_message *msg) { size_t n = 0; - n = bufferevent_read (msg->conn->base_.bev, msg->hdr, RS_HEADER_LEN); + n = bufferevent_read (TO_BASE_CONN(msg->conn)->bev, msg->hdr, RS_HEADER_LEN); if (n == RS_HEADER_LEN) { msg->flags |= RS_MESSAGE_HEADER_READ; msg->rpkt->length = (msg->hdr[2] << 8) + msg->hdr[3]; if (msg->rpkt->length < 20 || msg->rpkt->length > RS_MAX_PACKET_LEN) - { - conn_close (&msg->conn); - return rs_err_conn_push (msg->conn, RSE_INVALID_MSG, - "invalid message length: %d", - msg->rpkt->length); - } + return rs_err_conn_push (msg->conn, RSE_INVALID_MSG, + "invalid message length: %d", + msg->rpkt->length); memcpy (msg->rpkt->data, msg->hdr, RS_HEADER_LEN); - bufferevent_setwatermark (msg->conn->base_.bev, EV_READ, + bufferevent_setwatermark (TO_BASE_CONN(msg->conn)->bev, EV_READ, msg->rpkt->length - RS_HEADER_LEN, 0); rs_debug (("%s: message header read, total msg len=%d\n", __func__, msg->rpkt->length)); } else if (n < 0) - { - rs_debug (("%s: buffer frozen while reading header\n", __func__)); - } + rs_debug (("%s: buffer frozen while reading header\n", __func__)); else /* Error: libevent gave us less than the low watermark. */ - { - conn_close (&msg->conn); - return rs_err_conn_push_fl (msg->conn, RSE_INTERNAL, __FILE__, __LINE__, - "got %d octets reading header", n); - } - - return 0; + return rs_err_conn_push_fl (msg->conn, RSE_INTERNAL, __FILE__, __LINE__, + "got %d octets reading header", n); + return RSE_OK; } /** Read a message, check that it's valid RADIUS and hand it off to @@ -98,12 +89,9 @@ _read_message (struct rs_message *msg) - attribute lengths >= 2 - attribute sizes adding up correctly */ err = nr_packet_ok (msg->rpkt); - if (err != RSE_OK) - { - conn_close (&msg->conn); - return rs_err_conn_push_fl (msg->conn, err, __FILE__, __LINE__, - "invalid message"); - } + if (err) + return rs_err_conn_push_fl (msg->conn, err, __FILE__, __LINE__, + "invalid message"); #if defined (DEBUG) /* Find out what happens if there's data left in the buffer. */ @@ -133,11 +121,11 @@ _read_message (struct rs_message *msg) /* The read callback for TCP. - Read exactly one RADIUS message from BEV and store it in struct - rs_message passed in USER_DATA. + Read exactly one RADIUS message from \a bev and store it in the + struct rs_message passed in \a user_data. Inform upper layer about successful reception of received RADIUS - message by invoking conn->callbacks.recevied_cb(), if !NULL. */ + message by invoking conn->callbacks.recevied_cb(), if not NULL. */ void tcp_read_cb (struct bufferevent *bev, void *user_data) { @@ -148,7 +136,7 @@ tcp_read_cb (struct bufferevent *bev, void *user_data) assert (msg->rpkt); msg->rpkt->sockfd = msg->conn->base_.fd; - msg->rpkt->vps = NULL; /* FIXME: can this be done when initializing msg? */ + msg->rpkt->vps = NULL; /* FIXME: can this be done when initializing msg? */ /* Read a message header if not already read, return if that fails. Read a message and have it dispatched to the user @@ -158,8 +146,9 @@ tcp_read_cb (struct bufferevent *bev, void *user_data) avoid the extra copying. */ if ((msg->flags & RS_MESSAGE_HEADER_READ) == 0) if (_read_header (msg)) - return; /* Error. */ - _read_message (msg); + return; /* Invalid header. */ + if (_read_message (msg)) + return; /* Invalid message. */ } void @@ -179,15 +168,27 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data) assert (msg->conn); conn = msg->conn; #if defined (DEBUG) - assert (msg->conn->base_.active_peer); - p = conn->base_.active_peer; + assert (conn->active_peer); + p = conn->active_peer; #endif if (events & BEV_EVENT_CONNECTED) { - if (conn->tev) - evtimer_del (conn->tev); /* Cancel connect timer. */ - if (event_on_connect (conn, msg)) + int err = -1; + + if (conn_originating_p (conn)) /* We're a client. */ + { + assert (conn->tev); + if (conn->tev) + evtimer_del (conn->tev); /* Cancel connect timer. */ + err = event_on_connect_orig (conn, msg); + } + else /* We're a server. */ + { + assert (conn->tev == NULL); + err = event_on_connect_term (conn, msg); + } + if (err) { event_on_disconnect (conn); event_loopbreak (conn); diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am index 33ddb51..30f5e0f 100644 --- a/lib/tests/Makefile.am +++ b/lib/tests/Makefile.am @@ -4,9 +4,13 @@ AM_CFLAGS = -Wall -Werror -g TESTS = test-udp -check_PROGRAMS = test-udp udp-server +check_PROGRAMS = test-udp udp-server tls-server test_udp_SOURCES = test-udp.c udp.c test_udp_LDADD = ../libradsec.la -lcgreen -lm udp_server_SOURCES = udp-server.c udp.c + +tls_server_SOURCES = server.c +tls_server_LDADD = ../libradsec.la + diff --git a/lib/tls.c b/lib/tls.c index b9fb3cf..788bf5c 100644 --- a/lib/tls.c +++ b/lib/tls.c @@ -20,20 +20,20 @@ static struct tls * _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm) { - struct tls *c = rs_malloc (conn->base_.ctx, sizeof (struct tls)); + struct tls *c = rs_calloc (conn->base_.ctx, 1, sizeof (struct tls)); if (c) { - memset (c, 0, sizeof (struct tls)); + assert (realm); /* _conn_open() should've picked a peer by now. */ - assert (conn->base_.active_peer); + assert (conn->active_peer); /* TODO: Make sure old radsecproxy code doesn't free these all of a sudden, or strdup them. */ c->name = realm->name; - c->cacertfile = conn->base_.active_peer->cacertfile; + c->cacertfile = conn->active_peer->cacertfile; c->cacertpath = NULL; /* NYI */ - c->certfile = conn->base_.active_peer->certfile; - c->certkeyfile = conn->base_.active_peer->certkeyfile; + c->certfile = conn->active_peer->certfile; + c->certkeyfile = conn->active_peer->certkeyfile; c->certkeypwd = NULL; /* NYI */ c->cacheexpiry = 0; /* NYI */ c->crlcheck = 0; /* NYI */ @@ -60,7 +60,7 @@ psk_client_cb (SSL *ssl, conn = SSL_get_ex_data (ssl, 0); assert (conn != NULL); - cred = conn->base_.active_peer->transport_cred; + cred = conn->active_peer->transport_cred; assert (cred != NULL); /* NOTE: Ignoring identity hint from server. */ @@ -125,8 +125,9 @@ rs_tls_init (struct rs_connection *conn) assert (conn->base_.ctx); ctx = conn->base_.ctx; + assert (conn->active_peer); - tlsconf = _get_tlsconf (conn, conn->base_.active_peer->realm); + tlsconf = _get_tlsconf (conn, conn->active_peer->realm); if (!tlsconf) return -1; ssl_ctx = tlsgetctx (RAD_TLS, tlsconf); @@ -147,7 +148,7 @@ rs_tls_init (struct rs_connection *conn) } #if defined RS_ENABLE_TLS_PSK - if (conn->base_.active_peer->transport_cred != NULL) + if (conn->active_peer->transport_cred != NULL) { SSL_set_psk_client_callback (ssl, psk_client_cb); SSL_set_ex_data (ssl, 0, conn); @@ -203,9 +204,9 @@ tls_verify_cert (struct rs_connection *conn) struct in6_addr addr; const char *hostname = NULL; - assert (conn->base_.active_peer != NULL); - assert (conn->base_.active_peer->hostname != NULL); - hostname = conn->base_.active_peer->hostname; + assert (conn->active_peer != NULL); + 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. */ diff --git a/lib/udp.c b/lib/udp.c index 22a8375..de78fed 100644 --- a/lib/udp.c +++ b/lib/udp.c @@ -16,6 +16,7 @@ #include "event.h" #include "compat.h" #include "udp.h" +#include "conn.h" /* Send one packet, the first in queue. */ static int @@ -55,16 +56,16 @@ _send (struct rs_connection *conn, int fd) return RSE_OK; } -/* Callback for conn->wev and conn->rev. FIXME: Rename. +/** Callback for conn->wev and conn->rev. FIXME: Rename. - USER_DATA contains connection for EV_READ and a message for - EV_WRITE. This is because we don't have a connect/establish entry - point at the user level -- send implies connect so when we're - connected we need the message to send. */ + \a user_data holds a message. */ static void _evcb (evutil_socket_t fd, short what, void *user_data) { int err; + struct rs_message *msg = (struct rs_message *) user_data; + assert (msg); + assert (msg->conn); rs_debug (("%s: fd=%d what =", __func__, fd)); if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT")); @@ -74,13 +75,11 @@ _evcb (evutil_socket_t fd, short what, void *user_data) if (what & EV_READ) { - /* Read a single UDP packet and stick it in USER_DATA. */ + /* Read a single UDP packet and stick it in the struct + rs_message passed in user_data. */ /* TODO: Verify that unsolicited packets are dropped. */ - struct rs_message *msg = (struct rs_message *) user_data; ssize_t r = 0; - - assert (msg); - assert (msg->conn); + assert (msg->rpkt); assert (msg->rpkt->data); r = compat_recv (fd, msg->rpkt->data, RS_MAX_PACKET_LEN, MSG_TRUNC); @@ -96,9 +95,9 @@ _evcb (evutil_socket_t fd, short what, void *user_data) } /* Hard error. */ - rs_err_conn_push_fl (msg->conn, RSE_SOCKERR, __FILE__, __LINE__, - "%d: recv: %d (%s)", fd, sockerr, - evutil_socket_error_to_string (sockerr)); + rs_err_conn_push (msg->conn, RSE_SOCKERR, + "%d: recv: %d (%s)", fd, sockerr, + evutil_socket_error_to_string (sockerr)); event_del (msg->conn->tev); return; } @@ -125,12 +124,18 @@ _evcb (evutil_socket_t fd, short what, void *user_data) } else if (what & EV_WRITE) { - struct rs_message *msg = (struct rs_message *) user_data; - assert (msg); - assert (msg->conn); - - if (msg->conn->state == RS_CONN_STATE_CONNECTING) - event_on_connect (msg->conn, msg); + if (conn_originating_p (msg->conn)) + { + /* We're a client. */ + if (msg->conn->state == RS_CONN_STATE_CONNECTING) + event_on_connect_orig (msg->conn, msg); + } + else + { + /* We're a server. */ + rs_debug (("%s: write event on terminating conn %p\n", + __func__, msg->conn)); + } if (msg->conn->out_queue) if (_send (msg->conn, fd) == RSE_OK) @@ -149,10 +154,12 @@ udp_init (struct rs_connection *conn, struct rs_message *msg) { assert (!conn->base_.bev); + /* FIXME: Explain why we set EV_PERSIST on the read event but not on + the write event. */ conn->base_.rev = event_new (conn->base_.ctx->evb, conn->base_.fd, - EV_READ|EV_PERSIST, _evcb, NULL); + EV_READ|EV_PERSIST, _evcb, NULL); conn->base_.wev = event_new (conn->base_.ctx->evb, conn->base_.fd, - EV_WRITE, _evcb, NULL); + EV_WRITE, _evcb, NULL); if (!conn->base_.rev || !conn->base_.wev) { if (conn->base_.rev)