diff options
-rw-r--r-- | lib/conf.c | 255 | ||||
-rw-r--r-- | lib/confutil.c | 146 | ||||
-rw-r--r-- | lib/event.c | 3 | ||||
-rw-r--r-- | lib/include/radsec/radsec-impl.h | 17 | ||||
-rw-r--r-- | lib/include/radsec/radsec.h | 3 | ||||
-rw-r--r-- | lib/radsec.c | 2 | ||||
-rw-r--r-- | lib/radsec.sym | 1 | ||||
-rw-r--r-- | lib/tls.c | 23 |
8 files changed, 341 insertions, 109 deletions
@@ -1,4 +1,4 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010, 2011, 2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -15,49 +15,190 @@ #include "util.h" #include "debug.h" -#if 0 - # common config options - - # common realm config options +#if 0 /* Configuration file syntax. */ + # realm specific configuration realm STRING { type = "UDP"|"TCP"|"TLS"|"DTLS" timeout = INT retries = INT + } + + # realm configuration inherited by clients and servers + realm STRING { cacertfile = STRING #cacertpath = STRING certfile = STRING certkeyfile = STRING - pskstr = STRING # Transport pre-shared key, UTF-8 form. + 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 + # client configuration realm STRING { server { hostname = STRING - service = STRING + service = STRING # name or port number + secret = STRING # RADIUS secret + } + } + + # server configuration + realm STRING { + client { + hostname = STRING + service = STRING # name or port number secret = STRING # RADIUS secret } } #endif +struct confcommon { + struct rs_credentials *transport_cred; + char *cacertfile; + char *cacertpath; + char *certfile; + char *certkeyfile; + char *pskstr; + char *pskhexstr; +}; + +#define CONFGET_STR(dst,cfg,key,def) do { \ + (dst) = cfg_getstr ((cfg), (key)); \ + if ((dst) == NULL) (dst) = (def); \ + } while (0) +#define CONFGET_INT(dst,cfg,key,def) do { \ + (dst) = cfg_getint ((cfg), (key)); \ + if ((dst) == -1) (dst) = (def); \ + } while (0) + +static int +confload_peers (struct rs_context *ctx, + /*const*/ cfg_t *cfg_realm, + enum rs_peer_type type, + struct rs_realm *r) +{ + const char *peer_type_str[] = {"<no type>", "client", "server"}; + cfg_t *cfg_peer = NULL; + int j; + char *def_cacertfile = cfg_getstr (cfg_realm, "cacertfile"); + /*char *def_cacertpath = cfg_getstr (cfg_realm, "cacertpath");*/ + char *def_certfile = cfg_getstr (cfg_realm, "certfile"); + char *def_certkeyfile = cfg_getstr (cfg_realm, "certkeyfile"); + char *def_pskstr = cfg_getstr (cfg_realm, "pskstr"); + char *def_pskhexstr = cfg_getstr (cfg_realm, "pskhexstr"); + + for (j = 0; j < cfg_size (cfg_realm, peer_type_str[type]); j++) + { + char *pskstr = NULL; + char *pskhexstr = NULL; + struct rs_peer *p = peer_create (ctx, &r->peers); + if (p == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, + NULL); + p->type = type; + p->realm = r; + + cfg_peer = cfg_getnsec (cfg_realm, peer_type_str[type], j); + p->hostname = cfg_getstr (cfg_peer, "hostname"); + p->service = cfg_getstr (cfg_peer, "service"); + p->secret = cfg_getstr (cfg_peer, "secret"); + + 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); + CONFGET_STR (pskstr, cfg_peer, "pskstr", def_pskstr); + CONFGET_STR (pskhexstr, cfg_peer, "pskhexstr", def_pskhexstr); + + if (pskstr || pskhexstr) + { +#if defined RS_ENABLE_TLS_PSK + char *def_pskex = cfg_getstr (cfg_realm, "pskex"); + char *tmp_pskex = NULL; + rs_cred_type_t type = RS_CRED_NONE; + struct rs_credentials *cred = NULL; + + CONFGET_STR (tmp_pskex, cfg_peer, "pskex", def_pskex); + if (!strcmp (tmp_pskex, "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) + { + char *def_pskid = cfg_getstr (cfg_realm, "pskid"); + 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; + CONFGET_STR (cred->identity, cfg_peer, "pskid", def_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; + } + + p->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 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) + && (p->cacertfile == NULL && p->cacertpath == NULL) + && p->transport_cred == NULL) + return rs_err_ctx_push (ctx, RSE_CONFIG, + "%s: missing both CA file/path and PSK", + r->name); + } + + return RSE_OK; +} + /* FIXME: Leaking memory in error cases. */ int rs_context_read_config(struct rs_context *ctx, const char *config_file) { - cfg_t *cfg, *cfg_realm, *cfg_server; + cfg_t *cfg, *cfg_realm; int err = 0; - int i, j; + int i; const char *s; struct rs_config *config = NULL; - cfg_opt_t server_opts[] = + cfg_opt_t peer_opts[] = { CFG_STR ("hostname", NULL, CFGF_NONE), CFG_STR ("service", "2083", CFGF_NONE), CFG_STR ("secret", "radsec", CFGF_NONE), + CFG_STR ("cacertfile", NULL, CFGF_NONE), + /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/ + CFG_STR ("certfile", NULL, CFGF_NONE), + CFG_STR ("certkeyfile", NULL, CFGF_NONE), + CFG_STR ("pskstr", NULL, CFGF_NONE), + CFG_STR ("pskhexstr", NULL, CFGF_NONE), + CFG_STR ("pskid", NULL, CFGF_NONE), + CFG_STR ("pskex", "PSK", CFGF_NONE), + CFG_END () }; cfg_opt_t realm_opts[] = @@ -73,7 +214,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) 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_SEC ("server", peer_opts, CFGF_MULTI), + CFG_SEC ("client", peer_opts, CFGF_MULTI), CFG_END () }; cfg_opt_t opts[] = @@ -111,8 +253,9 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) { struct rs_realm *r = NULL; const char *typestr; - char *pskstr = NULL, *pskhexstr = NULL; + struct confcommon cc; + memset (&cc, 0, sizeof(cc)); r = rs_calloc (ctx, 1, sizeof(*r)); if (r == NULL) return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); @@ -148,87 +291,17 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) 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->cacertfile = cfg_getstr (cfg_realm, "cacertfile"); - /*r->cacertpath = cfg_getstr (cfg_realm, "cacertpath");*/ - 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++) - { - struct rs_peer *p = peer_create (ctx, &r->peers); - if (p == NULL) - return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, - NULL); - p->realm = r; - - cfg_server = cfg_getnsec (cfg_realm, "server", j); - p->hostname = cfg_getstr (cfg_server, "hostname"); - p->service = cfg_getstr (cfg_server, "service"); - p->secret = cfg_getstr (cfg_server, "secret"); - } + /* Add client and server peers. */ + err = confload_peers (ctx, cfg_realm, RS_PEER_TYPE_CLIENT, r); + if (err) + return err; + err = confload_peers (ctx, cfg_realm, RS_PEER_TYPE_SERVER, r); + if (err) + return err; } /* Save config object in context, for freeing in rs_context_destroy(). */ diff --git a/lib/confutil.c b/lib/confutil.c new file mode 100644 index 0000000..3a1d639 --- /dev/null +++ b/lib/confutil.c @@ -0,0 +1,146 @@ +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> + +#if 0 +/* This code triggers the memory-stomping-detector of electric fence */ + +#define MEMCHUNK 30 + +static int +print_to_buf (const struct rs_context *ctx, + char **buf_ptr, ssize_t *i_ptr, ssize_t *len_ptr, + const char *fmt, ...) +{ + char *buf = *buf_ptr; + ssize_t i = *i_ptr; + ssize_t len = *len_ptr; + + va_list args; + for (;;) + { + int n; + va_start (args, fmt); + fprintf (stdout, "sprintf (%p + %ld, %ld, \"%s\") -->", + buf, i, len, buf); + fflush (stdout); + n = vsnprintf (buf + i, len - i, fmt, args); + fprintf (stdout, "%d\n", n); + va_end (args); + if (n < 0) + return -RSE_INTERNAL; + if (n >= len - i) + { + int newlen = len + MEMCHUNK; + buf = rs_realloc (ctx, buf, newlen); + if (buf == NULL) + return -RSE_NOMEM; + len = newlen; + continue; + } + len -= n; + i += n; + + *buf_ptr = buf; + *i_ptr = i; + *len_ptr = len; + return RSE_OK; + } +} +#endif /* 0 */ + +static int +pp (char **out, size_t *len, const char *fmt, ...) +{ + int n; + va_list args; + va_start (args, fmt); + n = vsnprintf (*out, *len, fmt, args); + va_end (args); + if (n == -1 || n >= *len) + return -RSE_INTERNAL; + *out += n; + *len -= n; + return RSE_OK; +} + +int +rs_context_print_config (struct rs_context *ctx, char **buf_out) +{ + char *buf = rs_malloc (ctx, 8192); + char *out = NULL; + size_t len = 8192; + struct rs_config *cfg = ctx->config; + struct rs_realm *r = NULL; + struct rs_peer *p = NULL; + char *peer_type[] = {"<no type>", "client", "server"}; + char *realm_type[] = {"<no type>", "UDP", "TCP", "TLS", "DTLS"}; + char *cred_type[] = {"<no type>", "PSK", "DHE_PSK", "RSA_PSK"}; + + out = buf; + assert (out); + assert (cfg); + + 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", + realm_type[r->type], + r->timeout, + r->retries)) + return -RSE_INTERNAL; + for (p = r->peers; p != NULL; p = p->next) + { + if (pp (&out, &len, + "\t%s {\n" + "\t\thostname = \"%s\"\n" + "\t\tservice = \"%s\"\n" + "\t\tsecret = \"%s\"\n", + peer_type[p->type], + p->hostname, + p->service, + p->secret)) + return -RSE_INTERNAL; + if (p->cacertfile) + if (pp (&out, &len, "\t\tcacertfile = \"%s\"\n", p->cacertfile)) + return -RSE_INTERNAL; + if (p->certfile) + if (pp (&out, &len, "\t\tcertfile = \"%s\"\n", p->certfile)) + return -RSE_INTERNAL; + if (p->certkeyfile) + if (pp (&out, &len, "\t\tcertkeyfile = \"%s\"\n", p->certkeyfile)) + return -RSE_INTERNAL; + if (p->transport_cred) + { + if (pp (&out, &len, "\t\tpskex = \"%s\"\n", + cred_type[p->transport_cred->type]) + || pp (&out, &len, "\t\tpskid = \"%s\"\n", + p->transport_cred->identity) + || pp (&out, &len, + "\t\t%s = \"%s\"\n", (p->transport_cred->secret_encoding + == RS_KEY_ENCODING_ASCII_HEX + ? "pskhexstr" : "pskstr"), + p->transport_cred->secret)) + return -RSE_INTERNAL; + } + if (pp (&out, &len, "\t}\n")) + return -RSE_INTERNAL; + } + if (pp (&out, &len, "}\n")) + return -RSE_INTERNAL; + } + + if (buf_out) + *buf_out = buf; + return RSE_OK; +} diff --git a/lib/event.c b/lib/event.c index 138fa72..6b7ba48 100644 --- a/lib/event.c +++ b/lib/event.c @@ -244,11 +244,12 @@ int event_on_connect (struct rs_connection *conn, struct rs_message *msg) { assert (!conn->is_connecting); + assert (conn->active_peer); #if defined (RS_ENABLE_TLS) if ((conn->realm->type == RS_CONN_TYPE_TLS || conn->realm->type == RS_CONN_TYPE_DTLS) - && conn->realm->transport_cred->type != RS_CRED_TLS_PSK) + && conn->active_peer->transport_cred->type != RS_CRED_TLS_PSK) if (tls_verify_cert (conn) != RSE_OK) { rs_debug (("%s: server cert verification failed\n", __func__)); diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h index 9f8ebbb..ef01d70 100644 --- a/lib/include/radsec/radsec-impl.h +++ b/lib/include/radsec/radsec-impl.h @@ -40,7 +40,6 @@ struct rs_credentials { char *identity; char *secret; enum rs_key_encoding secret_encoding; - unsigned int secret_len; }; struct rs_error { @@ -48,14 +47,25 @@ struct rs_error { char buf[1024]; }; +enum rs_peer_type { + RS_PEER_TYPE_CLIENT = 1, + RS_PEER_TYPE_SERVER = 2 +}; + /** Configuration object for a connection. */ struct rs_peer { + enum rs_peer_type type; struct rs_connection *conn; struct rs_realm *realm; char *hostname; char *service; char *secret; /* RADIUS secret. */ struct evutil_addrinfo *addr_cache; + char *cacertfile; + char *cacertpath; + char *certfile; + char *certkeyfile; + struct rs_credentials *transport_cred; struct rs_peer *next; }; @@ -65,11 +75,6 @@ struct rs_realm { enum rs_conn_type type; int timeout; int retries; - char *cacertfile; - char *cacertpath; - char *certfile; - char *certkeyfile; - struct rs_credentials *transport_cred; struct rs_peer *peers; struct rs_realm *next; }; diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h index ee8c6a8..0a43f6f 100644 --- a/lib/include/radsec/radsec.h +++ b/lib/include/radsec/radsec.h @@ -208,6 +208,9 @@ int rs_context_set_alloc_scheme(struct rs_context *ctx, accessed using \a rs_err_ctx_pop. */ int rs_context_read_config(struct rs_context *ctx, const char *config_file); +int rs_context_print_config (struct rs_context *ctx, char **buf_out); + + /****************/ /* Connection. */ /****************/ diff --git a/lib/radsec.c b/lib/radsec.c index db406ae..e6b79cf 100644 --- a/lib/radsec.c +++ b/lib/radsec.c @@ -107,11 +107,11 @@ rs_context_destroy (struct rs_context *ctx) evutil_freeaddrinfo (p->addr_cache); p->addr_cache = NULL; } + rs_free (ctx, p->transport_cred); p = p->next; rs_free (ctx, tmp); } free (r->name); - rs_free (ctx, r->transport_cred); r = r->next; rs_free (ctx, tmp); } diff --git a/lib/radsec.sym b/lib/radsec.sym index 11f2468..6378cfd 100644 --- a/lib/radsec.sym +++ b/lib/radsec.sym @@ -53,6 +53,7 @@ rs_conn_set_timeout rs_conn_set_type rs_context_create rs_context_destroy +rs_context_print_config rs_context_read_config rs_context_set_alloc_scheme rs_dump_message @@ -25,13 +25,15 @@ _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm) if (c) { memset (c, 0, sizeof (struct tls)); + /* _conn_open() should've picked a peer by now. */ + 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 = realm->cacertfile; + c->cacertfile = conn->active_peer->cacertfile; c->cacertpath = NULL; /* NYI */ - c->certfile = realm->certfile; - c->certkeyfile = realm->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 */ @@ -54,10 +56,11 @@ psk_client_cb (SSL *ssl, { struct rs_connection *conn = NULL; struct rs_credentials *cred = NULL; + unsigned int secret_len; conn = SSL_get_ex_data (ssl, 0); assert (conn != NULL); - cred = conn->active_peer->realm->transport_cred; + cred = conn->active_peer->transport_cred; assert (cred != NULL); /* NOTE: Ignoring identity hint from server. */ @@ -72,14 +75,14 @@ psk_client_cb (SSL *ssl, switch (cred->secret_encoding) { case RS_KEY_ENCODING_UTF8: - cred->secret_len = strlen (cred->secret); - if (cred->secret_len > max_psk_len) + secret_len = strlen (cred->secret); + if (secret_len > max_psk_len) { rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d", max_psk_len); return 0; } - memcpy (psk, cred->secret, cred->secret_len); + memcpy (psk, cred->secret, secret_len); break; case RS_KEY_ENCODING_ASCII_HEX: { @@ -99,7 +102,7 @@ psk_client_cb (SSL *ssl, BN_clear_free (bn); return 0; } - cred->secret_len = BN_bn2bin (bn, psk); + secret_len = BN_bn2bin (bn, psk); BN_clear_free (bn); } break; @@ -107,7 +110,7 @@ psk_client_cb (SSL *ssl, assert (!"unknown psk encoding"); } - return cred->secret_len; + return secret_len; } #endif /* RS_ENABLE_TLS_PSK */ @@ -144,7 +147,7 @@ rs_tls_init (struct rs_connection *conn) } #if defined RS_ENABLE_TLS_PSK - if (conn->active_peer->realm->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); |