summaryrefslogtreecommitdiff
path: root/lib/radsecproxy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/radsecproxy')
-rw-r--r--lib/radsecproxy/Makefile.am18
-rw-r--r--lib/radsecproxy/debug.c213
-rw-r--r--lib/radsecproxy/debug.h36
-rw-r--r--lib/radsecproxy/gconfig.h32
-rw-r--r--lib/radsecproxy/hash.c131
-rw-r--r--lib/radsecproxy/hash.h51
-rw-r--r--lib/radsecproxy/hostport_types.h6
-rw-r--r--lib/radsecproxy/list.c122
-rw-r--r--lib/radsecproxy/list.h54
-rw-r--r--lib/radsecproxy/radmsg.h40
-rw-r--r--lib/radsecproxy/radsecproxy.h216
-rw-r--r--lib/radsecproxy/tlscommon.c545
-rw-r--r--lib/radsecproxy/tlscommon.h45
-rw-r--r--lib/radsecproxy/tlv11.h23
-rw-r--r--lib/radsecproxy/util.c256
-rw-r--r--lib/radsecproxy/util.h35
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: */