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