diff options
Diffstat (limited to 'lib/radsecproxy')
-rw-r--r-- | lib/radsecproxy/Makefile.am | 18 | ||||
-rw-r--r-- | lib/radsecproxy/debug.c | 213 | ||||
-rw-r--r-- | lib/radsecproxy/debug.h | 36 | ||||
-rw-r--r-- | lib/radsecproxy/gconfig.h | 32 | ||||
-rw-r--r-- | lib/radsecproxy/hash.c | 131 | ||||
-rw-r--r-- | lib/radsecproxy/hash.h | 51 | ||||
-rw-r--r-- | lib/radsecproxy/hostport_types.h | 6 | ||||
-rw-r--r-- | lib/radsecproxy/list.c | 122 | ||||
-rw-r--r-- | lib/radsecproxy/list.h | 54 | ||||
-rw-r--r-- | lib/radsecproxy/radmsg.h | 40 | ||||
-rw-r--r-- | lib/radsecproxy/radsecproxy.h | 216 | ||||
-rw-r--r-- | lib/radsecproxy/tlscommon.c | 545 | ||||
-rw-r--r-- | lib/radsecproxy/tlscommon.h | 45 | ||||
-rw-r--r-- | lib/radsecproxy/tlv11.h | 23 | ||||
-rw-r--r-- | lib/radsecproxy/util.c | 256 | ||||
-rw-r--r-- | lib/radsecproxy/util.h | 35 |
16 files changed, 1823 insertions, 0 deletions
diff --git a/lib/radsecproxy/Makefile.am b/lib/radsecproxy/Makefile.am new file mode 100644 index 0000000..16b66ae --- /dev/null +++ b/lib/radsecproxy/Makefile.am @@ -0,0 +1,18 @@ +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I m4 + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir) +AM_CFLAGS = -Wall -g + +noinst_LTLIBRARIES = libradsec-radsecproxy.la + +libradsec_radsecproxy_la_SOURCES = \ + debug.c \ + hash.c \ + list.c \ + util.c + +if RS_ENABLE_TLS +libradsec_radsecproxy_la_SOURCES += \ + tlscommon.c +endif diff --git a/lib/radsecproxy/debug.c b/lib/radsecproxy/debug.c new file mode 100644 index 0000000..8a4881d --- /dev/null +++ b/lib/radsecproxy/debug.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2007-2009, UNINETT AS + * Copyright (c) 2010-2011, NORDUnet A/S */ +/* See LICENSE for licensing information. */ + +#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 "debug.h" +#include "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/radsecproxy/debug.h b/lib/radsecproxy/debug.h new file mode 100644 index 0000000..f9858ab --- /dev/null +++ b/lib/radsecproxy/debug.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2007-2009, UNINETT AS + * Copyright (c) 2010-2011, NORDUnet A/S */ +/* See LICENSE for licensing information. */ + +#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 + +#if defined (__cplusplus) +extern "C" { +#endif + +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(); + +#if defined (__cplusplus) +} +#endif + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/gconfig.h b/lib/radsecproxy/gconfig.h new file mode 100644 index 0000000..3cb34b3 --- /dev/null +++ b/lib/radsecproxy/gconfig.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2007-2008, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#define CONF_STR 1 +#define CONF_CBK 2 +#define CONF_MSTR 3 +#define CONF_BLN 4 +#define CONF_LINT 5 + +#include <stdio.h> + +struct gconffile { + char *path; + FILE *file; + const char *data; + size_t datapos; +}; + +int getconfigline(struct gconffile **cf, char *block, char **opt, char **val, int *conftype); +int getgenericconfig(struct gconffile **cf, char *block, ...); +int pushgconfdata(struct gconffile **cf, const char *data); +FILE *pushgconfpath(struct gconffile **cf, const char *path); +FILE *pushgconffile(struct gconffile **cf, FILE *file, const char *description); +FILE *pushgconfpaths(struct gconffile **cf, const char *path); +int popgconf(struct gconffile **cf); +void freegconfmstr(char **mstr); +void freegconf(struct gconffile **cf); +struct gconffile *openconfigfile(const char *file); + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/hash.c b/lib/radsecproxy/hash.c new file mode 100644 index 0000000..ab17433 --- /dev/null +++ b/lib/radsecproxy/hash.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2008, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include "list.h" +#include "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/radsecproxy/hash.h b/lib/radsecproxy/hash.h new file mode 100644 index 0000000..90ba64b --- /dev/null +++ b/lib/radsecproxy/hash.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2008, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#ifndef SYS_SOLARIS9 +#include <stdint.h> +#endif + +#if defined (__cplusplus) +extern "C" { +#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); + +#if defined (__cplusplus) +} +#endif + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/hostport_types.h b/lib/radsecproxy/hostport_types.h new file mode 100644 index 0000000..01fb443 --- /dev/null +++ b/lib/radsecproxy/hostport_types.h @@ -0,0 +1,6 @@ +struct hostportres { + char *host; + char *port; + uint8_t prefixlen; + struct addrinfo *addrinfo; +}; diff --git a/lib/radsecproxy/list.c b/lib/radsecproxy/list.c new file mode 100644 index 0000000..4cfd358 --- /dev/null +++ b/lib/radsecproxy/list.c @@ -0,0 +1,122 @@ +/* Copyright (c) 2007-2009, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include "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/radsecproxy/list.h b/lib/radsecproxy/list.h new file mode 100644 index 0000000..4f4d1f9 --- /dev/null +++ b/lib/radsecproxy/list.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2007-2009, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#ifdef SYS_SOLARIS9 +#include <sys/inttypes.h> +#else +#include <stdint.h> +#endif + +#if defined (__cplusplus) +extern "C" { +#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); + +#if defined (__cplusplus) +} +#endif + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/radmsg.h b/lib/radsecproxy/radmsg.h new file mode 100644 index 0000000..1bef59b --- /dev/null +++ b/lib/radsecproxy/radmsg.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2007-2008, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#define RAD_Access_Request 1 +#define RAD_Access_Accept 2 +#define RAD_Access_Reject 3 +#define RAD_Accounting_Request 4 +#define RAD_Accounting_Response 5 +#define RAD_Access_Challenge 11 +#define RAD_Status_Server 12 +#define RAD_Status_Client 13 + +#define RAD_Attr_User_Name 1 +#define RAD_Attr_User_Password 2 +#define RAD_Attr_Reply_Message 18 +#define RAD_Attr_Vendor_Specific 26 +#define RAD_Attr_Calling_Station_Id 31 +#define RAD_Attr_Tunnel_Password 69 +#define RAD_Attr_Message_Authenticator 80 + +#define RAD_VS_ATTR_MS_MPPE_Send_Key 16 +#define RAD_VS_ATTR_MS_MPPE_Recv_Key 17 + +struct radmsg { + uint8_t code; + uint8_t id; + uint8_t auth[20]; + struct list *attrs; +}; + +void radmsg_free(struct radmsg *); +struct radmsg *radmsg_init(uint8_t, uint8_t, uint8_t *); +int radmsg_add(struct radmsg *, struct tlv *); +struct tlv *radmsg_gettype(struct radmsg *, uint8_t); +uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *); +struct radmsg *buf2radmsg(uint8_t *, uint8_t *, uint8_t *); + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/radsecproxy.h b/lib/radsecproxy/radsecproxy.h new file mode 100644 index 0000000..7528f7f --- /dev/null +++ b/lib/radsecproxy/radsecproxy.h @@ -0,0 +1,216 @@ +/* + * 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. + */ + +#include "tlv11.h" +#include "radmsg.h" +#include "gconfig.h" + +#define DEBUG_LEVEL 2 + +#define CONFIG_MAIN "/etc/radsecproxy.conf" + +/* MAX_REQUESTS must be 256 due to Radius' 8 bit ID field */ +#define MAX_REQUESTS 256 +#define REQUEST_RETRY_INTERVAL 5 +#define REQUEST_RETRY_COUNT 2 +#define DUPLICATE_INTERVAL REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT +#define MAX_CERT_DEPTH 5 +#define STATUS_SERVER_PERIOD 25 +#define IDLE_TIMEOUT 300 + +/* 27262 is vendor DANTE Ltd. */ +#define DEFAULT_TTL_ATTR "27262:1" + +#define RAD_UDP 0 +#define RAD_TLS 1 +#define RAD_TCP 2 +#define RAD_DTLS 3 +#define RAD_PROTOCOUNT 4 + +struct options { + char *logdestination; + char *ttlattr; + uint32_t ttlattrtype[2]; + uint8_t addttl; + uint8_t loglevel; + uint8_t loopprevention; +}; + +struct commonprotoopts { + char **listenargs; + char *sourcearg; +}; + +struct request { + struct timeval created; + uint32_t refcount; + uint8_t *buf, *replybuf; + struct radmsg *msg; + struct client *from; + struct server *to; + char *origusername; + uint8_t rqid; + uint8_t rqauth[16]; + uint8_t newid; + int udpsock; /* only for UDP */ + uint16_t udpport; /* only for UDP */ +}; + +/* requests that our client will send */ +struct rqout { + pthread_mutex_t *lock; + struct request *rq; + uint8_t tries; + struct timeval expiry; +}; + +struct gqueue { + struct list *entries; + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + +struct clsrvconf { + char *name; + uint8_t type; /* RAD_UDP/RAD_TLS/RAD_TCP */ + const struct protodefs *pdef; + char **hostsrc; + char *portsrc; + struct list *hostports; + char *secret; + char *tls; + char *matchcertattr; + regex_t *certcnregex; + regex_t *certuriregex; + char *confrewritein; + char *confrewriteout; + char *confrewriteusername; + struct modattr *rewriteusername; + char *dynamiclookupcommand; + uint8_t statusserver; + uint8_t retryinterval; + uint8_t retrycount; + uint8_t dupinterval; + uint8_t certnamecheck; + uint8_t addttl; + uint8_t loopprevention; + struct rewrite *rewritein; + struct rewrite *rewriteout; + pthread_mutex_t *lock; /* only used for updating clients so far */ + struct tls *tlsconf; + struct list *clients; + struct server *servers; +}; + +#include "tlscommon.h" + +struct client { + struct clsrvconf *conf; + int sock; + SSL *ssl; + struct request *rqs[MAX_REQUESTS]; + struct gqueue *replyq; + struct gqueue *rbios; /* for dtls */ + struct sockaddr *addr; + time_t expiry; /* for udp */ +}; + +struct server { + struct clsrvconf *conf; + int sock; + SSL *ssl; + pthread_mutex_t lock; + pthread_t clientth; + uint8_t clientrdgone; + struct timeval lastconnecttry; + struct timeval lastreply; + uint8_t connectionok; + uint8_t lostrqs; + uint8_t dynstartup; + char *dynamiclookuparg; + int nextid; + struct timeval lastrcv; + struct rqout *requests; + uint8_t newrq; + pthread_mutex_t newrq_mutex; + pthread_cond_t newrq_cond; + struct gqueue *rbios; /* for dtls */ +}; + +struct realm { + char *name; + char *message; + uint8_t accresp; + regex_t regex; + uint32_t refcount; + pthread_mutex_t mutex; + struct realm *parent; + struct list *subrealms; + struct list *srvconfs; + struct list *accsrvconfs; +}; + +struct modattr { + uint8_t t; + char *replacement; + regex_t *regex; +}; + +struct rewrite { + uint8_t *removeattrs; + uint32_t *removevendorattrs; + struct list *addattrs; + struct list *modattrs; +}; + +struct protodefs { + char *name; + char *secretdefault; + int socktype; + char *portdefault; + uint8_t retrycountdefault; + uint8_t retrycountmax; + uint8_t retryintervaldefault; + uint8_t retryintervalmax; + uint8_t duplicateintervaldefault; + void (*setprotoopts)(struct commonprotoopts *); + char **(*getlistenerargs)(); + void *(*listener)(void*); + int (*connecter)(struct server *, struct timeval *, int, char *); + void *(*clientconnreader)(void*); + int (*clientradput)(struct server *, unsigned char *); + void (*addclient)(struct client *); + void (*addserverextra)(struct clsrvconf *); + void (*setsrcres)(); + void (*initextra)(); +}; + +#define RADLEN(x) ntohs(((uint16_t *)(x))[1]) + +#define ATTRTYPE(x) ((x)[0]) +#define ATTRLEN(x) ((x)[1]) +#define ATTRVAL(x) ((x) + 2) +#define ATTRVALLEN(x) ((x)[1] - 2) + +struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_node **cur); +struct clsrvconf *find_srvconf(uint8_t type, struct sockaddr *addr, struct list_node **cur); +struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur); +struct client *addclient(struct clsrvconf *conf, uint8_t lock); +void removelockedclient(struct client *client); +void removeclient(struct client *client); +struct gqueue *newqueue(); +void freebios(struct gqueue *q); +struct request *newrequest(); +void freerq(struct request *rq); +int radsrv(struct request *rq); +void replyh(struct server *server, unsigned char *buf); +struct addrinfo *resolve_hostport_addrinfo(uint8_t type, char *hostport); + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/tlscommon.c b/lib/radsecproxy/tlscommon.c new file mode 100644 index 0000000..002788d --- /dev/null +++ b/lib/radsecproxy/tlscommon.c @@ -0,0 +1,545 @@ +/* Copyright (c) 2007-2009, UNINETT AS + * Copyright (c) 2010-2011, NORDUnet A/S */ +/* See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#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/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 "debug.h" +#include "list.h" +#include "hash.h" +#include "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; + } + } + 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 (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; +} + +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; +} + +int subjectaltnameaddr(X509 *cert, int family, const 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; +} + +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; + 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; +} + +int cnregexp(X509 *cert, const char *exact, const 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; +} + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/tlscommon.h b/lib/radsecproxy/tlscommon.h new file mode 100644 index 0000000..da2092e --- /dev/null +++ b/lib/radsecproxy/tlscommon.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2007-2009, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#include <netinet/in.h> +#include <openssl/ssl.h> + +#if defined (__cplusplus) +extern "C" { +#endif + +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 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); +#endif + +#if defined (__cplusplus) +} +#endif + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/tlv11.h b/lib/radsecproxy/tlv11.h new file mode 100644 index 0000000..87909c0 --- /dev/null +++ b/lib/radsecproxy/tlv11.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2008, UNINETT AS + * Copyright (c) 2010, NORDUnet A/S */ +/* See LICENSE for licensing information. */ + +struct tlv { + uint8_t t; + uint8_t l; + uint8_t *v; +}; + +struct tlv *maketlv(uint8_t, uint8_t, void *); +struct tlv *copytlv(struct tlv *); +void freetlv(struct tlv *); +int eqtlv(struct tlv *, struct tlv *); +struct list *copytlvlist(struct list *); +void freetlvlist(struct list *); +void rmtlv(struct list *, uint8_t); +uint8_t *tlv2str(struct tlv *tlv); +uint8_t *tlv2buf(uint8_t *, const struct tlv *tlv); + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ diff --git a/lib/radsecproxy/util.c b/lib/radsecproxy/util.c new file mode 100644 index 0000000..ad974ac --- /dev/null +++ b/lib/radsecproxy/util.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2007-2009, UNINETT AS */ +/* See LICENSE for licensing information. */ + +/* Code contributions from: + * + * Stefan Winter <stefan.winter@restena.lu> + */ + +#include <sys/socket.h> +#include <sys/types.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 "debug.h" +#include "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) { + union { + struct sockaddr *sa; + struct sockaddr_in *sa4; + struct sockaddr_in6 *sa6; + } u; + struct sockaddr_in sa4; + static char addr_buf[2][INET6_ADDRSTRLEN]; + static int i = 0; + i = !i; + u.sa = addr; + if (u.sa->sa_family == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED(&u.sa6->sin6_addr)) { + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + sa4.sin_port = u.sa6->sin6_port; + memcpy(&sa4.sin_addr, &u.sa6->sin6_addr.s6_addr[12], 4); + u.sa4 = &sa4; + } + } + if (getnameinfo(u.sa, SOCKADDRP_SIZE(u.sa), 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/radsecproxy/util.h b/lib/radsecproxy/util.h new file mode 100644 index 0000000..cec4673 --- /dev/null +++ b/lib/radsecproxy/util.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2007-2009, UNINETT AS */ +/* See LICENSE for licensing information. */ + +#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)) + +#if defined (__cplusplus) +extern "C" { +#endif + +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); + +#if defined (__cplusplus) +} +#endif + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */ |