- Application runs its own event loop, using fd's for select and
performs I/O using the libradsec send/receive calls
(a.k.a. on-your-own mode)
- - Fully reentrant (FIXME: issues with libfreeradius-radius?)
- User chooses allocation regime
+Note that as of 0.0.2.dev libradsec suffers from way too much focus on
+the behaviour of a blocking client and is totally useless as a server.
+Not only does it lack most of the functions needed for writing a
+server but it also contains at least one architectural mishap which
+kills the server idea. A connection timeout (TCP) or a retransmit
+timeout (UDP) will result in the event loop being broken. The same is
+thing will happen if there's an error on a TCP connection, f.ex. a
+failing certificate validation (TLS).
* Dependencies
Details apply to Ubuntu 10.10.
- - libfreeradius-radius (2.1.9+dfsg-1ubuntu1)
- sudo apt-get install libfreeradius-dev libfreeradius2
- libconfuse (2.7-1)
sudo apt-get install libconfuse-dev libconfuse0
- libevent from source (release-2.0.10-stable)
- [TCP] short read
- [TCP] short write
- [TLS] basic tls support
+- [TLS] preshared key support
+- [TLS] verification of CN
** Known issues
- error stack is only one entry deep
- custom allocation scheme is not used in all places
** Not implemented
- server failover
-- [TLS] verification of CN
-- [TLS] preshared key support
- [DTLS] support
* Found a bug?
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
- SUBDIRS = . examples include
+# Shared library interface version, i.e. -version-info to Libtool,
+# expressed as three integers CURRENT:REVISION:AGE.
+
+# CURRENT is the version number of the current interface. Increment
+# CURRENT when the library interface changes.
+
+# REVISION is the version number of the _implementation_ of the
+# CURRENT interface. Set REVISION to 0 when CURRENT changes,
+# else increment.
+
+# AGE is the number of interfaces this library implements, i.e. how
+# many versions before CURRENT that are supported. Increment AGE
+# when the library interface is _extended_. Set AGE to 0 when the
+# library interface is _changed_.
+
+
+ SUBDIRS = radius . include examples
INCLUDES = -I$(srcdir)/include
AM_CFLAGS = -Wall -g
lib_LTLIBRARIES = libradsec.la
libradsec_la_SOURCES = \
+ avp.c \
compat.c \
conf.c \
conn.c \
request.c \
send.c \
tcp.c \
- udp.c
+ udp.c \
+ util.c
libradsec_la_SOURCES += \
rsp_debug.c \
rsp_tlscommon.c
endif
+ libradsec_la_LIBADD = radius/libradsec-radius.la
libradsec_la_LDFLAGS = -version-info 0:0:0 -export-symbols radsec.sym
- libradsec_la_CFLAGS = $(AM_CFLAGS) -DDEBUG -DDEBUG_LEVENT
+ libradsec_la_CFLAGS = $(AM_CFLAGS) -DHAVE_CONFIG_H -Werror # -DDEBUG -DDEBUG_LEVENT
#endif
#include <confuse.h>
+ #include <stdlib.h>
#include <string.h>
+#include <assert.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
#include "peer.h"
+#include "util.h"
#include "debug.h"
#if 0
# common config options
- dictionary = STRING
# common realm config options
realm NAME {
#cacertpath = STRING
certfile = STRING
certkeyfile = STRING
+ pskstr = STRING # Transport pre-shared key, UTF-8 form.
+ pskhexstr = STRING # Transport pre-shared key, ASCII hex form.
+ pskid = STRING
+ pskex = "PSK"|"DHE_PSK"|"RSA_PSK"
}
# client specific realm config options
server {
hostname = STRING
service = STRING
- secret = STRING
+ secret = STRING # RADIUS secret
}
}
#endif
-/* FIXME: Leaking memory in error cases? */
+/* FIXME: Leaking memory in error cases. */
int
rs_context_read_config(struct rs_context *ctx, const char *config_file)
{
/*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/
CFG_STR ("certfile", NULL, CFGF_NONE),
CFG_STR ("certkeyfile", NULL, CFGF_NONE),
+ CFG_STR ("pskstr", NULL, CFGF_NONE),
+ CFG_STR ("pskhexstr", NULL, CFGF_NONE),
+ CFG_STR ("pskid", NULL, CFGF_NONE),
+ CFG_STR ("pskex", "PSK", CFGF_NONE),
CFG_SEC ("server", server_opts, CFGF_MULTI),
CFG_END ()
};
cfg_opt_t opts[] =
{
- CFG_STR ("dictionary", NULL, CFGF_NONE),
CFG_SEC ("realm", realm_opts, CFGF_TITLE | CFGF_MULTI),
CFG_END ()
};
if (config == NULL)
return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
ctx->config = config;
- config->dictionary = cfg_getstr (cfg, "dictionary");
for (i = 0; i < cfg_size (cfg, "realm"); i++)
{
struct rs_realm *r = NULL;
const char *typestr;
+ char *pskstr = NULL, *pskhexstr = NULL;
r = rs_calloc (ctx, 1, sizeof(*r));
if (r == NULL)
config->realms = r;
}
cfg_realm = cfg_getnsec (cfg, "realm", i);
- /* We use a copy of the return value of cfg_title() since it's const. */
s = cfg_title (cfg_realm);
if (s == NULL)
return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
"missing realm name");
- r->name = strdup (s);
+ /* We use a copy of the return value of cfg_title() since it's const. */
+ r->name = rs_strdup (ctx, s);
if (r->name == NULL)
- return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
+ return RSE_NOMEM;
typestr = cfg_getstr (cfg_realm, "type");
if (strcmp (typestr, "UDP") == 0)
else if (strcmp (typestr, "DTLS") == 0)
r->type = RS_CONN_TYPE_DTLS;
else
- return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
- "invalid connection type: %s", typestr);
+ return rs_err_ctx_push (ctx, RSE_CONFIG,
+ "%s: invalid connection type: %s",
+ r->name, typestr);
r->timeout = cfg_getint (cfg_realm, "timeout");
r->retries = cfg_getint (cfg_realm, "retries");
r->certfile = cfg_getstr (cfg_realm, "certfile");
r->certkeyfile = cfg_getstr (cfg_realm, "certkeyfile");
+ pskstr = cfg_getstr (cfg_realm, "pskstr");
+ pskhexstr = cfg_getstr (cfg_realm, "pskhexstr");
+ if (pskstr || pskhexstr)
+ {
+#if defined RS_ENABLE_TLS_PSK
+ char *kex = cfg_getstr (cfg_realm, "pskex");
+ rs_cred_type_t type = RS_CRED_NONE;
+ struct rs_credentials *cred = NULL;
+ assert (kex != NULL);
+
+ if (!strcmp (kex, "PSK"))
+ type = RS_CRED_TLS_PSK;
+ else
+ {
+ /* TODO: push a warning on the error stack:*/
+ /*rs_err_ctx_push (ctx, RSE_WARN, "%s: unsupported PSK key exchange"
+ " algorithm -- PSK not used", kex);*/
+ }
+
+ if (type != RS_CRED_NONE)
+ {
+ cred = rs_calloc (ctx, 1, sizeof (*cred));
+ if (cred == NULL)
+ return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
+ NULL);
+ cred->type = type;
+ cred->identity = cfg_getstr (cfg_realm, "pskid");
+ if (pskhexstr)
+ {
+ cred->secret_encoding = RS_KEY_ENCODING_ASCII_HEX;
+ cred->secret = pskhexstr;
+ if (pskstr)
+ ; /* TODO: warn that we're ignoring pskstr */
+ }
+ else
+ {
+ cred->secret_encoding = RS_KEY_ENCODING_UTF8;
+ cred->secret = pskstr;
+ }
+
+ r->transport_cred = cred;
+ }
+#else /* !RS_ENABLE_TLS_PSK */
+ /* TODO: push a warning on the error stack: */
+ /* rs_err_ctx_push (ctx, RSE_WARN, "libradsec wasn't configured with "
+ "support for TLS preshared keys, ignoring pskstr "
+ "and pskhexstr");*/
+#endif /* RS_ENABLE_TLS_PSK */
+ }
+
+ /* For TLS and DTLS realms, validate that we either have (i) CA
+ cert file or path or (ii) PSK. */
+ if ((r->type == RS_CONN_TYPE_TLS || r->type == RS_CONN_TYPE_DTLS)
+ && (r->cacertfile == NULL && r->cacertpath == NULL)
+ && r->transport_cred == NULL)
+ return rs_err_ctx_push (ctx, RSE_CONFIG,
+ "%s: missing both CA file/path and PSK",
+ r->name);
+
/* Add peers, one per server stanza. */
for (j = 0; j < cfg_size (cfg_realm, "server"); j++)
{
p->realm = r;
cfg_server = cfg_getnsec (cfg_realm, "server", j);
- /* FIXME: Handle resolve errors, possibly by postponing name
- resolution. */
- rs_resolv (&p->addr, r->type, cfg_getstr (cfg_server, "hostname"),
- cfg_getstr (cfg_server, "service"));
+ p->hostname = cfg_getstr (cfg_server, "hostname");
+ p->service = cfg_getstr (cfg_server, "service");
p->secret = cfg_getstr (cfg_server, "secret");
}
}
AC_MSG_ERROR([required library libconfuse not found]))
AC_CHECK_LIB([event_core], [event_get_version],,
AC_MSG_ERROR([required library libevent_core not found]))
- AC_CHECK_LIB([freeradius-radius], [rad_alloc],,
- AC_MSG_ERROR([required library libfreeradius-radius not found]))
# Enable-knobs.
+## Enable TLS (RadSec).
AH_TEMPLATE([RS_ENABLE_TLS], [TLS (RadSec) enabled])
-AH_TEMPLATE([RADPROT_TLS], [])
+AH_TEMPLATE([RADPROT_TLS], []) dnl Legacy.
AC_ARG_ENABLE([tls], AS_HELP_STRING([--enable-tls], [enable TLS (RadSec)]),
[AC_CHECK_LIB([event_openssl], [bufferevent_openssl_socket_new],,
AC_MSG_ERROR([required library event_openssl not found]))
AC_DEFINE([RS_ENABLE_TLS])
- AC_DEFINE([RADPROT_TLS])])
+ AC_DEFINE([RADPROT_TLS])]) dnl Legacy.
AM_CONDITIONAL([RS_ENABLE_TLS], [test "${enable_tls+set}" = set])
+## Enable TLS-PSK (preshared keys).
+AH_TEMPLATE([RS_ENABLE_TLS_PSK], [TLS-PSK (TLS preshared keys) enabled])
+AC_ARG_ENABLE([tls-psk], AS_HELP_STRING([--enable-tls-psk], [enable TLS-PSK (TLS preshared keys)]),
+ [AC_CHECK_LIB([ssl], [SSL_set_psk_client_callback],,
+ AC_MSG_ERROR([required library openssl with SSL_set_psk_client_callback() not found]))
+ AC_DEFINE([RS_ENABLE_TLS_PSK])])
+AM_CONDITIONAL([RS_ENABLE_TLS_PSK], [test "${enable_tls_psk+set}" = set])
# Checks for header files.
AC_CHECK_HEADERS(
- [netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h unistd.h])
+ [sys/time.h time.h netdb.h netinet/in.h stdint.h stdlib.h strings.h string.h \
+ sys/socket.h unistd.h syslog.h sys/select.h fcntl.h arpa/inet.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
AC_CHECK_FUNCS([memset socket strdup strerror strrchr])
AC_CONFIG_FILES([Makefile
+ radius/Makefile
include/Makefile
examples/Makefile
tests/Makefile])
#endif
#include <string.h>
+ #include <stdlib.h>
+ #include <errno.h>
#include <assert.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
assert (conn);
assert (conn->realm);
- assert (!conn_user_dispatch_p (conn)); /* Dispatching mode only. */
+ assert (!conn_user_dispatch_p (conn)); /* Blocking mode only. */
if (rs_packet_create (conn, &pkt))
return -1;
"event_add: %s",
evutil_gai_strerror (err));
- /* Activae retransmission timer. */
+ /* Activate retransmission timer. */
conn_activate_timeout (pkt->conn);
}
|| (req_msg
&& packet_verify_response (pkt->conn, pkt, req_msg) != RSE_OK))
{
- assert (rs_err_conn_peek_code (pkt->conn));
+ if (rs_err_conn_peek_code (pkt->conn) == RSE_OK)
+ /* No packet and no error on the stack _should_ mean that the
+ server hung up on us. */
+ rs_err_conn_push (pkt->conn, RSE_DISCO, "no response");
return rs_err_conn_peek_code (conn);
}
#endif
#include <stdio.h>
+ #include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
static const char *_errtxt[] = {
- "SUCCESS", /* 0 RSE_OK */
- "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 */
- "FreeRadius error", /* 6 RSE_FR */
- "bad hostname or port", /* 7 RSE_BADADDR */
- "no peer configured", /* 8 RSE_NOPEER */
- "libevent error", /* 9 RSE_EVENT */
- "socket error", /* 10 RSE_SOCKERR */
- "invalid configuration file", /* 11 RSE_CONFIG */
- "authentication failed", /* 12 RSE_BADAUTH */
- "internal error", /* 13 RSE_INTERNAL */
- "SSL error", /* 14 RSE_SSLERR */
- "invalid packet", /* 15 RSE_INVALID_PKT */
- "connect timeout", /* 16 RSE_TIMEOUT_CONN */
- "invalid argument", /* 17 RSE_INVAL */
- "I/O timeout", /* 18 RSE_TIMEOUT_IO */
- "timeout", /* 19 RSE_TIMEOUT */
- "peer disconnected", /* 20 RSE_DISCO */
- "invalid credentials", /* 21 RSE_CRED */
- "certificate validation error", /* 22 RSE_CERT */
+ "SUCCESS", /* 0 RSE_OK */
+ "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 */
+ "FreeRadius error", /* 6 RSE_FR */
+ "bad hostname or port", /* 7 RSE_BADADDR */
+ "no peer configured", /* 8 RSE_NOPEER */
+ "libevent error", /* 9 RSE_EVENT */
+ "socket error", /* 10 RSE_SOCKERR */
+ "invalid configuration file", /* 11 RSE_CONFIG */
+ "authentication failed", /* 12 RSE_BADAUTH */
+ "internal error", /* 13 RSE_INTERNAL */
+ "SSL error", /* 14 RSE_SSLERR */
+ "invalid packet", /* 15 RSE_INVALID_PKT */
+ "connect timeout", /* 16 RSE_TIMEOUT_CONN */
+ "invalid argument", /* 17 RSE_INVAL */
+ "I/O timeout", /* 18 RSE_TIMEOUT_IO */
+ "timeout", /* 19 RSE_TIMEOUT */
+ "peer disconnected", /* 20 RSE_DISCO */
+ "resource is in use", /* 21 RSE_INUSE */
+ "packet is too small", /* 22 RSE_PACKET_TOO_SMALL */
+ "packet is too large", /* 23 RSE_PACKET_TOO_LARGE */
+ "attribute overflows packet", /* 24 RSE_ATTR_OVERFLOW */
+ "attribute is too small", /* 25 RSE_ATTR_TOO_SMALL */
+ "attribute is too large", /* 26 RSE_ATTR_TOO_LARGE */
+ "unknown attribute", /* 27 RSE_ATTR_UNKNOWN */
+ "invalid name for attribute", /* 28 RSE_ATTR_BAD_NAME */
+ "invalid value for attribute", /* 29 RSE_ATTR_VALUE_MALFORMED */
+ "invalid attribute", /* 30 RSE_ATTR_INVALID */
+ "too many attributes in the packet", /* 31 RSE_TOO_MANY_ATTRS */
+ "attribute type unknown", /* 32 RSE_ATTR_TYPE_UNKNOWN */
+ "invalid message authenticator", /* 33 RSE_MSG_AUTH_LEN */
+ "incorrect message authenticator", /* 34 RSE_MSG_AUTH_WRONG */
+ "request is required", /* 35 RSE_REQUEST_REQUIRED */
+ "invalid request code", /* 36 RSE_REQUEST_CODE_INVALID */
+ "incorrect request authenticator", /* 37 RSE_AUTH_VECTOR_WRONG */
+ "response code is unsupported", /* 38 RSE_INVALID_RESPONSE_CODE */
+ "response ID is invalid", /* 39 RSE_INVALID_RESPONSE_ID */
+ "response from the wrong source address", /* 40 RSE_INVALID_RESPONSE_SRC */
+ "no packet data", /* 41 RSE_NO_PACKET_DATA */
+ "vendor is unknown", /* 42 RSE_VENDOR_UNKNOWN */
++ "invalid credentials", /* 43 RSE_CRED */
++ "certificate validation error", /* 44 RSE_CERT */
};
#define ERRTXT_SIZE (sizeof(_errtxt) / sizeof(*_errtxt))
#endif
#include <assert.h>
+ #include <string.h>
+ #include <errno.h>
+
#include <event2/event.h>
#include <event2/bufferevent.h>
#if defined (RS_ENABLE_TLS)
#if defined (RS_ENABLE_TLS)
#include "tls.h"
#endif
+#include "err.h"
+#include "radsec.h"
#include "event.h"
#include "packet.h"
#include "conn.h"
if (conn->fd != -1)
return RSE_OK;
- assert (p->addr);
- conn->fd = socket (p->addr->ai_family, p->addr->ai_socktype,
- p->addr->ai_protocol);
+ if (p->addr_cache == NULL)
+ {
+ struct rs_error *err =
+ rs_resolve (&p->addr_cache, p->realm->type, p->hostname, p->service);
+ if (err != NULL)
+ return err_conn_push_err (conn, err);
+ }
+
+ conn->fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype,
+ p->addr_cache->ai_protocol);
if (conn->fd < 0)
return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
"socket: %d (%s)",
{
char host[80], serv[80];
- getnameinfo (p->addr->ai_addr,
- p->addr->ai_addrlen,
+ getnameinfo (p->addr_cache->ai_addr,
+ p->addr_cache->ai_addrlen,
host, sizeof(host), serv, sizeof(serv),
0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
rs_debug (("%s: connecting to %s:%s\n", __func__, host, serv));
if (p->conn->bev) /* TCP */
{
conn_activate_timeout (conn); /* Connect timeout. */
- err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr,
- p->addr->ai_addrlen);
+ err = bufferevent_socket_connect (p->conn->bev, p->addr_cache->ai_addr,
+ p->addr_cache->ai_addrlen);
if (err < 0)
rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__,
"bufferevent_socket_connect: %s",
}
else /* UDP */
{
- err = connect (p->conn->fd, p->addr->ai_addr, p->addr->ai_addrlen);
+ err = connect (p->conn->fd,
+ p->addr_cache->ai_addr,
+ p->addr_cache->ai_addrlen);
if (err < 0)
{
sockerr = evutil_socket_geterror (p->conn->fd);
conn->callbacks.disconnected_cb (conn->user_data);
}
-void
+/** Internal connect event returning 0 on success or -1 on error. */
+int
event_on_connect (struct rs_connection *conn, struct rs_packet *pkt)
{
assert (!conn->is_connecting);
+
+#if defined (RS_ENABLE_TLS)
+ if (conn->realm->type == RS_CONN_TYPE_TLS
+ || conn->realm->type == RS_CONN_TYPE_DTLS)
+ if (tls_verify_cert (conn) != RSE_OK)
+ {
+ rs_debug (("%s: server cert verification failed\n", __func__));
+ return -1;
+ }
+#endif /* RS_ENABLE_TLS */
+
conn->is_connected = 1;
rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
if (pkt)
packet_do_send (pkt);
+
+ return 0;
}
int
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
+#include <assert.h>
#include <event2/event.h>
- #include <freeradius/libradius.h>
#include <radsec/radsec.h>
+ #include <radsec/radsec-impl.h>
#include <radsec/request.h>
+#include "err.h"
#include "debug.h" /* For rs_dump_packet(). */
#define SECRET "sikrit"
#define USER_PW "password"
struct rs_error *
-blocking_client (const char *av1, const char *av2, int use_request_object_flag)
+blocking_client (const char *config_fn, const char *configuration,
+ int use_request_object_flag)
{
struct rs_context *h = NULL;
struct rs_connection *conn = NULL;
struct rs_error *err = NULL;
if (rs_context_create (&h))
- return NULL;
+ {
+ err = err_create (RSE_INTERNAL, NULL, 0, "unable to create context");
+ assert (err != NULL);
+ return err;
+ }
#if !defined (USE_CONFIG_FILE)
{
struct rs_peer *server;
- if (rs_context_init_freeradius_dict (h, "/usr/share/freeradius/dictionary"))
- goto cleanup;
if (rs_conn_create (h, &conn, NULL))
goto cleanup;
rs_conn_set_type (conn, RS_CONN_TYPE_UDP);
goto cleanup;
}
#else /* defined (USE_CONFIG_FILE) */
- if (rs_context_read_config (h, av1))
+ if (rs_context_read_config (h, config_fn))
goto cleanup;
- if (rs_context_init_freeradius_dict (h, NULL))
- goto cleanup;
- if (rs_conn_create (h, &conn, av2))
+ if (rs_conn_create (h, &conn, configuration))
goto cleanup;
#endif /* defined (USE_CONFIG_FILE) */
if (resp)
{
rs_dump_packet (resp);
- if (rs_packet_frpkt (resp)->code == PW_AUTHENTICATION_ACK)
+ if (rs_packet_code (resp) == PW_ACCESS_ACCEPT)
printf ("Good auth.\n");
else
- printf ("Bad auth: %d\n", rs_packet_frpkt (resp)->code);
+ printf ("Bad auth: %d\n", rs_packet_code (resp));
}
else
fprintf (stderr, "%s: no response\n", __func__);
return err;
}
+void
+usage (int argc, char *argv[])
+{
+ fprintf (stderr, "usage: %s: [-r] config-file config-name\n", argv[0]);
+ exit (1);
+}
+
int
main (int argc, char *argv[])
{
argc--;
argv++;
}
+ if (argc < 3)
+ usage (argc, argv);
err = blocking_client (argv[1], argv[2], use_request_object_flag);
if (err)
{
- dictionary = "/home/linus/usr/moonshot/share/freeradius/dictionary"
-
realm blocking-udp {
type = "UDP"
timeout = 2
cacertfile = "tests/demoCA/newcerts/01.pem"
certfile = "tests/demoCA/newcerts/02.pem"
certkeyfile = "tests/demoCA/private/c2key.pem"
+ #pskstr = "sikrit psk"
+ pskhexstr = "deadbeef4711"
+ pskid = "Client_identity"
+ pskex = "PSK"
server {
hostname = "localhost"
service = "2083"
/* See the file COPYING for licensing information. */
- #include <freeradius/libradius.h>
+ #ifndef _RADSEC_RADSEC_IMPL_H_
+ #define _RADSEC_RADSEC_IMPL_H_ 1
+
#include <event2/util.h>
#include <confuse.h>
#if defined(RS_ENABLE_TLS)
/* Data types. */
enum rs_cred_type {
RS_CRED_NONE = 0,
- RS_CRED_TLS_PSK_RSA, /* RFC 4279. */
+ /* TLS pre-shared keys, RFC 4279. */
+ RS_CRED_TLS_PSK,
+ /* RS_CRED_TLS_DH_PSK, */
+ /* RS_CRED_TLS_RSA_PSK, */
};
typedef unsigned int rs_cred_type_t;
+enum rs_key_encoding {
+ RS_KEY_ENCODING_UTF8 = 1,
+ RS_KEY_ENCODING_ASCII_HEX = 2,
+};
+typedef unsigned int rs_key_encoding_t;
+
#if defined (__cplusplus)
extern "C" {
#endif
enum rs_cred_type type;
char *identity;
char *secret;
+ enum rs_key_encoding secret_encoding;
+ unsigned int secret_len;
};
struct rs_error {
struct rs_peer {
struct rs_connection *conn;
struct rs_realm *realm;
- struct evutil_addrinfo *addr;
- char *secret;
+ char *hostname;
+ char *service;
+ char *secret; /* RADIUS secret. */
+ struct evutil_addrinfo *addr_cache;
struct rs_peer *next;
};
char *cacertpath;
char *certfile;
char *certkeyfile;
+ struct rs_credentials *transport_cred;
struct rs_peer *peers;
struct rs_realm *next;
};
/** Top configuration object. */
struct rs_config {
- char *dictionary;
struct rs_realm *realms;
cfg_t *cfg;
};
struct rs_config *config;
struct rs_alloc_scheme alloc_scheme;
struct rs_error *err;
- fr_randctx fr_randctx;
};
struct rs_connection {
struct rs_realm *realm; /* Owned by ctx. */
struct event_base *evb; /* Event base. */
struct event *tev; /* Timeout event. */
- struct rs_credentials transport_credentials;
struct rs_conn_callbacks callbacks;
void *user_data;
struct rs_peer *peers;
rs_packet_sent_flag,
};
+ struct radius_packet;
+
struct rs_packet {
struct rs_connection *conn;
unsigned int flags;
uint8_t hdr[RS_HEADER_LEN];
- RADIUS_PACKET *rpkt; /* FreeRADIUS object. */
+ struct radius_packet *rpkt; /* FreeRADIUS object. */
struct rs_packet *next; /* Used for UDP output queue. */
};
-/* Nonpublic functions (in radsec.c -- FIXME: move?). */
-struct rs_error *rs_resolv (struct evutil_addrinfo **addr,
- rs_conn_type_t type,
- const char *hostname,
- const char *service);
-
#if defined (__cplusplus)
}
#endif
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
+ #endif /* _RADSEC_RADSEC_IMPL_H_ */
+
/* Local Variables: */
/* c-file-style: "stroustrup" */
/* End: */
/* See the file COPYING for licensing information. */
- #include <unistd.h>
- #include <sys/time.h>
+ #ifndef _RADSEC_RADSEC_H_
+ #define _RADSEC_RADSEC_H_ 1
- #ifdef SYSCONFDIR
- #define RS_FREERADIUS_DICT SYSCONFDIR "/raddb/dictionary"
- #else /* !SYSCONFDIR */
- #define RS_FREERADIUS_DICT "/usr/local/raddb/dictionary"
- #endif /* !SYSCONFDIR */
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+ #ifdef HAVE_SYS_TIME_H
+ #include <sys/time.h>
+ #endif
+ #ifdef HAVE_ARPA_INET_H
+ #include <arpa/inet.h>
+ #endif
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+ #ifdef HAVE_STDINT_H
+ #include <stdint.h>
+ #endif
enum rs_error_code {
RSE_OK = 0,
RSE_INVALID_CTX = 3,
RSE_INVALID_CONN = 4,
RSE_CONN_TYPE_MISMATCH = 5,
- RSE_FR = 6, /* FreeRADIUS error. */
+ RSE_FR = 6,
RSE_BADADDR = 7,
RSE_NOPEER = 8,
RSE_EVENT = 9, /* libevent error. */
RSE_TIMEOUT_CONN = 16, /* Connection timeout. */
RSE_INVAL = 17, /* Invalid argument. */
RSE_TIMEOUT_IO = 18, /* I/O timeout. */
- RSE_TIMEOUT= 19, /* High level timeout. */
+ RSE_TIMEOUT = 19, /* High level timeout. */
RSE_DISCO = 20,
- RSE_CRED = 21, /* Credentials. */
- RSE_CERT = 22, /* Cert validation. */
+ RSE_INUSE = 21,
+ RSE_PACKET_TOO_SMALL = 22,
+ RSE_PACKET_TOO_LARGE = 23,
+ RSE_ATTR_OVERFLOW = 24,
+ RSE_ATTR_TOO_SMALL = 25,
+ RSE_ATTR_TOO_LARGE = 26,
+ RSE_ATTR_UNKNOWN = 27,
+ RSE_ATTR_BAD_NAME = 28,
+ RSE_ATTR_VALUE_MALFORMED = 29,
+ RSE_ATTR_INVALID = 30,
+ RSE_TOO_MANY_ATTRS = 31,
+ RSE_ATTR_TYPE_UNKNOWN = 32,
+ RSE_MSG_AUTH_LEN = 33,
+ RSE_MSG_AUTH_WRONG = 34,
+ RSE_REQUEST_REQUIRED = 35,
+ RSE_INVALID_REQUEST_CODE = 36,
+ RSE_AUTH_VECTOR_WRONG = 37,
+ RSE_INVALID_RESPONSE_CODE = 38,
+ RSE_INVALID_RESPONSE_ID = 39,
+ RSE_INVALID_RESPONSE_SRC = 40,
+ RSE_NO_PACKET_DATA = 41,
+ RSE_VENDOR_UNKNOWN = 42,
- RSE_MAX = RSE_VENDOR_UNKNOWN
++ RSE_CRED = 43,
++ RSE_CERT = 44,
++ RSE_MAX = RSE_CERT
};
enum rs_conn_type {
};
typedef unsigned int rs_conn_type_t;
+ typedef enum rs_attr_type_t {
+ RS_TYPE_INVALID = 0, /**< Invalid data type */
+ RS_TYPE_STRING, /**< printable-text */
+ RS_TYPE_INTEGER, /**< a 32-bit unsigned integer */
+ RS_TYPE_IPADDR, /**< an IPv4 address */
+ RS_TYPE_DATE, /**< a 32-bit date, of seconds since January 1, 1970 */
+ RS_TYPE_OCTETS, /**< a sequence of binary octets */
+ RS_TYPE_IFID, /**< an Interface Id */
+ RS_TYPE_IPV6ADDR, /**< an IPv6 address */
+ RS_TYPE_IPV6PREFIX, /**< an IPv6 prefix */
+ RS_TYPE_BYTE, /**< an 8-bit integer */
+ RS_TYPE_SHORT, /**< a 16-bit integer */
+ } rs_attr_type_t;
+
+ #define PW_ACCESS_REQUEST 1
+ #define PW_ACCESS_ACCEPT 2
+ #define PW_ACCESS_REJECT 3
+ #define PW_ACCOUNTING_REQUEST 4
+ #define PW_ACCOUNTING_RESPONSE 5
+ #define PW_ACCOUNTING_STATUS 6
+ #define PW_PASSWORD_REQUEST 7
+ #define PW_PASSWORD_ACK 8
+ #define PW_PASSWORD_REJECT 9
+ #define PW_ACCOUNTING_MESSAGE 10
+ #define PW_ACCESS_CHALLENGE 11
+ #define PW_STATUS_SERVER 12
+ #define PW_STATUS_CLIENT 13
+ #define PW_DISCONNECT_REQUEST 40
+ #define PW_DISCONNECT_ACK 41
+ #define PW_DISCONNECT_NAK 42
+ #define PW_COA_REQUEST 43
+ #define PW_COA_ACK 44
+ #define PW_COA_NAK 45
#if defined (__cplusplus)
extern "C" {
struct rs_conn; /* radsec-impl.h */
struct rs_error; /* radsec-impl.h */
struct rs_peer; /* radsec-impl.h */
- struct radius_packet; /* <freeradius/libradius.h> */
+ struct radius_packet; /* <radius/client.h> */
+ struct value_pair; /* <radius/client.h> */
struct event_base; /* <event2/event-internal.h> */
typedef void *(*rs_calloc_fp) (size_t nmemb, size_t size);
rs_conn_packet_sent_cb sent_cb;
};
+ typedef struct value_pair rs_avp;
+ typedef const struct value_pair rs_const_avp;
/* Function prototypes. */
all other libradsec objects have been freed. */
void rs_context_destroy(struct rs_context *ctx);
- /** Initialize FreeRADIUS dictionary needed for creating packets.
-
- \a ctx Context.
-
- \a dict Optional string with full path to FreeRADIUS dictionary.
- If \a dict is NULL the path to the dictionary file is taken from
- the "dictionary" configuration directive. Note that the
- configuration file must be read prior to using this option (see \a
- rs_context_read_config).
-
- \return RSE_OK (0) on success, RSE_NOMEM on memory allocation
- error and RSE_FR on FreeRADIUS error. */
- int rs_context_init_freeradius_dict(struct rs_context *ctx, const char *dict);
-
/** Set allocation scheme to use. \a scheme is the allocation scheme
to use, see \a rs_alloc_scheme. \return On success, RSE_OK (0) is
returned. On error, !0 is returned and a struct \a rs_error is
rs_err_conn_pop. */
int rs_packet_send(struct rs_packet *pkt, void *user_data);
- /** Return the FreeRADIUS packet associated with packet \a pkt. */
- struct radius_packet *rs_packet_frpkt(struct rs_packet *pkt);
-
/** Create a RADIUS authentication request packet associated with
connection \a conn. Optionally, User-Name and User-Password
attributes are added to the packet using the data in \a user_name
const char *user_name,
const char *user_pw);
+ /*** Append \a tail to packet \a pkt. */
+ int
+ rs_packet_append_avp(struct rs_packet *pkt,
+ unsigned int attribute, unsigned int vendor,
+ const void *data, size_t data_len);
+
+ /*** Get pointer to \a pkt attribute value pairs. */
+ void
+ rs_packet_avps(struct rs_packet *pkt, rs_avp ***vps);
+
+ /*** Get RADIUS packet type of \a pkt. */
+ unsigned int
+ rs_packet_code(struct rs_packet *pkt);
+
+ /*** Get RADIUS AVP from \a pkt. */
+ rs_const_avp *
+ rs_packet_find_avp(struct rs_packet *pkt, unsigned int attr, unsigned int vendor);
+
+ /*** Set packet identifier in \a pkt; returns old identifier */
+ int
+ rs_packet_set_id (struct rs_packet *pkt, int id);
+
/************/
/* Config. */
/************/
char *rs_err_msg(struct rs_error *err);
int rs_err_code(struct rs_error *err, int dofree_flag);
+ /************/
+ /* AVPs. */
+ /************/
+ #define rs_avp_is_string(vp) (rs_avp_typeof(vp) == RS_TYPE_STRING)
+ #define rs_avp_is_integer(vp) (rs_avp_typeof(vp) == RS_TYPE_INTEGER)
+ #define rs_avp_is_ipaddr(vp) (rs_avp_typeof(vp) == RS_TYPE_IPADDR)
+ #define rs_avp_is_date(vp) (rs_avp_typeof(vp) == RS_TYPE_DATE)
+ #define rs_avp_is_octets(vp) (rs_avp_typeof(vp) == RS_TYPE_OCTETS)
+ #define rs_avp_is_ifid(vp) (rs_avp_typeof(vp) == RS_TYPE_IFID)
+ #define rs_avp_is_ipv6addr(vp) (rs_avp_typeof(vp) == RS_TYPE_IPV6ADDR)
+ #define rs_avp_is_ipv6prefix(vp) (rs_avp_typeof(vp) == RS_TYPE_IPV6PREFIX)
+ #define rs_avp_is_byte(vp) (rs_avp_typeof(vp) == RS_TYPE_BYTE)
+ #define rs_avp_is_short(vp) (rs_avp_typeof(vp) == RS_TYPE_SHORT)
+ #define rs_avp_is_tlv(vp) (rs_avp_typeof(vp) == RS_TYPE_TLV)
+
+ /** The maximum length of a RADIUS attribute.
+ *
+ * The RFCs require that a RADIUS attribute transport no more than
+ * 253 octets of data. We add an extra byte for a trailing NUL, so
+ * that the VALUE_PAIR::vp_strvalue field can be handled as a C
+ * string.
+ */
+ #define RS_MAX_STRING_LEN 254
+
+ /** Free the AVP list \a vps */
+ void
+ rs_avp_free(rs_avp **vps);
+
+ /** Return the length of AVP \a vp in bytes */
+ size_t
+ rs_avp_length(rs_const_avp *vp);
+
+ /** Return the type of \a vp */
+ rs_attr_type_t
+ rs_avp_typeof(rs_const_avp *vp);
+
+ /** Retrieve the attribute and vendor ID of \a vp */
+ void
+ rs_avp_attrid(rs_const_avp *vp, unsigned int *attr, unsigned int *vendor);
+
+ /** Add \a vp to the list pointed to by \a head */
+ void
+ rs_avp_append(rs_avp **head, rs_avp *vp);
+
+ /** Find an AVP in \a vp that matches \a attr and \a vendor */
+ rs_avp *
+ rs_avp_find(rs_avp *vp, unsigned int attr, unsigned int vendor);
+
+ /** Find an AVP in \a vp that matches \a attr and \a vendor */
+ rs_const_avp *
+ rs_avp_find_const(rs_const_avp *vp, unsigned int attr, unsigned int vendor);
+
+ /** Alloc a new AVP for \a attr and \a vendor */
+ rs_avp *
+ rs_avp_alloc(unsigned int attr, unsigned int vendor);
+
+ /** Duplicate existing AVP \a vp */
+ rs_avp *
+ rs_avp_dup(rs_const_avp *vp);
+
+ /** Remove matching AVP from list \a vps */
+ int
+ rs_avp_delete(rs_avp **vps, unsigned int attr, unsigned int vendor);
+
+ /** Return next AVP in list */
+ rs_avp *
+ rs_avp_next(rs_avp *vp);
+
+ /** Return next AVP in list */
+ rs_const_avp *
+ rs_avp_next_const(rs_const_avp *avp);
+
+ /** Return string value of \a vp */
+ const char *
+ rs_avp_string_value(rs_const_avp *vp);
+
+ /** Set AVP \a vp to string \a str */
+ int
+ rs_avp_string_set(rs_avp *vp, const char *str);
+
+ /** Return integer value of \a vp */
+ uint32_t
+ rs_avp_integer_value(rs_const_avp *vp);
+
+ /** Set AVP \a vp to integer \a val */
+ int
+ rs_avp_integer_set(rs_avp *vp, uint32_t val);
+
+ /** Return IPv4 value of \a vp */
+ uint32_t
+ rs_avp_ipaddr_value(rs_const_avp *vp);
+
+ /** Set AVP \a vp to IPv4 address \a in */
+ int
+ rs_avp_ipaddr_set(rs_avp *vp, struct in_addr in);
+
+ /** Return POSIX time value of \a vp */
+ time_t
+ rs_avp_date_value(rs_const_avp *vp);
+
+ /** Set AVP \a vp to POSIX time \a date */
+ int
+ rs_avp_date_set(rs_avp *vp, time_t date);
+
+ /** Return constant pointer to octets in \a vp */
+ const unsigned char *
+ rs_avp_octets_value_const_ptr(rs_const_avp *vp);
+
+ /** Return pointer to octets in \a vp */
+ unsigned char *
+ rs_avp_octets_value_ptr(rs_avp *vp);
+
+ /** Retrieve octet pointer \a p and length \a len from \a vp */
+ int
+ rs_avp_octets_value_byref(rs_avp *vp,
+ unsigned char **p,
+ size_t *len);
+
+ /** Copy octets from \a vp into \a buf and \a len */
+ int
+ rs_avp_octets_value(rs_const_avp *vp,
+ unsigned char *buf,
+ size_t *len);
+
+ /**
+ * Copy octets possibly fragmented across multiple VPs
+ * into \a buf and \a len
+ */
+ int
+ rs_avp_fragmented_value(rs_const_avp *vps,
+ unsigned char *buf,
+ size_t *len);
+
+ /** Copy \a len octets in \a buf to AVP \a vp */
+ int
+ rs_avp_octets_set(rs_avp *vp,
+ const unsigned char *buf,
+ size_t len);
+
+ /** Return IFID value of \a vp */
+ int
+ rs_avp_ifid_value(rs_const_avp *vp, uint8_t val[8]);
+
+ int
+ rs_avp_ifid_set(rs_avp *vp, const uint8_t val[8]);
+
+ /** Return byte value of \a vp */
+ uint8_t
+ rs_avp_byte_value(rs_const_avp *vp);
+
+ /** Set AVP \a vp to byte \a val */
+ int
+ rs_avp_byte_set(rs_avp *vp, uint8_t val);
+
+ /** Return short value of \a vp */
+ uint16_t
+ rs_avp_short_value(rs_const_avp *vp);
+
+ /** Set AVP \a vp to short integer \a val */
+ int
+ rs_avp_short_set(rs_avp *vp, uint16_t val);
+
+ /** Display possibly \a canonical attribute name into \a buffer */
+ int
+ rs_attr_display_name (unsigned int attr,
+ unsigned int vendor,
+ char *buffer,
+ size_t bufsize,
+ int canonical);
+
+ /** Display AVP \a vp into \a buffer */
+ size_t
+ rs_avp_display_value(rs_const_avp *vp,
+ char *buffer,
+ size_t buflen);
+
+ int
+ rs_attr_parse_name (const char *name,
+ unsigned int *attr,
+ unsigned int *vendor);
+
+ /** Lookup attribute \a name */
+ int
+ rs_attr_find(const char *name,
+ unsigned int *attr,
+ unsigned int *vendor);
+
+ /** Return dictionary name for AVP \a vp */
+ const char *
+ rs_avp_name(rs_const_avp *vp);
+
#if defined (__cplusplus)
}
#endif
+ #endif /* _RADSEC_RADSEC_H_ */
+
/* Local Variables: */
/* c-file-style: "stroustrup" */
/* End: */
#endif
#include <assert.h>
+ #include <radius/client.h>
#include <event2/bufferevent.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
struct rs_packet *response,
struct rs_packet *request)
{
+ int err;
+
assert (conn);
assert (conn->active_peer);
assert (conn->active_peer->secret);
assert (request);
assert (request->rpkt);
+ response->rpkt->secret = conn->active_peer->secret;
+ response->rpkt->sizeof_secret = strlen (conn->active_peer->secret);
+
/* Verify header and message authenticator. */
- if (rad_verify (response->rpkt, request->rpkt, conn->active_peer->secret))
+ err = nr_packet_verify (response->rpkt, request->rpkt);
+ if (err)
{
- conn_close (&conn);
- return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__,
- "rad_verify: %s", fr_strerror ());
+ if (conn->is_connected)
+ rs_conn_disconnect(conn);
+ return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__,
+ "nr_packet_verify");
}
/* Decode and decrypt. */
- if (rad_decode (response->rpkt, request->rpkt, conn->active_peer->secret))
+ err = nr_packet_decode (response->rpkt, request->rpkt);
+ if (err)
{
- conn_close (&conn);
- return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__,
- "rad_decode: %s", fr_strerror ());
+ if (conn->is_connected)
+ rs_conn_disconnect(conn);
+ return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__,
+ "nr_packet_decode");
}
return RSE_OK;
int
packet_do_send (struct rs_packet *pkt)
{
- VALUE_PAIR *vp = NULL;
+ int err;
assert (pkt);
assert (pkt->conn);
assert (pkt->conn->active_peer->secret);
assert (pkt->rpkt);
- /* Add a Message-Authenticator, RFC 2869, if not already present. */
- /* FIXME: Make Message-Authenticator optional? */
- vp = paircreate (PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
- if (!vp)
- return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
- "paircreate: %s", fr_strerror ());
- pairreplace (&pkt->rpkt->vps, vp);
+ pkt->rpkt->secret = pkt->conn->active_peer->secret;
+ pkt->rpkt->sizeof_secret = strlen (pkt->rpkt->secret);
/* Encode message. */
- if (rad_encode (pkt->rpkt, NULL, pkt->conn->active_peer->secret))
- return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
- "rad_encode: %s", fr_strerror ());
+ err = nr_packet_encode (pkt->rpkt, NULL);
+ if (err < 0)
+ return rs_err_conn_push_fl (pkt->conn, -err, __FILE__, __LINE__,
+ "nr_packet_encode");
/* Sign message. */
- if (rad_sign (pkt->rpkt, NULL, pkt->conn->active_peer->secret))
- return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
- "rad_sign: %s", fr_strerror ());
+ err = nr_packet_sign (pkt->rpkt, NULL);
+ if (err < 0)
+ return rs_err_conn_push_fl (pkt->conn, -err, __FILE__, __LINE__,
+ "nr_packet_sign");
#if defined (DEBUG)
{
char host[80], serv[80];
- getnameinfo (pkt->conn->active_peer->addr->ai_addr,
- pkt->conn->active_peer->addr->ai_addrlen,
+ getnameinfo (pkt->conn->active_peer->addr_cache->ai_addr,
+ pkt->conn->active_peer->addr_cache->ai_addrlen,
host, sizeof(host), serv, sizeof(serv),
0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv));
if (pkt->conn->bev) /* TCP. */
{
int err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data,
- pkt->rpkt->data_len);
+ pkt->rpkt->length);
if (err < 0)
return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
"bufferevent_write: %s",
{
struct rs_packet *p;
RADIUS_PACKET *rpkt;
+ int err;
*pkt_out = NULL;
- rpkt = rad_alloc (1);
- if (!rpkt)
+ rpkt = rs_malloc (conn->ctx, sizeof(*rpkt) + RS_MAX_PACKET_LEN);
+ if (rpkt == NULL)
return rs_err_conn_push (conn, RSE_NOMEM, __func__);
- rpkt->id = conn->nextid++;
- p = (struct rs_packet *) malloc (sizeof (struct rs_packet));
- if (!p)
+ /*
+ * This doesn't make sense; the packet identifier is constant for
+ * an entire conversation. A separate API should be provided to
+ * allow the application to set the packet ID, or a conversation
+ * object should group related packets together.
+ */
+ #if 0
+ rpkt->id = conn->nextid++
+ #endif
+
+ err = nr_packet_init (rpkt, NULL, NULL,
+ PW_ACCESS_REQUEST,
+ rpkt + 1, RS_MAX_PACKET_LEN);
+ if (err < 0)
+ return rs_err_conn_push (conn, -err, __func__);
+
+ p = (struct rs_packet *) rs_calloc (conn->ctx, 1, sizeof (*p));
+ if (p == NULL)
{
- rad_free (&rpkt);
+ rs_free (conn->ctx, rpkt);
return rs_err_conn_push (conn, RSE_NOMEM, __func__);
}
- memset (p, 0, sizeof (struct rs_packet));
p->conn = conn;
p->rpkt = rpkt;
const char *user_name, const char *user_pw)
{
struct rs_packet *pkt;
- VALUE_PAIR *vp = NULL;
+ int err;
if (rs_packet_create (conn, pkt_out))
return -1;
+
pkt = *pkt_out;
- pkt->rpkt->code = PW_AUTHENTICATION_REQUEST;
+ pkt->rpkt->code = PW_ACCESS_REQUEST;
if (user_name)
{
- vp = pairmake ("User-Name", user_name, T_OP_EQ);
- if (vp == NULL)
- return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__,
- "pairmake: %s", fr_strerror ());
- pairadd (&pkt->rpkt->vps, vp);
+ err = rs_packet_append_avp (pkt, PW_USER_NAME, 0, user_name, 0);
+ if (err)
+ return err;
}
if (user_pw)
{
- vp = pairmake ("User-Password", user_pw, T_OP_EQ);
- if (vp == NULL)
- return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__,
- "pairmake: %s", fr_strerror ());
- pairadd (&pkt->rpkt->vps, vp);
+ err = rs_packet_append_avp (pkt, PW_USER_PASSWORD, 0, user_pw, 0);
+ if (err)
+ return err;
}
return RSE_OK;
}
- struct radius_packet *
- rs_packet_frpkt (struct rs_packet *pkt)
- {
- assert (pkt);
- return pkt->rpkt;
- }
-
void
rs_packet_destroy (struct rs_packet *pkt)
{
assert (pkt->conn);
assert (pkt->conn->ctx);
- rad_free (&pkt->rpkt); /* Note: This frees the VALUE_PAIR's too. */
+ rs_avp_free (&pkt->rpkt->vps);
+ rs_free (pkt->conn->ctx, pkt->rpkt);
rs_free (pkt->conn->ctx, pkt);
}
+
+ int
+ rs_packet_append_avp (struct rs_packet *pkt,
+ unsigned int attr, unsigned int vendor,
+ const void *data, size_t data_len)
+ {
+ const DICT_ATTR *da;
+ int err;
+
+ assert (pkt);
+
+ da = nr_dict_attr_byvalue (attr, vendor);
+ if (da == NULL)
+ return RSE_ATTR_TYPE_UNKNOWN;
+
+ err = nr_packet_attr_append (pkt->rpkt, NULL, da, data, data_len);
+ if (err < 0)
+ return rs_err_conn_push (pkt->conn, -err, __func__);
+
+ return RSE_OK;
+ }
+
+ void
+ rs_packet_avps (struct rs_packet *pkt, rs_avp ***vps)
+ {
+ assert (pkt);
+ *vps = &pkt->rpkt->vps;
+ }
+
+ unsigned int
+ rs_packet_code (struct rs_packet *pkt)
+ {
+ assert (pkt);
+ return pkt->rpkt->code;
+ }
+
+ rs_const_avp *
+ rs_packet_find_avp (struct rs_packet *pkt, unsigned int attr, unsigned int vendor)
+ {
+ assert (pkt);
+ return rs_avp_find_const (pkt->rpkt->vps, attr, vendor);
+ }
+
+ int
+ rs_packet_set_id (struct rs_packet *pkt, int id)
+ {
+ int old = pkt->rpkt->id;
+
+ pkt->rpkt->id = id;
+
+ return old;
+ }
#endif
#include <assert.h>
+ #include <stdlib.h>
+ #include <string.h>
+
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
#include "err.h"
#include "peer.h"
+#include "util.h"
struct rs_peer *
peer_pick_peer (struct rs_connection *conn)
int
rs_peer_set_address (struct rs_peer *peer, const char *hostname,
- const char *service)
+ const char *service)
{
- struct rs_error *err;
-
assert (peer);
- assert (peer->realm);
+ assert (peer->conn);
+ assert (peer->conn->ctx);
+
+ peer->hostname = rs_strdup (peer->conn->ctx, hostname);
+ peer->service = rs_strdup (peer->conn->ctx, service);
+ if (peer->hostname == NULL || peer->service == NULL)
+ return RSE_NOMEM;
- err = rs_resolv (&peer->addr, peer->realm->type, hostname, service);
- if (err)
- return err_conn_push_err (peer->conn, err);
return RSE_OK;
}
#include <libgen.h>
#include <assert.h>
- #include <freeradius/libradius.h>
+ #include <radius/client.h>
#include <event2/event.h>
#include <event2/util.h>
#include <radsec/radsec.h>
#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 */
- fr_randinit (&h->fr_randctx, 0);
- fr_rand_seed (NULL, 0);
+ debug_init ("libradsec"); /* radsecproxy compat, FIXME: remove */
if (ctx != NULL)
*ctx = h;
return RSE_OK;
}
- /** Initialize freeradius dictionary. */
- int
- rs_context_init_freeradius_dict (struct rs_context *ctx, const char *dict)
- {
- int r = RSE_OK;
- size_t dictlen;
- char *dir = NULL;
- char *fn = NULL;
-
- if (dict == NULL)
- if (ctx->config != NULL && ctx->config->dictionary)
- dict = ctx->config->dictionary;
-
- if (dict == NULL)
- dict = RS_FREERADIUS_DICT;
-
- dictlen = strlen (dict);
- dir = rs_calloc (ctx, 1, dictlen + 1);
- fn = rs_calloc (ctx, 1, dictlen + 1);
- if (dir == NULL || fn == NULL)
- {
- r = rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
- goto out;
- }
- strncpy (dir, dict, dictlen);
- strncpy (fn, dict, dictlen);
-
- if (dict_init (dirname (dir), basename (fn)) < 0)
- {
- r = rs_err_ctx_push_fl (ctx, RSE_FR, __FILE__, __LINE__,
- "failing dict_init(\"%s\")", dict);
- goto out;
- }
-
- out:
- if (dir)
- rs_free (ctx, dir);
- if (fn)
- rs_free (ctx, fn);
- return r;
- }
-
struct rs_error *
-rs_resolv (struct evutil_addrinfo **addr,
- rs_conn_type_t type,
- const char *hostname,
- const char *service)
+rs_resolve (struct evutil_addrinfo **addr,
+ rs_conn_type_t type,
+ const char *hostname,
+ const char *service)
{
int err;
struct evutil_addrinfo hints, *res = NULL;
for (p = r->peers; p; )
{
struct rs_peer *tmp = p;
- if (p->addr)
- evutil_freeaddrinfo (p->addr);
+ if (p->addr_cache)
+ {
+ evutil_freeaddrinfo (p->addr_cache);
+ p->addr_cache = NULL;
+ }
p = p->next;
rs_free (ctx, tmp);
}
free (r->name);
+ rs_free (ctx, r->transport_cred);
r = r->next;
rs_free (ctx, tmp);
}
* copyright notice and this permission notice appear in all copies.
*/
++#include <netinet/in.h>
#include <openssl/ssl.h>
#if defined (__cplusplus)
struct tls *tlsgettls(char *alt1, char *alt2);
SSL_CTX *tlsgetctx(uint8_t type, struct tls *t);
X509 *verifytlscert(SSL *ssl);
+int subjectaltnameaddr(X509 *cert, int family, const struct in6_addr *addr);
+int subjectaltnameregexp(X509 *cert, int type, const char *exact, const regex_t *regex);
+int cnregexp(X509 *cert, const char *exact, const regex_t *regex);
int verifyconfcert(X509 *cert, struct clsrvconf *conf);
-int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val);
-int addmatchcertattr(struct clsrvconf *conf);
#endif
#if defined (__cplusplus)
#include <event2/bufferevent_ssl.h>
#include <openssl/err.h>
#endif
+ #include <radius/client.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
#include "tcp.h"
if (n == RS_HEADER_LEN)
{
pkt->flags |= rs_packet_hdr_read_flag;
- pkt->rpkt->data_len = (pkt->hdr[2] << 8) + pkt->hdr[3];
- if (pkt->rpkt->data_len < 20 || pkt->rpkt->data_len > 4096)
+ pkt->rpkt->length = (pkt->hdr[2] << 8) + pkt->hdr[3];
+ if (pkt->rpkt->length < 20 || pkt->rpkt->length > RS_MAX_PACKET_LEN)
{
conn_close (&pkt->conn);
return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
"invalid packet length: %d",
- pkt->rpkt->data_len);
- }
- pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len);
- if (!pkt->rpkt->data)
- {
- conn_close (&pkt->conn);
- return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__,
- NULL);
+ pkt->rpkt->length);
}
memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN);
bufferevent_setwatermark (pkt->conn->bev, EV_READ,
- pkt->rpkt->data_len - RS_HEADER_LEN, 0);
+ pkt->rpkt->length - RS_HEADER_LEN, 0);
rs_debug (("%s: packet header read, total pkt len=%d\n",
- __func__, pkt->rpkt->data_len));
+ __func__, pkt->rpkt->length));
}
else if (n < 0)
{
_read_packet (struct rs_packet *pkt)
{
size_t n = 0;
+ int err;
rs_debug (("%s: trying to read %d octets of packet data\n", __func__,
- pkt->rpkt->data_len - RS_HEADER_LEN));
+ pkt->rpkt->length - RS_HEADER_LEN));
n = bufferevent_read (pkt->conn->bev,
pkt->rpkt->data + RS_HEADER_LEN,
- pkt->rpkt->data_len - RS_HEADER_LEN);
+ pkt->rpkt->length - RS_HEADER_LEN);
rs_debug (("%s: read %ld octets of packet data\n", __func__, n));
- if (n == pkt->rpkt->data_len - RS_HEADER_LEN)
+ if (n == pkt->rpkt->length - RS_HEADER_LEN)
{
bufferevent_disable (pkt->conn->bev, EV_READ);
rs_debug (("%s: complete packet read\n", __func__));
- invalid code field
- attribute lengths >= 2
- attribute sizes adding up correctly */
- if (!rad_packet_ok (pkt->rpkt, 0))
+ err = nr_packet_ok (pkt->rpkt);
+ if (err != RSE_OK)
{
conn_close (&pkt->conn);
- return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
- "invalid packet: %s", fr_strerror ());
+ return rs_err_conn_push_fl (pkt->conn, err, __FILE__, __LINE__,
+ "invalid packet");
}
#if defined (DEBUG)
rs_debug (("%s: buffer frozen when reading packet\n", __func__));
else /* Short packet. */
rs_debug (("%s: waiting for another %d octets\n", __func__,
- pkt->rpkt->data_len - RS_HEADER_LEN - n));
+ pkt->rpkt->length - RS_HEADER_LEN - n));
return 0;
}
{
struct rs_packet *pkt = (struct rs_packet *) user_data;
struct rs_connection *conn = NULL;
- struct rs_peer *p = NULL;
int sockerr = 0;
#if defined (RS_ENABLE_TLS)
unsigned long tlserr = 0;
#endif
+#if defined (DEBUG)
+ struct rs_peer *p = NULL;
+#endif
assert (pkt);
assert (pkt->conn);
- assert (pkt->conn->active_peer);
conn = pkt->conn;
+#if defined (DEBUG)
+ assert (pkt->conn->active_peer);
p = conn->active_peer;
+#endif
conn->is_connecting = 0;
if (events & BEV_EVENT_CONNECTED)
{
if (conn->tev)
evtimer_del (conn->tev); /* Cancel connect timer. */
- event_on_connect (conn, pkt);
+ if (event_on_connect (conn, pkt))
+ {
+ event_on_disconnect (conn);
+ event_loopbreak (conn);
+ }
}
else if (events & BEV_EVENT_EOF)
{
{
rs_debug (("%s: %p times out on %s\n", __func__, p,
(events & BEV_EVENT_READING) ? "read" : "write"));
- rs_err_conn_push_fl (pkt->conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
+ rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
}
else if (events & BEV_EVENT_ERROR)
{
if (sockerr == 0) /* FIXME: True that errno == 0 means closed? */
{
event_on_disconnect (conn);
- rs_err_conn_push_fl (pkt->conn, RSE_DISCO, __FILE__, __LINE__, NULL);
+ rs_err_conn_push_fl (conn, RSE_DISCO, __FILE__, __LINE__, NULL);
}
else
{
rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr,
evutil_socket_error_to_string (sockerr)));
- rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
+ rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
"%d: %d (%s)", conn->fd, sockerr,
evutil_socket_error_to_string (sockerr));
}
{
rs_debug (("%s: openssl error: %s\n", __func__,
ERR_error_string (tlserr, NULL)));
- rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__,
+ rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__,
ERR_error_string (tlserr, NULL));
}
}
#include <stdlib.h>
#include <cgreen/cgreen.h>
- #include <freeradius/libradius.h>
#include "radsec/radsec.h"
#include "radsec/request.h"
#include "udp.h"
rs_request_add_reqpkt (req, msg);
assert_true (rs_request_send (req, &resp) == 0);
//printf ("%s\n", rs_err_msg (rs_err_conn_pop (conn), 1));
- assert_true (rs_packet_frpkt (resp)->code == PW_AUTHENTICATION_ACK);
+ assert_true (rs_packet_code(resp) == PW_ACCESS_ACCEPT);
rs_request_destroy (req);
}
assert_true (rs_packet_send (msg1, NULL) == 0);
}
++#if 0
static void
send_large_packet (struct rs_connection *conn)
{
struct rs_packet *msg0;
- struct rs_attr *attr_x;
+ struct radius_packet *frpkt = NULL;
char *buf;
int f;
- buf = malloc (4096);
+ buf = malloc (RS_MAX_PACKET_LEN);
assert_true (buf != NULL);
- memset (buf, 0, 4096);
+ memset (buf, 0, RS_MAX_PACKET_LEN);
assert_true (rs_packet_create (conn, &msg0) == 0);
- frpkt = rs_packet_frpkt (msg0);
- assert_true (frpkt != NULL);
/* 16 chunks --> heap corruption in evbuffer_drain detected by free() */
for (f = 0; f < 15; f++)
{
- VALUE_PAIR *vp = NULL;
memset (buf, 'a' + f, 252);
- vp = pairmake ("EAP-Message", buf, T_OP_EQ);
- assert_true (vp != NULL);
- pairadd (&frpkt->vps, vp);
- rs_attr_create (conn, &attr_x, "EAP-Message", buf);
- rs_packet_add_attr (msg0, attr_x);
++ //vp = pairmake ("EAP-Message", buf, T_OP_EQ);
++ assert_true (rs_packet_append_avp (msg0, fixme...) == RSE_OK);
}
assert_true (rs_packet_send (msg0, NULL) == 0);
}
++#endif /* 0 */
/* ************************************************************ */
static struct setup {
setup.config_file = "test.conf";
setup.config_name = "test-udp-auth";
- setup.username = "molgan";
+ setup.username = "molgan@PROJECT-MOONSHOT.ORG";
setup.pw = "password";
- assert_true (rs_context_create (&ctx, NULL) == 0);
+ assert_true (rs_context_create (&ctx) == 0);
assert_true (rs_context_read_config (ctx, setup.config_file) == 0);
+ assert_true (rs_context_init_freeradius_dict (ctx, NULL) == 0);
assert_true (rs_conn_create (ctx, &conn, setup.config_name) == 0);
authenticate (conn, setup.username, setup.pw);
hd (buf, len);
#endif
assert_true (len >= 20);
- assert_true (len <= 4096);
+ assert_true (len <= RS_MAX_PACKET_LEN);
assert_true ((buf[2] << 8) + buf[3] == len);
return len;
}
struct timeval timeout;
struct polldata *polldata;
- assert_true (rs_context_create (&ctx, NULL) == 0);
+ assert_true (rs_context_create (&ctx) == 0);
assert_true (rs_context_read_config (ctx, "test.conf") == 0);
- assert_true (rs_context_init_freeradius_dict (ctx, NULL) == 0);
assert_true (rs_conn_create (ctx, &conn, "test-udp-buffering") == 0);
timeout.tv_sec = 0;
assert_true (udp_poll (polldata) > 0);
assert_true (udp_poll (polldata) > 0);
+#if 0
+"
+send_large_packet() disabled, it's hanging after
+
+Sending Access-Request of id 1 to (null) port 0
+ Message-Authenticator = 0x00000000000000000000000000000000
+packet_do_send: about to send this to localhost:11820:
+ Code: 1, Identifier: 1, Lenght: 38
+rs_packet_send: entering event loop
+_evcb: fd=5 what = WRITE
+rs_packet_send: event loop done
+"
send_large_packet (conn);
assert_true (udp_poll (polldata) > 0);
+#endif /* 0 */
udp_free_polldata (polldata);
rs_conn_destroy (conn);