diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile.am | 13 | ||||
| -rw-r--r-- | lib/attr.c | 4 | ||||
| -rw-r--r-- | lib/conf.c | 23 | ||||
| -rw-r--r-- | lib/configure.ac | 17 | ||||
| -rw-r--r-- | lib/conn.c | 7 | ||||
| -rw-r--r-- | lib/debug.c | 4 | ||||
| -rw-r--r-- | lib/err.c | 14 | ||||
| -rw-r--r-- | lib/examples/client.conf | 22 | ||||
| -rw-r--r-- | lib/include/radsec/radsec-impl.h | 12 | ||||
| -rw-r--r-- | lib/include/radsec/radsec.h | 2 | ||||
| -rw-r--r-- | lib/packet.c | 62 | ||||
| -rw-r--r-- | lib/radsec.c | 14 | ||||
| -rw-r--r-- | lib/request.c | 12 | ||||
| -rw-r--r-- | lib/rsp_debug.c | 217 | ||||
| -rw-r--r-- | lib/rsp_debug.h | 32 | ||||
| -rw-r--r-- | lib/rsp_hash.c | 136 | ||||
| -rw-r--r-- | lib/rsp_hash.h | 48 | ||||
| -rw-r--r-- | lib/rsp_list.c | 127 | ||||
| -rw-r--r-- | lib/rsp_list.h | 51 | ||||
| -rw-r--r-- | lib/rsp_tlscommon.c | 664 | ||||
| -rw-r--r-- | lib/rsp_tlscommon.h | 40 | ||||
| -rw-r--r-- | lib/rsp_util.c | 256 | ||||
| -rw-r--r-- | lib/rsp_util.h | 25 | ||||
| -rw-r--r-- | lib/tls.c | 73 | ||||
| -rw-r--r-- | lib/tls.h | 1 | 
25 files changed, 1855 insertions, 21 deletions
| diff --git a/lib/Makefile.am b/lib/Makefile.am index 5d7cd0a..ca7de1f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -13,7 +13,16 @@ libradsec_la_SOURCES = \  	debug.c \  	err.c \  	packet.c \ +	radsec.c \  	request.c \ -	radsec.c +	tls.c + +libradsec_la_SOURCES += \ +	rsp_debug.c \ +	rsp_hash.c \ +	rsp_list.c \ +	rsp_tlscommon.c \ +	rsp_util.c +  libradsec_la_LDFLAGS = -version-info 0:0:0 -libradsec_la_CFLAGS = $(CFLAGS) #-DDEBUG -DDEBUG_LEVENT +libradsec_la_CFLAGS = $(CFLAGS) -DDEBUG -DDEBUG_LEVENT @@ -1,5 +1,9 @@  /* See the file COPYING for licensing information.  */ +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <freeradius/libradius.h>  #include <radsec/radsec.h>  #include <radsec/radsec-impl.h> @@ -1,3 +1,9 @@ +/* See the file COPYING for licensing information.  */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <confuse.h>  #include <string.h>  #include <radsec/radsec.h> @@ -6,7 +12,11 @@  #if 0    # example of client config    config NAME { -      type = "UDP|TCP|TLS|DTLS" +      type = "UDP"|"TCP"|"TLS"|"DTLS" +      cacertfile = STRING +      #cacertpath = STRING +      certfile = STRING +      certkeyfile = STRING        server {            hostname = STRING  	  service = STRING @@ -33,6 +43,10 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)    cfg_opt_t config_opts[] =      {        CFG_STR ("type", "UDP", CFGF_NONE), +      CFG_STR ("cacertfile", NULL, CFGF_NONE), +      /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/ +      CFG_STR ("certfile", NULL, CFGF_NONE), +      CFG_STR ("certkeyfile", NULL, CFGF_NONE),        CFG_SEC ("server", server_opts, CFGF_MULTI),        CFG_END ()      }; @@ -62,6 +76,7 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)  	ctx->realms = r;        cfg_config = cfg_getnsec (cfg, "config", i);        r->name = strdup (cfg_title (cfg_config)); +        typestr = cfg_getstr (cfg_config, "type");        if (!strcmp (typestr, "UDP"))  	r->type = RS_CONN_TYPE_UDP; @@ -75,6 +90,11 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)  	return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,  				   "%s: invalid connection type", typestr); +      r->cacertfile = cfg_getstr (cfg_config, "cacertfile"); +      /*r->cacertpath = cfg_getstr (cfg_config, "cacertpath");*/ +      r->certfile = cfg_getstr (cfg_config, "certfile"); +      r->certkeyfile = cfg_getstr (cfg_config, "certkeyfile"); +        /* Add peers, one per server stanza.  */        for (j = 0; j < cfg_size (cfg_config, "server"); j++)  	{ @@ -82,6 +102,7 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)  	  if (!p)  	    return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,  				       NULL); +	  p->realm = r;  	  cfg_server = cfg_getnsec (cfg_config, "server", j);  	  _rs_resolv (&p->addr, r->type, cfg_getstr (cfg_server, "hostname"), diff --git a/lib/configure.ac b/lib/configure.ac index 5ae729f..2039233 100644 --- a/lib/configure.ac +++ b/lib/configure.ac @@ -11,6 +11,13 @@ AC_PROG_LIBTOOL  # Checks for programs.  AC_PROG_CC +# Enable-knobs. +AH_TEMPLATE([RS_ENABLE_TLS], [TLS (RadSec) enabled]) +AH_TEMPLATE([RADPROT_TLS], []) +AC_ARG_ENABLE([tls], AS_HELP_STRING([--enable-tls], [enable TLS (RadSec)]), +    [AC_DEFINE([RS_ENABLE_TLS]) +     AC_DEFINE([RADPROT_TLS])]) +  # Checks for libraries.  AC_CHECK_LIB([confuse], [cfg_init],,      AC_MSG_ERROR([required library libconfuse not found])) @@ -18,9 +25,15 @@ AC_CHECK_LIB([event_core], [event_get_version],,      AC_MSG_ERROR([required library libevent_core not found]))  AC_CHECK_LIB([freeradius-radius], [rad_alloc],,      AC_MSG_ERROR([required library libfreeradius-radius not found])) +dnl TODO: Only do this if --enable-tls or --enable-dtls. +#AC_CHECK_LIB([ssl], [SSL_new],, +#    AC_MSG_ERROR([required library libssl not found])) +AC_CHECK_LIB([event_openssl], [bufferevent_openssl_socket_new],, +    AC_MSG_ERROR([required library event_openssl not found]))  # Checks for header files. -AC_CHECK_HEADERS([netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h unistd.h]) +AC_CHECK_HEADERS( +    [netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h unistd.h])  # Checks for typedefs, structures, and compiler characteristics.  AC_TYPE_SIZE_T @@ -28,8 +41,6 @@ AC_TYPE_SSIZE_T  AC_TYPE_UINT8_T  # Checks for library functions. -AC_FUNC_MALLOC -AC_FUNC_REALLOC  AC_CHECK_FUNCS([memset socket strdup strerror strrchr])  AC_CONFIG_FILES([Makefile @@ -1,6 +1,11 @@  /* See the file COPYING for licensing information.  */ +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <assert.h> +#include <event2/event.h>  #include <radsec/radsec.h>  #include <radsec/radsec-impl.h> @@ -49,7 +54,7 @@ _rs_resolv (struct evutil_addrinfo **addr, rs_conn_type_t type,    struct evutil_addrinfo hints, *res = NULL;    memset (&hints, 0, sizeof(struct evutil_addrinfo)); -  hints.ai_family = AF_UNSPEC;	/* v4 or v6.  */ +  hints.ai_family = AF_INET;   /* IPv4 only.  TODO: Set AF_UNSPEC.  */    hints.ai_flags = AI_ADDRCONFIG;    switch (type)      { diff --git a/lib/debug.c b/lib/debug.c index e6c6afe..604ab23 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -1,5 +1,9 @@  /* See the file COPYING for licensing information.  */ +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <stdio.h>  #include <freeradius/libradius.h>  #include <radsec/radsec.h> @@ -1,5 +1,9 @@  /* See the file COPYING for licensing information.  */ +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <stdio.h>  #include <string.h>  #include <assert.h> @@ -8,8 +12,8 @@  const char *_errtxt[] = {    "SUCCESS",			/* 0 RSE_OK */ -  "NOMEM",			/* 1 RSE_NOMEM */ -  "NYI -- not yet implemented",	/* 2 RSE_NOSYS */ +  "out of memory",		/* 1 RSE_NOMEM */ +  "not yet implemented",	/* 2 RSE_NOSYS */    "invalid handle"		/* 3 RSE_INVALID_CTX */    "invalid connection"		/* 4 RSE_INVALID_CONN */    "connection type mismatch"	/* 5 RSE_CONN_TYPE_MISMATCH */ @@ -19,9 +23,9 @@ const char *_errtxt[] = {    "libevent error"		/* 9 RSE_EVENT */    "connection error"		/* 10 RSE_CONNERR */    "invalid configuration file"	/* 11 RSE_CONFIG */ -  "authentication failed"	/*  RSE_BADAUTH */ -  "ERR 13"			/*  RSE_ */ -  "ERR 14"			/*  RSE_ */ +  "authentication failed"	/* 12 RSE_BADAUTH */ +  "internal error"		/* 13 RSE_INTERNAL */ +  "SSL error"			/* 14 RSE_SSLERR */    "ERR 15"			/*  RSE_ */    "ERR 16"			/*  RSE_ */    "ERR 17"			/*  RSE_ */ diff --git a/lib/examples/client.conf b/lib/examples/client.conf index fe2ded5..e939756 100644 --- a/lib/examples/client.conf +++ b/lib/examples/client.conf @@ -1,4 +1,4 @@ -config blocking { +config blocking-udp {      type = "UDP"      server {          hostname = "localhost" @@ -8,3 +8,23 @@ config blocking {  	tries = 10	      /* optional */      }  } +config blocking-tls { +    type = "TLS" + +    cacertfile = "/home/linus/nordberg-ca.crt" +    #cacertpath =  +    certfile = "/home/linus/p/radsecproxy/src/maatuska.nordberg.se.crt" +    certkeyfile = "/home/linus/p/radsecproxy/src/maatuska.nordberg.se.key" +    #certkeypwd = "passphrase" +    #cacheexpiry = <seconds> +    #crlcheck = "on" | "off" +    #policyoids = ? +     +    server { +        hostname = "localhost" +	service = "4433" +	secret = "sikrit" +	timeout = 1         /* optional */ +	tries = 10	      /* optional */ +    } +} diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h index 6e5ee83..2b3d878 100644 --- a/lib/include/radsec/radsec-impl.h +++ b/lib/include/radsec/radsec-impl.h @@ -5,6 +5,9 @@  #include <freeradius/libradius.h>  #include <event2/util.h> +#if defined(RS_ENABLE_TLS) +#include <openssl/ssl.h> +#endif  /* Constants.  */  #define RS_HEADER_LEN 4 @@ -32,6 +35,7 @@ struct rs_error {  struct rs_peer {      struct rs_connection *conn; +    struct rs_realm *realm;      struct evutil_addrinfo *addr;      int fd;			/* Socket.  */      char is_connecting;		/* FIXME: replace with a single state member */ @@ -45,6 +49,10 @@ struct rs_peer {  struct rs_realm {      char *name;      enum rs_conn_type type; +    char *cacertfile; +    char *cacertpath; +    char *certfile; +    char *certkeyfile;      struct rs_peer *peers;      struct rs_realm *next;  }; @@ -69,6 +77,10 @@ struct rs_connection {      struct rs_error *err;      int nextid;      int user_dispatch_flag : 1;	/* User does the dispatching.  */ +#if defined(RS_ENABLE_TLS) +    SSL_CTX *tls_ctx; +    SSL *tls_ssl; +#endif  };  struct rs_packet { diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h index 8e97072..d80c296 100644 --- a/lib/include/radsec/radsec.h +++ b/lib/include/radsec/radsec.h @@ -18,6 +18,8 @@ enum rs_err_code {      RSE_CONNERR = 10,      RSE_CONFIG = 11,      RSE_BADAUTH = 12, +    RSE_INTERNAL = 13, +    RSE_SSLERR = 14,      RSE_SOME_ERROR = 21,  }; diff --git a/lib/packet.c b/lib/packet.c index cdd094a..154abc8 100644 --- a/lib/packet.c +++ b/lib/packet.c @@ -1,12 +1,22 @@  /* See the file COPYING for licensing information.  */ +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h>  #include <string.h>  #include <assert.h>  #include <freeradius/libradius.h>  #include <event2/event.h>  #include <event2/bufferevent.h> +#if defined RS_ENABLE_TLS +#include <event2/bufferevent_ssl.h> +#include <openssl/err.h> +#endif  #include <radsec/radsec.h>  #include <radsec/radsec-impl.h> +#include "tls.h"  #if defined DEBUG  #include <netdb.h>  #include <sys/socket.h> @@ -89,7 +99,7 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)    struct rs_packet *pkt = (struct rs_packet *)ctx;    struct rs_connection *conn;    struct rs_peer *p; -  int err; +  unsigned long err;    assert (pkt);    assert (pkt->conn); @@ -103,7 +113,6 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)        p->is_connected = 1;        if (conn->callbacks.connected_cb)  	conn->callbacks.connected_cb (conn->user_data); -  #if defined (DEBUG)        fprintf (stderr, "%s: connected\n", __func__);  #endif @@ -114,7 +123,24 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)        /* Packet will be freed in write callback.  */      }    else if (events & BEV_EVENT_ERROR) -    rs_err_conn_push_fl (pkt->conn, RSE_CONNERR, __FILE__, __LINE__, NULL); +    { +#if defined RS_ENABLE_TLS +      if (conn->tls_ssl)	/* FIXME: correct check?  */ +	{ +	  for (err = bufferevent_get_openssl_error (conn->bev); +	       err; +	       err = bufferevent_get_openssl_error (conn->bev)) +	    { +	      fprintf (stderr, "%s: openssl error: %s\n", __func__, +		       ERR_error_string (err, NULL)); /* DEBUG, until verified that pushed errors will actually be handled  */ +	      rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__, +				   "%d", err); +	    } +	} +#endif +      rs_err_conn_push_fl (pkt->conn, RSE_CONNERR, __FILE__, __LINE__, NULL); +      fprintf (stderr, "%s: BEV_EVENT_ERROR\n", __func__); /* DEBUG, until verified that pushed errors will actually be handled  */ +    }  }  static void @@ -301,13 +327,41 @@ _pick_peer (struct rs_connection *conn)  static int  _init_bev (struct rs_connection *conn, struct rs_peer *peer)  { -  if (!conn->bev) +  if (conn->bev) +    return RSE_OK; + +  switch (conn->type)      { +    case RS_CONN_TYPE_UDP: +    case RS_CONN_TYPE_TCP:        conn->bev = bufferevent_socket_new (conn->evb, peer->fd, 0);        if (!conn->bev)  	return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,  				    "bufferevent_socket_new"); +      break; +    case RS_CONN_TYPE_TLS: +      if (rs_tls_init (conn)) +	return -1; +      /* Would be convenient to pass BEV_OPT_CLOSE_ON_FREE but things +	 seem to break when be_openssl_ctrl() (in libevent) calls +	 SSL_set_bio() after BIO_new_socket() with flag=1.  */ +      conn->bev = +	bufferevent_openssl_socket_new (conn->evb, peer->fd, conn->tls_ssl, +					BUFFEREVENT_SSL_CONNECTING, 0); +      if (!conn->bev) +	return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, +				    "bufferevent_openssl_socket_new"); + +      break; +    case RS_CONN_TYPE_DTLS: +      return rs_err_conn_push_fl (conn, RSE_NOSYS, __FILE__, __LINE__, +				  "%s: NYI", __func__); +    default: +      return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__, +				  "%s: invalid connection type: %d", __func__, +				  conn->type);      } +    return RSE_OK;  } diff --git a/lib/radsec.c b/lib/radsec.c index b7ac9ba..40d14fc 100644 --- a/lib/radsec.c +++ b/lib/radsec.c @@ -1,5 +1,9 @@  /* See the file COPYING for licensing information.  */ +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <stdio.h>  #include <stdlib.h>  #include <stdint.h> @@ -12,6 +16,12 @@  #include <event2/util.h>  #include <radsec/radsec.h>  #include <radsec/radsec-impl.h> +#if defined RS_ENABLE_TLS +#include <regex.h> +#include "rsp_list.h" +#include "../radsecproxy.h" +#endif +#include "rsp_debug.h"  int  rs_context_create(struct rs_context **ctx, const char *dict) @@ -48,10 +58,14 @@ rs_context_create(struct rs_context **ctx, const char *dict)  	}        free (buf1);        free (buf2); +#if defined RS_ENABLE_TLS +      ssl_init (); +#endif  #if defined (DEBUG)        fr_log_fp = stderr;        fr_debug_flag = 1;  #endif +      debug_init ("libradsec");	/* radsecproxy compat, FIXME: remove */        memset (h, 0, sizeof(struct rs_context));        fr_randinit (&h->fr_randctx, 0); diff --git a/lib/request.c b/lib/request.c index 5cb87bb..bc6f795 100644 --- a/lib/request.c +++ b/lib/request.c @@ -1,5 +1,9 @@  /* See the file COPYING for licensing information.  */ +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <time.h>  #include <assert.h>  #include <event2/event.h> @@ -39,25 +43,25 @@ _timer_cb(evutil_socket_t fd, short what, void *arg)  static void  _rs_req_connected(void *user_data)  { -  struct rs_request *request = (struct rs_request *)user_data; +  //struct rs_request *request = (struct rs_request *)user_data;  }  static void  _rs_req_disconnected(void *user_data)  { -  struct rs_request *request = (struct rs_request *)user_data; +  //struct rs_request *request = (struct rs_request *)user_data;  }  static void  _rs_req_packet_received(const struct rs_packet *pkt, void *user_data)  { -  struct rs_request *request = (struct rs_request *)user_data; +  //struct rs_request *request = (struct rs_request *)user_data;  }  static void  _rs_req_packet_sent(void *user_data)  { -  struct rs_request *request = (struct rs_request *)user_data; +  //struct rs_request *request = (struct rs_request *)user_data;  }  int diff --git a/lib/rsp_debug.c b/lib/rsp_debug.c new file mode 100644 index 0000000..dd7c053 --- /dev/null +++ b/lib/rsp_debug.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2007 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#ifndef SYS_SOLARIS9 +#include <stdint.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <strings.h> +#include <time.h> +#include <sys/time.h> +#include <syslog.h> +#include <errno.h> +#include <assert.h> +#include "rsp_debug.h" +#include "rsp_util.h" + +static char *debug_ident = NULL; +static uint8_t debug_level = DBG_INFO; +static char *debug_filepath = NULL; +static FILE *debug_file = NULL; +static int debug_syslogfacility = 0; +static uint8_t debug_timestamp = 0; + +void debug_init(char *ident) { +    debug_file = stderr; +    setvbuf(debug_file, NULL, _IONBF, 0); +    debug_ident = ident; +} + +void debug_set_level(uint8_t level) { +    switch (level) { +    case 1: +	debug_level = DBG_ERR; +	return; +    case 2: +	debug_level = DBG_WARN; +	return; +    case 3: +	debug_level = DBG_NOTICE; +	return; +    case 4: +	debug_level = DBG_INFO; +	return; +    case 5: +	debug_level = DBG_DBG; +	return; +    } +} + +void debug_timestamp_on() { +    debug_timestamp = 1; +} + +uint8_t debug_get_level() { +    return debug_level; +} + +int debug_set_destination(char *dest) { +    static const char *facstrings[] = { "LOG_DAEMON", "LOG_MAIL", "LOG_USER", "LOG_LOCAL0", +					"LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4", +					"LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", NULL }; +    static const int facvals[] = { LOG_DAEMON, LOG_MAIL, LOG_USER, LOG_LOCAL0, +				   LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, +				   LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7 }; +    extern int errno; +    int i; + +    if (!strncasecmp(dest, "file:///", 8)) { +	debug_filepath = stringcopy(dest + 7, 0); +	debug_file = fopen(debug_filepath, "a"); +	if (!debug_file) { +	    debug_file = stderr; +	    debugx(1, DBG_ERR, "Failed to open logfile %s\n%s", +		   debug_filepath, strerror(errno)); +	} +	setvbuf(debug_file, NULL, _IONBF, 0); +	return 1; +    } +    if (!strncasecmp(dest, "x-syslog://", 11)) { +	dest += 11; +	if (*dest == '/') +	    dest++; +	if (*dest) { +	    for (i = 0; facstrings[i]; i++) +		if (!strcasecmp(dest, facstrings[i])) +		    break; +	    if (!facstrings[i]) +		debugx(1, DBG_ERR, "Unknown syslog facility %s", dest); +	    debug_syslogfacility = facvals[i]; +	} else +	    debug_syslogfacility = LOG_DAEMON; +	openlog(debug_ident, LOG_PID, debug_syslogfacility); +	return 1; +    } +    debug(DBG_ERR, "Unknown log destination, exiting %s", dest); +    exit(1); +} + +void debug_reopen_log() { +    extern int errno; + +    /* not a file, noop, return success */ +    if (!debug_filepath) { +	debug(DBG_ERR, "skipping reopen"); +	return; +    } + +    if (debug_file != stderr) +	fclose(debug_file); + +    debug_file = fopen(debug_filepath, "a"); +    if (debug_file) +	debug(DBG_ERR, "Reopened logfile %s", debug_filepath); +    else { +	debug_file = stderr; +	debug(DBG_ERR, "Failed to open logfile %s, using stderr\n%s", +	      debug_filepath, strerror(errno)); +    } +    setvbuf(debug_file, NULL, _IONBF, 0); +} + +void debug_logit(uint8_t level, const char *format, va_list ap) { +    struct timeval now; +    char *timebuf; +    int priority; + +    if (debug_syslogfacility) { +	switch (level) { +	case DBG_DBG: +	    priority = LOG_DEBUG; +	    break; +	case DBG_INFO: +	    priority = LOG_INFO; +	    break; +	case DBG_NOTICE: +	    priority = LOG_NOTICE; +	    break; +	case DBG_WARN: +	    priority = LOG_WARNING; +	    break; +	case DBG_ERR: +	    priority = LOG_ERR; +	    break; +	default: +	    priority = LOG_DEBUG; +	} +	vsyslog(priority, format, ap); +    } else { +	if (debug_timestamp && (timebuf = malloc(256))) { +	    gettimeofday(&now, NULL); +	    ctime_r(&now.tv_sec, timebuf); +	    timebuf[strlen(timebuf) - 1] = '\0'; +	    fprintf(debug_file, "%s: ", timebuf + 4); +	    free(timebuf); +	} +	vfprintf(debug_file, format, ap); +	fprintf(debug_file, "\n"); +    } +} + +void debug(uint8_t level, char *format, ...) { +    va_list ap; +    if (level < debug_level) +	return; +    va_start(ap, format); +    debug_logit(level, format, ap); +    va_end(ap); +} + +void debugx(int status, uint8_t level, char *format, ...) { +    if (level >= debug_level) { +	va_list ap; +	va_start(ap, format); +	debug_logit(level, format, ap); +	va_end(ap); +    } +    exit(status); +} + +void debugerrno(int err, uint8_t level, char *format, ...) { +    if (level >= debug_level) { +	va_list ap; +	size_t len = strlen(format); +	char *tmp = malloc(len + 1024 + 2); +	assert(tmp); +	strcpy(tmp, format); +	tmp[len++] = ':'; +	tmp[len++] = ' '; +	if (strerror_r(err, tmp + len, 1024)) +	    tmp = format; +	va_start(ap, format); +	debug_logit(level, tmp, ap); +	va_end(ap); +    } +} + +void debugerrnox(int err, uint8_t level, char *format, ...) { +    if (level >= debug_level) { +	va_list ap; +	va_start(ap, format); +	debugerrno(err, level, format, ap); +	va_end(ap); +    } +    exit(err); +} + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_debug.h b/lib/rsp_debug.h new file mode 100644 index 0000000..803f406 --- /dev/null +++ b/lib/rsp_debug.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2007 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#ifndef SYS_SOLARIS9 +#include <stdint.h> +#endif + +#define DBG_DBG 8 +#define DBG_INFO 16 +#define DBG_NOTICE 32 +#define DBG_WARN 64 +#define DBG_ERR 128 + +void debug_init(char *ident); +void debug_set_level(uint8_t level); +void debug_timestamp_on(); +uint8_t debug_get_level(); +void debug(uint8_t level, char *format, ...); +void debugx(int status, uint8_t level, char *format, ...); +void debugerrno(int err, uint8_t level, char *format, ...); +void debugerrnox(int err, uint8_t level, char *format, ...); +int debug_set_destination(char *dest); +void debug_reopen_log(); + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_hash.c b/lib/rsp_hash.c new file mode 100644 index 0000000..cc37e7f --- /dev/null +++ b/lib/rsp_hash.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2008 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include "rsp_list.h" +#include "rsp_hash.h" + +/* allocates and initialises hash structure; returns NULL if malloc fails */ +struct hash *hash_create() { +    struct hash *h = malloc(sizeof(struct hash)); +    if (!h) +	return NULL; +    h->hashlist = list_create(); +    if (!h->hashlist) { +	free(h); +	return NULL; +    } +    pthread_mutex_init(&h->mutex, NULL); +    return h; +} + +/* frees all memory associated with the hash */ +void hash_destroy(struct hash *h) { +    struct list_node *ln; + +    if (!h) +	return; +    for (ln = list_first(h->hashlist); ln; ln = list_next(ln)) { +	free(((struct hash_entry *)ln->data)->key); +	free(((struct hash_entry *)ln->data)->data); +    } +    list_destroy(h->hashlist); +    pthread_mutex_destroy(&h->mutex); +} + +/* insert entry in hash; returns 1 if ok, 0 if malloc fails */ +int hash_insert(struct hash *h, void *key, uint32_t keylen, void *data) { +    struct hash_entry *e; + +    if (!h) +	return 0; +    e = malloc(sizeof(struct hash_entry)); +    if (!e) +	return 0; +    memset(e, 0, sizeof(struct hash_entry)); +    e->key = malloc(keylen); +    if (!e->key) { +	free(e); +	return 0; +    } +    memcpy(e->key, key, keylen); +    e->keylen = keylen; +    e->data = data; +    pthread_mutex_lock(&h->mutex); +    if (!list_push(h->hashlist, e)) { +	pthread_mutex_unlock(&h->mutex); +	free(e->key); +	free(e); +	return 0; +    } +    pthread_mutex_unlock(&h->mutex); +    return 1; +} + +/* reads entry from hash */ +void *hash_read(struct hash *h, void *key, uint32_t keylen) { +    struct list_node *ln; +    struct hash_entry *e; + +    if (!h) +	return 0; +    pthread_mutex_lock(&h->mutex); +    for (ln = list_first(h->hashlist); ln; ln = list_next(ln)) { +	e = (struct hash_entry *)ln->data; +	if (e->keylen == keylen && !memcmp(e->key, key, keylen)) { +	    pthread_mutex_unlock(&h->mutex); +	    return e->data; +	} +    } +    pthread_mutex_unlock(&h->mutex); +    return NULL; +} + +/* extracts entry from hash */ +void *hash_extract(struct hash *h, void *key, uint32_t keylen) { +    struct list_node *ln; +    struct hash_entry *e; + +    if (!h) +	return 0; +    pthread_mutex_lock(&h->mutex); +    for (ln = list_first(h->hashlist); ln; ln = list_next(ln)) { +	e = (struct hash_entry *)ln->data; +	if (e->keylen == keylen && !memcmp(e->key, key, keylen)) { +	    free(e->key); +	    list_removedata(h->hashlist, e); +	    free(e); +	    pthread_mutex_unlock(&h->mutex); +	    return e->data; +	} +    } +    pthread_mutex_unlock(&h->mutex); +    return NULL; +} + +/* returns first entry */ +struct hash_entry *hash_first(struct hash *hash) { +    struct list_node *ln; +    struct hash_entry *e; +    if (!hash || !((ln = list_first(hash->hashlist)))) +	return NULL; +    e = (struct hash_entry *)ln->data; +    e->next = ln->next; +    return e; +} + +/* returns the next node after the argument */ +struct hash_entry *hash_next(struct hash_entry *entry) { +    struct hash_entry *e; +    if (!entry || !entry->next) +	return NULL; +    e = (struct hash_entry *)entry->next->data; +    e->next = (struct list_node *)entry->next->next; +    return e; +} + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_hash.h b/lib/rsp_hash.h new file mode 100644 index 0000000..48f54a3 --- /dev/null +++ b/lib/rsp_hash.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#ifndef SYS_SOLARIS9 +#include <stdint.h> +#endif + +struct hash { +    struct list *hashlist; +    pthread_mutex_t mutex; +}; + +struct hash_entry { +    void *key; +    uint32_t keylen; +    void *data; +    struct list_node *next; /* used when walking through hash */ +}; + +/* allocates and initialises hash structure; returns NULL if malloc fails */ +struct hash *hash_create(); + +/* frees all memory associated with the hash */ +void hash_destroy(struct hash *hash); + +/* insert entry in hash; returns 1 if ok, 0 if malloc fails */ +int hash_insert(struct hash *hash, void *key, uint32_t keylen, void *data); + +/* reads entry from hash */ +void *hash_read(struct hash *hash, void *key, uint32_t keylen); + +/* extracts (read and remove) entry from hash */ +void *hash_extract(struct hash *hash, void *key, uint32_t keylen); + +/* returns first entry */ +struct hash_entry *hash_first(struct hash *hash); + +/* returns the next entry after the argument */ +struct hash_entry *hash_next(struct hash_entry *entry); + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_list.c b/lib/rsp_list.c new file mode 100644 index 0000000..b6cfe33 --- /dev/null +++ b/lib/rsp_list.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include "rsp_list.h" + +/* allocates and initialises list structure; returns NULL if malloc fails */ +struct list *list_create() { +    struct list *list = malloc(sizeof(struct list)); +    if (list) +	memset(list, 0, sizeof(struct list)); +    return list; +} + +/* frees all memory associated with the list */ +void list_destroy(struct list *list) { +    struct list_node *node, *next; + +    if (!list) +	return; + +    for (node = list->first; node; node = next) { +	free(node->data); +	next = node->next; +	free(node); +    } +    free(list); +} + +/* appends entry to list; returns 1 if ok, 0 if malloc fails */ +int list_push(struct list *list, void *data) { +    struct list_node *node; + +    node = malloc(sizeof(struct list_node)); +    if (!node) +	return 0; + +    node->next = NULL; +    node->data = data; + +    if (list->first) +	list->last->next = node; +    else +	list->first = node; +    list->last = node; + +    list->count++; +    return 1; +} + +/* removes first entry from list and returns data */ +void *list_shift(struct list *list) { +    struct list_node *node; +    void *data; + +    if (!list || !list->first) +	return NULL; + +    node = list->first; +    list->first = node->next; +    if (!list->first) +	list->last = NULL; +    data = node->data; +    free(node); +    list->count--; +    return data; +} + +/* removes all entries with matching data pointer */ +void list_removedata(struct list *list, void *data) { +    struct list_node *node, *t; + +    if (!list || !list->first) +	return; + +    node = list->first; +    while (node->data == data) { +	list->first = node->next; +	free(node); +	list->count--; +	node = list->first; +	if (!node) { +	    list->last = NULL; +	    return; +	} +    } +    for (; node->next; node = node->next) +	if (node->next->data == data) { +	    t = node->next; +	    node->next = t->next; +	    free(t); +	    list->count--; +	    if (!node->next) { /* we removed the last one */ +		list->last = node; +		return; +	    } +	} +} + +/* returns first node */ +struct list_node *list_first(struct list *list) { +    return list ? list->first : NULL; +} + +/* returns the next node after the argument */ +struct list_node *list_next(struct list_node *node) { +    return node->next; +} + +/* returns number of nodes */ +uint32_t list_count(struct list *list) { +    return list->count; +} + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_list.h b/lib/rsp_list.h new file mode 100644 index 0000000..80c0128 --- /dev/null +++ b/lib/rsp_list.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#ifdef SYS_SOLARIS9 +#include <sys/inttypes.h> +#else +#include <stdint.h> +#endif + +struct list_node { +    struct list_node *next; +    void *data; +}; + +struct list { +    struct list_node *first, *last; +    uint32_t count; +}; + +/* allocates and initialises list structure; returns NULL if malloc fails */ +struct list *list_create(); + +/* frees all memory associated with the list */ +void list_destroy(struct list *list); + +/* appends entry to list; returns 1 if ok, 0 if malloc fails */ +int list_push(struct list *list, void *data); + +/* removes first entry from list and returns data */ +void *list_shift(struct list *list); + +/* removes first entry with matching data pointer */ +void list_removedata(struct list *list, void *data); + +/* returns first node */ +struct list_node *list_first(struct list *list); + +/* returns the next node after the argument */ +struct list_node *list_next(struct list_node *node); + +/* returns number of nodes */ +uint32_t list_count(struct list *list); + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_tlscommon.c b/lib/rsp_tlscommon.c new file mode 100644 index 0000000..6002c41 --- /dev/null +++ b/lib/rsp_tlscommon.c @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(RADPROT_TLS) || defined(RADPROT_DTLS) +#include <signal.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#ifdef SYS_SOLARIS9 +#include <fcntl.h> +#endif +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#include <ctype.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <regex.h> +#include <libgen.h> +#include <pthread.h> +#include <openssl/ssl.h> +#include <openssl/rand.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/x509v3.h> +#include "rsp_debug.h" +#include "rsp_list.h" +#include "rsp_hash.h" +#include "rsp_util.h" +#include "../hostport_types.h" +#include "../radsecproxy.h" + +static struct hash *tlsconfs = NULL; + +void ssl_init(void) { +    time_t t; +    pid_t pid; + +    SSL_load_error_strings(); +    SSL_library_init(); + +    while (!RAND_status()) { +	t = time(NULL); +	pid = getpid(); +	RAND_seed((unsigned char *)&t, sizeof(time_t)); +	RAND_seed((unsigned char *)&pid, sizeof(pid)); +    } +} + +static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) { +    int pwdlen = strlen(userdata); +    if (rwflag != 0 || pwdlen > size) /* not for decryption or too large */ +	return 0; +    memcpy(buf, userdata, pwdlen); +    return pwdlen; +} + +static int verify_cb(int ok, X509_STORE_CTX *ctx) { +    char *buf = NULL; +    X509 *err_cert; +    int err, depth; + +    err_cert = X509_STORE_CTX_get_current_cert(ctx); +    err = X509_STORE_CTX_get_error(ctx); +    depth = X509_STORE_CTX_get_error_depth(ctx); + +    if (depth > MAX_CERT_DEPTH) { +	ok = 0; +	err = X509_V_ERR_CERT_CHAIN_TOO_LONG; +	X509_STORE_CTX_set_error(ctx, err); +    } + +    if (!ok) { +	if (err_cert) +	    buf = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0); +	debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf ? buf : ""); +	free(buf); +	buf = NULL; + +	switch (err) { +	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: +	    if (err_cert) { +		buf = X509_NAME_oneline(X509_get_issuer_name(err_cert), NULL, 0); +		if (buf) { +		    debug(DBG_WARN, "\tIssuer=%s", buf); +		    free(buf); +		    buf = NULL; +		} +	    } +	    break; +	case X509_V_ERR_CERT_NOT_YET_VALID: +	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: +	    debug(DBG_WARN, "\tCertificate not yet valid"); +	    break; +	case X509_V_ERR_CERT_HAS_EXPIRED: +	    debug(DBG_WARN, "Certificate has expired"); +	    break; +	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: +	    debug(DBG_WARN, "Certificate no longer valid (after notAfter)"); +	    break; +	case X509_V_ERR_NO_EXPLICIT_POLICY: +	    debug(DBG_WARN, "No Explicit Certificate Policy"); +	    break; +	} +    } +#ifdef DEBUG +    printf("certificate verify returns %d\n", ok); +#endif +    return ok; +} + +#ifdef DEBUG +static void ssl_info_callback(const SSL *ssl, int where, int ret) { +    const char *s; +    int w; + +    w = where & ~SSL_ST_MASK; + +    if (w & SSL_ST_CONNECT) +	s = "SSL_connect"; +    else if (w & SSL_ST_ACCEPT) +	s = "SSL_accept"; +    else +	s = "undefined"; + +    if (where & SSL_CB_LOOP) +	debug(DBG_DBG, "%s:%s\n", s, SSL_state_string_long(ssl)); +    else if (where & SSL_CB_ALERT) { +	s = (where & SSL_CB_READ) ? "read" : "write"; +	debug(DBG_DBG, "SSL3 alert %s:%s:%s\n", s, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); +    } +    else if (where & SSL_CB_EXIT) { +	if (ret == 0) +	    debug(DBG_DBG, "%s:failed in %s\n", s, SSL_state_string_long(ssl)); +	else if (ret < 0) +	    debug(DBG_DBG, "%s:error in %s\n", s, SSL_state_string_long(ssl)); +    } +} +#endif + +static X509_VERIFY_PARAM *createverifyparams(char **poids) { +    X509_VERIFY_PARAM *pm; +    ASN1_OBJECT *pobject; +    int i; + +    pm = X509_VERIFY_PARAM_new(); +    if (!pm) +	return NULL; + +    for (i = 0; poids[i]; i++) { +	pobject = OBJ_txt2obj(poids[i], 0); +	if (!pobject) { +	    X509_VERIFY_PARAM_free(pm); +	    return NULL; +	} +	X509_VERIFY_PARAM_add0_policy(pm, pobject); +    } + +    X509_VERIFY_PARAM_set_flags(pm, X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY); +    return pm; +} + +static int tlsaddcacrl(SSL_CTX *ctx, struct tls *conf) { +    STACK_OF(X509_NAME) *calist; +    X509_STORE *x509_s; +    unsigned long error; + +    if (!SSL_CTX_load_verify_locations(ctx, conf->cacertfile, conf->cacertpath)) { +	while ((error = ERR_get_error())) +	    debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL)); +	debug(DBG_ERR, "tlsaddcacrl: Error updating TLS context %s", conf->name); +	return 0; +    } + +	calist = conf->cacertfile ? SSL_load_client_CA_file(conf->cacertfile) : NULL; + +    if (!conf->cacertfile || calist) { +	if (conf->cacertpath) { +	    if (!calist) +		calist = sk_X509_NAME_new_null(); +	    if (!SSL_add_dir_cert_subjects_to_stack(calist, conf->cacertpath)) { +		sk_X509_NAME_free(calist); +		calist = NULL; +	    } +	} +    } +    if (!calist) { +	while ((error = ERR_get_error())) +	    debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL)); +	debug(DBG_ERR, "tlsaddcacrl: Error adding CA subjects in TLS context %s", conf->name); +	return 0; +    } +    ERR_clear_error(); /* add_dir_cert_subj returns errors on success */ +    SSL_CTX_set_client_CA_list(ctx, calist); + +    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb); +    SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1); + +    if (conf->crlcheck || conf->vpm) { +	x509_s = SSL_CTX_get_cert_store(ctx); +	if (conf->crlcheck) +	    X509_STORE_set_flags(x509_s, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); +	if (conf->vpm) +	    X509_STORE_set1_param(x509_s, conf->vpm); +    } + +    debug(DBG_DBG, "tlsaddcacrl: updated TLS context %s", conf->name); +    return 1; +} + +static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) { +    SSL_CTX *ctx = NULL; +    unsigned long error; + +    switch (type) { +#ifdef RADPROT_TLS +    case RAD_TLS: +	ctx = SSL_CTX_new(TLSv1_method()); +	break; +#endif +#ifdef RADPROT_DTLS +    case RAD_DTLS: +	ctx = SSL_CTX_new(DTLSv1_method()); +	SSL_CTX_set_read_ahead(ctx, 1); +	break; +#endif +    } +    if (!ctx) { +	debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name); +	while ((error = ERR_get_error())) +	    debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL)); +	return NULL; +    } +#ifdef DEBUG +	SSL_CTX_set_info_callback(ctx, ssl_info_callback); +#endif + +    if (conf->certkeypwd) { +	SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->certkeypwd); +	SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb); +    } +    if (conf->certfile || conf->certkeyfile) { +	if (!SSL_CTX_use_certificate_chain_file(ctx, conf->certfile) || +	    !SSL_CTX_use_PrivateKey_file(ctx, conf->certkeyfile, SSL_FILETYPE_PEM) || +	    !SSL_CTX_check_private_key(ctx)) { +	    while ((error = ERR_get_error())) +		debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL)); +	    debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS (certfile issues) in TLS context %s", conf->name); +	    SSL_CTX_free(ctx); +	    return NULL; +	} +    } + +    if (conf->policyoids) { +	if (!conf->vpm) { +	    conf->vpm = createverifyparams(conf->policyoids); +	    if (!conf->vpm) { +		debug(DBG_ERR, "tlscreatectx: Failed to add policyOIDs in TLS context %s", conf->name); +		SSL_CTX_free(ctx); +		return 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; +} + +struct tls *tlsgettls(char *alt1, char *alt2) { +    struct tls *t; + +    t = hash_read(tlsconfs, alt1, strlen(alt1)); +    if (!t) +	t = hash_read(tlsconfs, alt2, strlen(alt2)); +    return t; +} + +SSL_CTX *tlsgetctx(uint8_t type, struct tls *t) { +    struct timeval now; + +    if (!t) +	return NULL; +    gettimeofday(&now, NULL); + +    switch (type) { +#ifdef RADPROT_TLS +    case RAD_TLS: +	if (t->tlsexpiry && t->tlsctx) { +	    if (t->tlsexpiry < now.tv_sec) { +		t->tlsexpiry = now.tv_sec + t->cacheexpiry; +		tlsaddcacrl(t->tlsctx, t); +	    } +	} +	if (!t->tlsctx) { +	    t->tlsctx = tlscreatectx(RAD_TLS, t); +	    if (t->cacheexpiry) +		t->tlsexpiry = now.tv_sec + t->cacheexpiry; +	} +	return t->tlsctx; +#endif +#ifdef RADPROT_DTLS +    case RAD_DTLS: +	if (t->dtlsexpiry && t->dtlsctx) { +	    if (t->dtlsexpiry < now.tv_sec) { +		t->dtlsexpiry = now.tv_sec + t->cacheexpiry; +		tlsaddcacrl(t->dtlsctx, t); +	    } +	} +	if (!t->dtlsctx) { +	    t->dtlsctx = tlscreatectx(RAD_DTLS, t); +	    if (t->cacheexpiry) +		t->dtlsexpiry = now.tv_sec + t->cacheexpiry; +	} +	return t->dtlsctx; +#endif +    } +    return NULL; +} + +X509 *verifytlscert(SSL *ssl) { +    X509 *cert; +    unsigned long error; + +    if (SSL_get_verify_result(ssl) != X509_V_OK) { +	debug(DBG_ERR, "verifytlscert: basic validation failed"); +	while ((error = ERR_get_error())) +	    debug(DBG_ERR, "verifytlscert: TLS: %s", ERR_error_string(error, NULL)); +	return NULL; +    } + +    cert = SSL_get_peer_certificate(ssl); +    if (!cert) +	debug(DBG_ERR, "verifytlscert: failed to obtain certificate"); +    return cert; +} + +static int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) { +    int loc, i, l, n, r = 0; +    char *v; +    X509_EXTENSION *ex; +    STACK_OF(GENERAL_NAME) *alt; +    GENERAL_NAME *gn; + +    debug(DBG_DBG, "subjectaltnameaddr"); + +    loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); +    if (loc < 0) +	return r; + +    ex = X509_get_ext(cert, loc); +    alt = X509V3_EXT_d2i(ex); +    if (!alt) +	return r; + +    n = sk_GENERAL_NAME_num(alt); +    for (i = 0; i < n; i++) { +	gn = sk_GENERAL_NAME_value(alt, i); +	if (gn->type != GEN_IPADD) +	    continue; +	r = -1; +	v = (char *)ASN1_STRING_data(gn->d.ia5); +	l = ASN1_STRING_length(gn->d.ia5); +	if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr))) +	    && !memcmp(v, &addr, l)) { +	    r = 1; +	    break; +	} +    } +    GENERAL_NAMES_free(alt); +    return r; +} + +static int subjectaltnameregexp(X509 *cert, int type, char *exact,  regex_t *regex) { +    int loc, i, l, n, r = 0; +    char *s, *v; +    X509_EXTENSION *ex; +    STACK_OF(GENERAL_NAME) *alt; +    GENERAL_NAME *gn; + +    debug(DBG_DBG, "subjectaltnameregexp"); + +    loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); +    if (loc < 0) +	return r; + +    ex = X509_get_ext(cert, loc); +    alt = X509V3_EXT_d2i(ex); +    if (!alt) +	return r; + +    n = sk_GENERAL_NAME_num(alt); +    for (i = 0; i < n; i++) { +	gn = sk_GENERAL_NAME_value(alt, i); +	if (gn->type != type) +	    continue; +	r = -1; +	v = (char *)ASN1_STRING_data(gn->d.ia5); +	l = ASN1_STRING_length(gn->d.ia5); +	if (l <= 0) +	    continue; +#ifdef DEBUG +	printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, v, l); +#endif +	if (exact) { +	    if (memcmp(v, exact, l)) +		continue; +	} else { +	    s = stringcopy((char *)v, l); +	    if (!s) { +		debug(DBG_ERR, "malloc failed"); +		continue; +	    } +	    if (regexec(regex, s, 0, NULL, 0)) { +		free(s); +		continue; +	    } +	    free(s); +	} +	r = 1; +	break; +    } +    GENERAL_NAMES_free(alt); +    return r; +} + +static int cnregexp(X509 *cert, char *exact, regex_t *regex) { +    int loc, l; +    char *v, *s; +    X509_NAME *nm; +    X509_NAME_ENTRY *e; +    ASN1_STRING *t; + +    nm = X509_get_subject_name(cert); +    loc = -1; +    for (;;) { +	loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc); +	if (loc == -1) +	    break; +	e = X509_NAME_get_entry(nm, loc); +	t = X509_NAME_ENTRY_get_data(e); +	v = (char *) ASN1_STRING_data(t); +	l = ASN1_STRING_length(t); +	if (l < 0) +	    continue; +	if (exact) { +	    if (l == strlen(exact) && !strncasecmp(exact, v, l)) +		return 1; +	} else { +	    s = stringcopy((char *)v, l); +	    if (!s) { +		debug(DBG_ERR, "malloc failed"); +		continue; +	    } +	    if (regexec(regex, s, 0, NULL, 0)) { +		free(s); +		continue; +	    } +	    free(s); +	    return 1; +	} +    } +    return 0; +} + +/* this is a bit sloppy, should not always accept match to any */ +int certnamecheck(X509 *cert, struct list *hostports) { +    struct list_node *entry; +    struct hostportres *hp; +    int r; +    uint8_t type = 0; /* 0 for DNS, AF_INET for IPv4, AF_INET6 for IPv6 */ +    struct in6_addr addr; + +    for (entry = list_first(hostports); entry; entry = list_next(entry)) { +	hp = (struct hostportres *)entry->data; +	if (hp->prefixlen != 255) { +	    /* we disable the check for prefixes */ +	    return 1; +	} +	if (inet_pton(AF_INET, hp->host, &addr)) +	    type = AF_INET; +	else if (inet_pton(AF_INET6, hp->host, &addr)) +	    type = AF_INET6; +	else +	    type = 0; + +	r = type ? subjectaltnameaddr(cert, type, &addr) : subjectaltnameregexp(cert, GEN_DNS, hp->host, NULL); +	if (r) { +	    if (r > 0) { +		debug(DBG_DBG, "certnamecheck: Found subjectaltname matching %s %s", type ? "address" : "host", hp->host); +		return 1; +	    } +	    debug(DBG_WARN, "certnamecheck: No subjectaltname matching %s %s", type ? "address" : "host", hp->host); +	} else { +	    if (cnregexp(cert, hp->host, NULL)) { +		debug(DBG_DBG, "certnamecheck: Found cn matching host %s", hp->host); +		return 1; +	    } +	    debug(DBG_WARN, "certnamecheck: cn not matching host %s", hp->host); +	} +    } +    return 0; +} + +int verifyconfcert(X509 *cert, struct clsrvconf *conf) { +    if (conf->certnamecheck) { +	if (!certnamecheck(cert, conf->hostports)) { +	    debug(DBG_WARN, "verifyconfcert: certificate name check failed"); +	    return 0; +	} +	debug(DBG_WARN, "verifyconfcert: certificate name check ok"); +    } +    if (conf->certcnregex) { +	if (cnregexp(cert, NULL, conf->certcnregex) < 1) { +	    debug(DBG_WARN, "verifyconfcert: CN not matching regex"); +	    return 0; +	} +	debug(DBG_DBG, "verifyconfcert: CN matching regex"); +    } +    if (conf->certuriregex) { +	if (subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex) < 1) { +	    debug(DBG_WARN, "verifyconfcert: subjectaltname URI not matching regex"); +	    return 0; +	} +	debug(DBG_DBG, "verifyconfcert: subjectaltname URI matching regex"); +    } +    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 new file mode 100644 index 0000000..d7e0930 --- /dev/null +++ b/lib/rsp_tlscommon.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#include <openssl/ssl.h> + +struct tls { +    char *name; +    char *cacertfile; +    char *cacertpath; +    char *certfile; +    char *certkeyfile; +    char *certkeypwd; +    uint8_t crlcheck; +    char **policyoids; +    uint32_t cacheexpiry; +    uint32_t tlsexpiry; +    uint32_t dtlsexpiry; +    X509_VERIFY_PARAM *vpm; +    SSL_CTX *tlsctx; +    SSL_CTX *dtlsctx; +}; + +#if defined(RADPROT_TLS) || defined(RADPROT_DTLS) +void ssl_init(); +struct tls *tlsgettls(char *alt1, char *alt2); +SSL_CTX *tlsgetctx(uint8_t type, struct tls *t); +X509 *verifytlscert(SSL *ssl); +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 + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_util.c b/lib/rsp_util.c new file mode 100644 index 0000000..22b8352 --- /dev/null +++ b/lib/rsp_util.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +/* Code contributions from: + * + * Stefan Winter <stefan.winter@restena.lu> + */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/select.h> +#include <stdarg.h> +#include "rsp_debug.h" +#include "rsp_util.h" + +char *stringcopy(const char *s, int len) { +    char *r; +    if (!s) +	return NULL; +    if (!len) +	len = strlen(s); +    r = malloc(len + 1); +    if (!r) +	debug(DBG_ERR, "stringcopy: malloc failed"); +    memcpy(r, s, len); +    r[len] = '\0'; +    return r; +} + +void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len) { +    int i; +    unsigned char *s = (unsigned char *)chars; +    if (prefix) +	printf(prefixfmt ? prefixfmt : "%s: ", prefix); +    for (i = 0; i < len; i++) +	printf(charfmt ? charfmt : "%c", s[i]); +    printf("\n"); +} + +void port_set(struct sockaddr *sa, uint16_t port) { +    switch (sa->sa_family) { +    case AF_INET: +	((struct sockaddr_in *)sa)->sin_port = htons(port); +	break; +    case AF_INET6: +	((struct sockaddr_in6 *)sa)->sin6_port = htons(port); +	break; +    } +} + +struct sockaddr *addr_copy(struct sockaddr *in) { +    struct sockaddr *out = NULL; + +    switch (in->sa_family) { +    case AF_INET: +	out = malloc(sizeof(struct sockaddr_in)); +	if (out) { +	    memset(out, 0, sizeof(struct sockaddr_in)); +	    ((struct sockaddr_in *)out)->sin_addr = ((struct sockaddr_in *)in)->sin_addr; +	} +	break; +    case AF_INET6: +	out = malloc(sizeof(struct sockaddr_in6)); +	if (out) { +	    memset(out, 0, sizeof(struct sockaddr_in6)); +	    ((struct sockaddr_in6 *)out)->sin6_addr = ((struct sockaddr_in6 *)in)->sin6_addr; +	} +	break; +    } +    out->sa_family = in->sa_family; +#ifdef SIN6_LEN +    out->sa_len = in->sa_len; +#endif +    return out; +} + +char *addr2string(struct sockaddr *addr) { +    struct sockaddr_in6 *sa6; +    struct sockaddr_in sa4; +    static char addr_buf[2][INET6_ADDRSTRLEN]; +    static int i = 0; +    i = !i; +    if (addr->sa_family == AF_INET6) { +	sa6 = (struct sockaddr_in6 *)addr; +	if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { +	    memset(&sa4, 0, sizeof(sa4)); +	    sa4.sin_family = AF_INET; +	    sa4.sin_port = sa6->sin6_port; +	    memcpy(&sa4.sin_addr, &sa6->sin6_addr.s6_addr[12], 4); +	    addr = (struct sockaddr *)&sa4; +	} +    } +    if (getnameinfo(addr, SOCKADDRP_SIZE(addr), addr_buf[i], sizeof(addr_buf[i]), +                    NULL, 0, NI_NUMERICHOST)) { +        debug(DBG_WARN, "getnameinfo failed"); +        return "getnameinfo_failed"; +    } +    return addr_buf[i]; +} + +#if 0 +/* not in use */ +int connectport(int type, char *host, char *port) { +    struct addrinfo hints, *res0, *res; +    int s = -1; + +    memset(&hints, 0, sizeof(hints)); +    hints.ai_socktype = type; +    hints.ai_family = AF_UNSPEC; + +    if (getaddrinfo(host, port, &hints, &res0) != 0) { +	debug(DBG_ERR, "connectport: can't resolve host %s port %s", host, port); +	return -1; +    } + +    for (res = res0; res; res = res->ai_next) { +	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); +	if (s < 0) { +	    debug(DBG_WARN, "connectport: socket failed"); +	    continue; +	} +	if (connect(s, res->ai_addr, res->ai_addrlen) == 0) +	    break; +	debug(DBG_WARN, "connectport: connect failed"); +	close(s); +	s = -1; +    } +    freeaddrinfo(res0); +    return s; +} +#endif + +/* Disable the "Don't Fragment" bit for UDP sockets. It is set by default, which may cause an "oversized" +   RADIUS packet to be discarded on first attempt (due to Path MTU discovery). +*/ + +void disable_DF_bit(int socket, struct addrinfo *res) { +    if ((res->ai_family == AF_INET) && (res->ai_socktype == SOCK_DGRAM)) { +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) +        /* +         * Turn off Path MTU discovery on IPv4/UDP sockets, Linux variant. +         */ +	int r, action; +        debug(DBG_INFO, "disable_DF_bit: disabling DF bit (Linux variant)"); +        action = IP_PMTUDISC_DONT; +        r = setsockopt(socket, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action)); +        if (r == -1) +	    debug(DBG_WARN, "Failed to set IP_MTU_DISCOVER"); +#else +	debug(DBG_INFO, "Non-Linux platform, unable to unset DF bit for UDP. You should check with tcpdump whether radsecproxy will send its UDP packets with DF bit set!"); +#endif +    } +} + +int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) { +    int s, on = 1; +    struct addrinfo *res; + +    for (res = addrinfo; res; res = res->ai_next) { +	if (family != AF_UNSPEC && family != res->ai_family) +	    continue; +	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); +	if (s < 0) { +	    debug(DBG_WARN, "bindtoaddr: socket failed"); +	    continue; +	} + +	disable_DF_bit(s,res); + +	if (reuse) +	    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); +#ifdef IPV6_V6ONLY +	if (v6only) +	    setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); +#endif +	if (!bind(s, res->ai_addr, res->ai_addrlen)) +	    return s; +	debug(DBG_WARN, "bindtoaddr: bind failed"); +	close(s); +    } +    return -1; +} + +int connectnonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout) { +    int origflags, error = 0, r = -1; +    fd_set writefds; +    socklen_t len; + +    origflags = fcntl(s, F_GETFL, 0); +    fcntl(s, F_SETFL, origflags | O_NONBLOCK); +    if (!connect(s, addr, addrlen)) { +	r = 0; +	goto exit; +    } +    if (errno != EINPROGRESS) +	goto exit; + +    FD_ZERO(&writefds); +    FD_SET(s, &writefds); +    if (select(s + 1, NULL, &writefds, NULL, timeout) < 1) +	goto exit; + +    len = sizeof(error); +    if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&error, &len) && !error) +	r = 0; + +exit: +    fcntl(s, F_SETFL, origflags); +    return r; +} + +int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) { +    int s; +    struct addrinfo *res; +    struct timeval to; + +    s = -1; +    if (timeout) { +	if (addrinfo && addrinfo->ai_next && timeout > 5) +	    timeout = 5; +	to.tv_sec = timeout; +	to.tv_usec = 0; +    } + +    for (res = addrinfo; res; res = res->ai_next) { +	s = bindtoaddr(src, res->ai_family, 1, 1); +	if (s < 0) { +	    debug(DBG_WARN, "connecttoserver: socket failed"); +	    continue; +	} +	if ((timeout +	     ? connectnonblocking(s, res->ai_addr, res->ai_addrlen, &to) +	     : connect(s, res->ai_addr, res->ai_addrlen)) == 0) +	    break; +	debug(DBG_WARN, "connecttoserver: connect failed"); +	close(s); +	s = -1; +    } +    return s; +} + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/rsp_util.h b/lib/rsp_util.h new file mode 100644 index 0000000..d8d002c --- /dev/null +++ b/lib/rsp_util.h @@ -0,0 +1,25 @@ +#include <sys/socket.h> +#include <netdb.h> + +#define SOCKADDR_SIZE(addr) ((addr).ss_family == AF_INET ?	\ +			     sizeof(struct sockaddr_in) :	\ +			     sizeof(struct sockaddr_in6)) + +#define SOCKADDRP_SIZE(addr) ((addr)->sa_family == AF_INET ?	\ +			      sizeof(struct sockaddr_in) :	\ +			      sizeof(struct sockaddr_in6)) + +char *stringcopy(const char *s, int len); +char *addr2string(struct sockaddr *addr); +struct sockaddr *addr_copy(struct sockaddr *in); +void port_set(struct sockaddr *sa, uint16_t port); + +void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len); +void disable_DF_bit(int socket, struct addrinfo *res); +int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only); +int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout); + + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/tls.c b/lib/tls.c new file mode 100644 index 0000000..15929d2 --- /dev/null +++ b/lib/tls.c @@ -0,0 +1,73 @@ +/* See the file COPYING for licensing information.  */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <openssl/ssl.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> + +#include <regex.h> +#include "rsp_list.h" +#include "../radsecproxy.h" + +static struct tls * +_get_tlsconf (const struct rs_context *ctx, const struct rs_realm *realm) +{ +  struct tls *c = rs_malloc (ctx, sizeof (struct tls)); + +  if (c) +    { +      memset (c, 0, sizeof (struct tls)); +      /* TODO: Make sure old radsecproxy code doesn't free these all +	 of a sudden, or strdup them.  */ +      c->name = realm->name; +      c->cacertfile = realm->cacertfile; +      c->cacertpath = NULL;	/* NYI */ +      c->certfile = realm->certfile; +      c->certkeyfile = realm->certkeyfile; +      c->certkeypwd = NULL;	/* NYI */ +      c->cacheexpiry = 0;	/* NYI */ +      c->crlcheck = 0;		/* NYI */ +      c->policyoids = (char **) NULL; /* NYI */ +    } + +  return c; +} + +int +rs_tls_init (struct rs_connection *conn) +{ +  struct rs_context *ctx; +  struct tls *tlsconf; +  SSL_CTX *ssl_ctx; +  SSL *ssl; +  assert (conn->ctx); +  ctx = conn->ctx; + +  tlsconf = _get_tlsconf (ctx, conn->active_peer->realm); +  assert (tlsconf); +  ssl_ctx = tlsgetctx (RADPROT_TLS, tlsconf); +  if (!ssl_ctx) +    { +      /* TODO: check radsecproxy error  */ +      return rs_err_conn_push_fl (conn, RSE_SOME_ERROR, __FILE__, __LINE__, +				  NULL); +    } + +  ssl = SSL_new (ssl_ctx); +  if (!ssl) +    { +      /* TODO: check and report SSL error  */ +      /* TODO: free ssl_ctx  */ +      return rs_err_conn_push_fl (conn, RSE_SOME_ERROR, __FILE__, __LINE__, +				  NULL); +    } + +  conn->tls_ctx = ssl_ctx; +  conn->tls_ssl = ssl; +  rs_free (ctx, tlsconf); +  return RSE_OK; +} diff --git a/lib/tls.h b/lib/tls.h new file mode 100644 index 0000000..7e10a46 --- /dev/null +++ b/lib/tls.h @@ -0,0 +1 @@ +int rs_tls_init (struct rs_connection *conn); | 
