From 766afb591a4375b7c5d7e07e73798a401fb3369f Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Tue, 9 Dec 2014 13:20:11 +0100 Subject: [PATCH] Added support for reading user configuration from radius. --- doc/sample.config | 2 +- src/Makefile.am | 1 + src/auth/radius.c | 49 +++++++++++++------ src/auth/radius.h | 21 ++++++++ src/config.c | 64 ++++++++++++++++++++++--- src/ocserv-args.def | 7 +-- src/sec-mod-sup-config.c | 7 ++- src/sec-mod-sup-config.h | 3 ++ src/sup-config/radius.c | 100 +++++++++++++++++++++++++++++++++++++++ src/sup-config/radius.h | 28 +++++++++++ src/vpn.h | 1 + 11 files changed, 258 insertions(+), 25 deletions(-) create mode 100644 src/sup-config/radius.c create mode 100644 src/sup-config/radius.h diff --git a/doc/sample.config b/doc/sample.config index 7f92b21c..75649414 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -23,7 +23,7 @@ auth = "plain[./sample.passwd]" #auth = "plain[/etc/ocserv/ocpasswd]" # User authentication using radius. -#auth = "radius[/etc/radiusclient/radiusclient.conf]" +#auth = "radius[/usr/local/etc/radiusclient/radiusclient.conf,groupconfig]" # Whether to enable seccomp worker isolation. That restricts the number of # system calls allowed to a worker process, in order to reduce damage from a diff --git a/src/Makefile.am b/src/Makefile.am index 08991828..e7d79464 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,6 +76,7 @@ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ icmp-ping.c icmp-ping.h \ sec-mod-sup-config.c sec-mod-sup-config.h \ sup-config/file.c sup-config/file.h \ + sup-config/radius.c sup-config/radius.h \ worker-bandwidth.c worker-bandwidth.h ctl.h main-ctl.h \ vasprintf.c vasprintf.h \ proc-search.c proc-search.h \ diff --git a/src/auth/radius.c b/src/auth/radius.c index 4fe78803..b3f12a81 100644 --- a/src/auth/radius.c +++ b/src/auth/radius.c @@ -33,26 +33,20 @@ #include +#if !defined(PW_FRAMED_IPV6_ADDRESS) && defined(PW_TYPE_IPV6ADDR) +#define PW_FRAMED_IPV6_ADDRESS 168 +#define PW_DNS_SERVER_IPV6_ADDRESS 169 +#endif + #define RAD_GROUP_NAME 1030 +#define RAD_IPV4_DNS1 ((311<<16)|(28)) +#define RAD_IPV4_DNS2 ((311<<16)|(29)) int rc_aaa(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received, char *msg, int add_nas_port, int request_type); static rc_handle *rh = NULL; -struct radius_ctx_st { - char username[MAX_USERNAME_SIZE*2]; - char groupname[MAX_GROUPNAME_SIZE]; - char msg[4096]; - - char ipv4[MAX_IP_STR]; - char ipv6[MAX_IP_STR]; - - const char *config; /* radius config file */ - const char *pass_msg; - unsigned retries; -}; - static void radius_global_init(void *pool, void *additional) { rh = rc_read_config(additional); @@ -170,6 +164,7 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) if (ret == OK_RC) { VALUE_PAIR *vp = recvd; + uint32_t ip; while(vp != NULL) { if (vp->attribute == PW_SERVICE_TYPE && vp->lvalue != PW_FRAMED) { syslog(LOG_ERR, @@ -177,9 +172,35 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) (int)vp->lvalue); goto fail; } else if (vp->attribute == RAD_GROUP_NAME && vp->type == PW_TYPE_STRING) { + /* Group-Name */ snprintf(pctx->groupname, sizeof(pctx->groupname), "%s", vp->strvalue); +#ifdef PW_FRAMED_IPV6_ADDRESS + } else if (vp->attribute == PW_FRAMED_IPV6_ADDRESS && vp->type == PW_TYPE_IPV6ADDR) { + /* Framed-IPv6-Address */ + inet_ntop(AF_INET6, vp->strvalue, pctx->ipv6, sizeof(pctx->ipv6)); + } else if (vp->attribute == PW_DNS_SERVER_IPV6_ADDRESS && vp->type == PW_TYPE_IPV6ADDR) { + /* DNS-Server-IPv6-Address */ + if (pctx->ipv6_dns1[0] == 0) + inet_ntop(AF_INET6, vp->strvalue, pctx->ipv6_dns1, sizeof(pctx->ipv6_dns1)); + else + inet_ntop(AF_INET6, vp->strvalue, pctx->ipv6_dns2, sizeof(pctx->ipv6_dns2)); +#endif } else if (vp->attribute == PW_FRAMED_IP_ADDRESS && vp->type == PW_TYPE_IPADDR) { - inet_ntop(AF_INET, &vp->lvalue, pctx->ipv4, sizeof(pctx->ipv4)); + /* Framed-IP-Address */ + ip = htonl(vp->lvalue); + inet_ntop(AF_INET, &ip, pctx->ipv4, sizeof(pctx->ipv4)); + } else if (vp->attribute == PW_FRAMED_IP_NETMASK && vp->type == PW_TYPE_IPADDR) { + /* Framed-IP-Netmask */ + ip = htonl(vp->lvalue); + inet_ntop(AF_INET, &ip, pctx->ipv4_mask, sizeof(pctx->ipv4_mask)); + } else if (vp->attribute == RAD_IPV4_DNS1 && vp->type == PW_TYPE_IPADDR) { + /* MS-Primary-DNS-Server */ + ip = htonl(vp->lvalue); + inet_ntop(AF_INET, &ip, pctx->ipv4_dns1, sizeof(pctx->ipv4_dns1)); + } else if (vp->attribute == RAD_IPV4_DNS2 && vp->type == PW_TYPE_IPADDR) { + /* MS-Secondary-DNS-Server */ + ip = htonl(vp->lvalue); + inet_ntop(AF_INET, &ip, pctx->ipv4_dns2, sizeof(pctx->ipv4_dns2)); } else { syslog(LOG_DEBUG, "radius: ignoring server's value %u of type %u", (int)vp->attribute, (int)vp->type); } diff --git a/src/auth/radius.h b/src/auth/radius.h index fa862020..b98ac823 100644 --- a/src/auth/radius.h +++ b/src/auth/radius.h @@ -23,6 +23,27 @@ #include +struct radius_ctx_st { + char username[MAX_USERNAME_SIZE*2]; + char groupname[MAX_GROUPNAME_SIZE]; + char msg[4096]; + + /* variables for configuration */ + char ipv4[MAX_IP_STR]; + char ipv4_mask[MAX_IP_STR]; + char ipv4_dns1[MAX_IP_STR]; + char ipv4_dns2[MAX_IP_STR]; + + char ipv6[MAX_IP_STR]; + uint16_t ipv6_prefix; + char ipv6_dns1[MAX_IP_STR]; + char ipv6_dns2[MAX_IP_STR]; + + const char *config; /* radius config file */ + const char *pass_msg; + unsigned retries; +}; + extern const struct auth_mod_st radius_auth_funcs; #endif diff --git a/src/config.c b/src/config.c index a62179ec..f42e7938 100644 --- a/src/config.c +++ b/src/config.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -145,7 +146,9 @@ static struct cfg_options available_options[] = { { .name = "default-group-config", .type = OPTION_STRING, .mandatory = 0 }, }; -static char *get_brackets_string(void *pool, const char *str); +#define get_brackets_string get_brackets_string1 +static char *get_brackets_string1(void *pool, const char *str); +static char *get_brackets_string2(void *pool, const char *str); static const tOptionValue* get_option(const char* name, unsigned * mand) { @@ -302,7 +305,7 @@ unsigned j; } } -static char *get_brackets_string(void *pool, const char *str) +static char *get_brackets_string1(void *pool, const char *str) { char *p, *p2; unsigned len; @@ -315,10 +318,47 @@ static char *get_brackets_string(void *pool, const char *str) while (c_isspace(*p)) p++; - p2 = strchr(p, ']'); + p2 = strchr(p, ','); if (p2 == NULL) { - fprintf(stderr, "error parsing %s\n", str); - exit(1); + p2 = strchr(p, ']'); + if (p2 == NULL) { + fprintf(stderr, "error parsing %s\n", str); + exit(1); + } + } + + len = p2 - p; + + return talloc_strndup(pool, p, len); +} + +static char *get_brackets_string2(void *pool, const char *str) +{ + char *p, *p2; + unsigned len; + + p = strchr(str, '['); + if (p == NULL) { + return NULL; + } + p++; + + p = strchr(p, ','); + if (p == NULL) { + return NULL; + } + p++; + + while (c_isspace(*p)) + p++; + + p2 = strchr(p, ','); + if (p2 == NULL) { + p2 = strchr(p, ']'); + if (p2 == NULL) { + fprintf(stderr, "error parsing %s\n", str); + exit(1); + } } len = p2 - p; @@ -366,6 +406,8 @@ unsigned force_cert_auth; prev = val; } while((val = optionNextValue(pov, prev)) != NULL); + config->sup_config_type = SUP_CONFIG_FILE; + READ_MULTI_LINE("auth", auth, auth_size); for (j=0;jauth_types |= amod->type; } else if (strncasecmp(auth[j], "radius", 6) == 0) { + const char *p; if ((config->auth_types & AUTH_TYPE_USERNAME_PASS) != 0) { fprintf(stderr, "You cannot mix multiple username/password authentication methods\n"); exit(1); } #ifdef HAVE_RADIUS - config->auth_additional = get_brackets_string(config, auth[j]+6); + config->auth_additional = get_brackets_string1(config, auth[j]+6); if (config->auth_additional == NULL) { fprintf(stderr, "No configuration specified; error in %s\n", auth[j]); exit(1); } + + p = get_brackets_string2(config, auth[j]+6); + if (p != NULL) { + if (strcasecmp(p, "groupconfig") != 0) { + fprintf(stderr, "No known configuration option: %s\n", p); + exit(1); + } + config->sup_config_type = SUP_CONFIG_RADIUS; + } amod = &radius_auth_funcs; config->auth_types |= amod->type; #else diff --git a/src/ocserv-args.def b/src/ocserv-args.def index 2e6eb46b..10db7245 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -76,7 +76,7 @@ An example configuration file follows. # User authentication method. Could be set multiple times and in # that case all should succeed. To enable multiple methods use # multiple auth directives. Available options: certificate, certificate[optional], -# plain, pam, radius[config]. +# plain, pam, radius[configfile,groupconfig]. #auth = "certificate" #auth = "pam" @@ -97,8 +97,9 @@ An example configuration file follows. #auth = "plain[/etc/ocserv/ocpasswd]" # The radius option requires specifying freeradius-client configuration -# file. -#auth = "radius[/etc/radiusclient/radiusclient.conf]" +# file. If the groupconfig option is set, then config-per-user will be overriden, +# and all configuration will be read from radius. +#auth = "radius[/etc/radiusclient/radiusclient.conf,groupconfig]" # Whether to enable seccomp worker isolation. That restricts the number of # system calls allowed to a worker process, in order to reduce damage from a diff --git a/src/sec-mod-sup-config.c b/src/sec-mod-sup-config.c index 29ce7752..0caa6376 100644 --- a/src/sec-mod-sup-config.c +++ b/src/sec-mod-sup-config.c @@ -29,9 +29,14 @@ #include #include #include +#include void sup_config_init(sec_mod_st *sec) { - sec->config_module = &file_sup_config; + if (sec->config->sup_config_type == SUP_CONFIG_FILE) { + sec->config_module = &file_sup_config; + } else if (sec->config->sup_config_type == SUP_CONFIG_RADIUS) { + sec->config_module = &radius_sup_config; + } } diff --git a/src/sec-mod-sup-config.h b/src/sec-mod-sup-config.h index ef11b041..1252bb79 100644 --- a/src/sec-mod-sup-config.h +++ b/src/sec-mod-sup-config.h @@ -23,6 +23,9 @@ #include +#define SUP_CONFIG_FILE 1 +#define SUP_CONFIG_RADIUS 2 + /* The get_sup_config() should read any additional configuration for * proc->username/proc->groupname and save it in proc->config. */ diff --git a/src/sup-config/radius.c b/src/sup-config/radius.c new file mode 100644 index 00000000..a05bde2e --- /dev/null +++ b/src/sup-config/radius.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_RADIUS + +#include +#include +#include +#include + +static int get_sup_config(struct cfg_st *cfg, client_entry_st *entry, + SecAuthSessionReplyMsg *msg, void *pool) +{ + struct radius_ctx_st *pctx = entry->auth_ctx; + unsigned dns = 0; + + if (pctx == NULL) + return 0; + + if (pctx->ipv4[0] != 0) { + msg->explicit_ipv4 = talloc_strdup(pool, pctx->ipv4); + } + + if (pctx->ipv4_mask[0] != 0) { + msg->ipv4_netmask = talloc_strdup(pool, pctx->ipv4_mask); + } + + if (pctx->ipv4_dns1[0] != 0) + dns++; + if (pctx->ipv4_dns2[0] != 0) + dns++; + if (pctx->ipv6_dns1[0] != 0) + dns++; + if (pctx->ipv6_dns2[0] != 0) + dns++; + + if (dns > 0) { + msg->dns = talloc_size(pool, dns*sizeof(char*)); + if (msg->dns != NULL) { + unsigned pos = 0; + if (pctx->ipv4_dns1[0] != 0) + msg->dns[pos++] = talloc_strdup(pool, pctx->ipv4_dns1); + if (pctx->ipv4_dns2[0] != 0) + msg->dns[pos++] = talloc_strdup(pool, pctx->ipv4_dns2); + if (pctx->ipv6_dns1[0] != 0) + msg->dns[pos++] = talloc_strdup(pool, pctx->ipv6_dns1); + if (pctx->ipv6_dns2[0] != 0) + msg->dns[pos++] = talloc_strdup(pool, pctx->ipv6_dns2); + + msg->n_dns = dns; + } + } + + if (pctx->ipv6[0] != 0) { + msg->explicit_ipv6 = talloc_strdup(pool, pctx->ipv6); + } + + if (pctx->ipv6_prefix != 0) { + msg->ipv6_prefix = pctx->ipv6_prefix; + msg->has_ipv6_prefix = 1; + } + + return 0; +} + +struct config_mod_st radius_sup_config = { + .get_sup_config = get_sup_config, +}; + +#endif diff --git a/src/sup-config/radius.h b/src/sup-config/radius.h new file mode 100644 index 00000000..554e7ab9 --- /dev/null +++ b/src/sup-config/radius.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 Red Hat + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of ocserv. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef SUP_CONFIG_RADIUS_H +#define SUP_CONFIG_RADIUS_H + +#include + +extern struct config_mod_st radius_sup_config; + +#endif diff --git a/src/vpn.h b/src/vpn.h index 527b87a2..5c70a4bc 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -192,6 +192,7 @@ struct cfg_st { unsigned int udp_port; unsigned int is_dyndns; char* unix_conn_file; + unsigned int sup_config_type; /* one of SUP_CONFIG_ */ char *pin_file; char *srk_pin_file;