Merge libradsec-new-client.
authorLinus Nordberg <linus@nordberg.se>
Fri, 27 Apr 2012 15:00:17 +0000 (17:00 +0200)
committerLinus Nordberg <linus@nordberg.se>
Fri, 27 Apr 2012 15:00:17 +0000 (17:00 +0200)
27 files changed:
lib/HACKING
lib/Makefile.am
lib/conf.c
lib/configure.ac
lib/conn.c
lib/err.c
lib/event.c
lib/event.h
lib/examples/client-blocking.c
lib/examples/client.conf
lib/include/radsec/radsec-impl.h
lib/include/radsec/radsec.h
lib/packet.c
lib/peer.c
lib/radsec.c
lib/radsec.h [new file with mode: 0644]
lib/rsp_tlscommon.c
lib/rsp_tlscommon.h
lib/tcp.c
lib/tests/Makefile.am
lib/tests/README
lib/tests/test-udp.c
lib/tests/test.conf
lib/tls.c
lib/tls.h
lib/util.c [new file with mode: 0644]
lib/util.h [new file with mode: 0644]

index 1494941..824cb77 100644 (file)
@@ -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?
index 51aebf9..9a74a7f 100644 (file)
@@ -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 \
index 84bd1a8..1cb7049 100644 (file)
@@ -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
       #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)
 {
@@ -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");
        }
     }
index 9b1d304..bb71a6a 100644 (file)
@@ -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(
index 7d0a794..fa63727 100644 (file)
@@ -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);
     }
 
index e318047..60ef82f 100644 (file)
--- a/lib/err.c
+++ b/lib/err.c
@@ -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))
 
index b2096bc..4f83394 100644 (file)
@@ -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
index e042599..befbd0d 100644 (file)
@@ -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);
index 2cfd617..7d3869a 100644 (file)
@@ -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)
     {
index 47528c8..bf57434 100644 (file)
@@ -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"
index 2274a99..6339e74 100644 (file)
 /* 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
index 6e967af..6c4f6a7 100644 (file)
@@ -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 {
index 86a16c3..ce68bea 100644 (file)
@@ -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));
index 256ff76..01dc243 100644 (file)
@@ -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;
 }
 
index 7421755..347a48b 100644 (file)
@@ -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 (file)
index 0000000..9e64692
--- /dev/null
@@ -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);
index a34fe33..abc395e 100644 (file)
@@ -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: */
index 6819cd0..d96f553 100644 (file)
@@ -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)
index 3a59d6f..841f905 100644 (file)
--- a/lib/tcp.c
+++ b/lib/tcp.c
@@ -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));
            }
        }
index 526dcdc..7c5408b 100644 (file)
@@ -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
index 8280b9e..4d68bde 100644 (file)
@@ -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
index f66eebd..ccad607 100644 (file)
@@ -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);
index 9be968d..839fd75 100644 (file)
@@ -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"
index 6fcf5a0..610df98 100644 (file)
--- a/lib/tls.c
+++ b/lib/tls.c
@@ -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;
+}
index d457cfd..0dc2ebd 100644 (file)
--- a/lib/tls.h
+++ b/lib/tls.h
@@ -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 (file)
index 0000000..3c9fef6
--- /dev/null
@@ -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 (file)
index 0000000..90c55d8
--- /dev/null
@@ -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);