diff --git a/README b/README index c4d60ff2..e62720c2 100644 --- a/README +++ b/README @@ -30,6 +30,9 @@ libopts25-dev / autogen-libopts-devel autogen / autogen +For radius support the freeradius-client library is required. Currently +(2014-12-10) the best would be to use the version from the git repository +at: https://github.com/FreeRADIUS/freeradius-client === Build instructions === diff --git a/TODO b/TODO index 397d3cba..f9d2adeb 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,7 @@ Short term items: -* Enhance the current accounting modules to allow, native radius support - with real-time accounting (e.g., sending periodically the transferred - bytes). Use radius for testing it. +* Use the UDP address to report the client's IP when sniproxy or haproxy + are being used. * When a user (IP) gets into the BAN list multiple times, disable it for longer time (or should we drop this functionality altogether and rely diff --git a/configure.ac b/configure.ac index 7127af48..69f13020 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,32 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([ LIBS="$oldlibs" fi +AC_ARG_WITH(radius, + AS_HELP_STRING([--without-radius], [do not include Radius support]), + test_for_radius=$withval, + test_for_radius=yes) + +radius_enabled=no + +if test "$test_for_radius" = yes;then +LIBS="$oldlibs -lfreeradius-client" +AC_MSG_CHECKING([for freeradius client library]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([ + #include ],[ + rc_read_config(0);])], + [AC_MSG_RESULT(yes) + AC_SUBST([FREERADIUS_CLIENT_LIBS], [-lfreeradius-client]) + AC_SUBST([FREERADIUS_CLIENT_CFLAGS], []) + radius_enabled=yes + AC_DEFINE([HAVE_RADIUS], 1, [Enable the Radius library])], + [AC_MSG_RESULT(no) + AC_MSG_WARN([[ +*** +*** libfreeradius-client was not found. Radius support will be disabled. +*** ]])]) +LIBS="$oldlibs" +fi + gl_INIT AC_LIB_HAVE_LINKFLAGS(crypt,, [#define _XOPEN_SOURCE @@ -381,6 +407,7 @@ Summary of build options: CFlags: ${CFLAGS} PAM auth backend: ${pam_enabled} + Radius auth backend: ${radius_enabled} TCP wrappers: ${libwrap_enabled} systemd: ${systemd_enabled} (socket activation) diff --git a/doc/sample.config b/doc/sample.config index e07e36a2..68733db6 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -22,6 +22,14 @@ auth = "plain[./sample.passwd]" # to generate password entries. #auth = "plain[/etc/ocserv/ocpasswd]" +# The radius option requires specifying freeradius-client configuration +# file. If the groupconfig option is set, then config-per-user will be overriden, +# and all configuration will be read from radius. The supported atributes for +# radius configuration are: +# Group-Name, Framed-IPv6-Address, DNS-Server-IPv6-Address, Framed-IP-Address, +# Framed-IP-Netmask, MS-Primary-DNS-Server, MS-Secondary-DNS-Server +#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 # bug in the worker process. It is available on Linux systems at a performance cost. @@ -56,6 +64,12 @@ max-same-clients = 2 # reconnects. #listen-host-is-dyndns = true +# Stats report time. The number of seconds after which each +# worker process will report its usage statistics (number of +# bytes transferred etc). This is useful when accounting like +# radius is in use. +#stats-report-time = 360 + # TCP and UDP port number tcp-port = 443 udp-port = 443 diff --git a/src/Makefile.am b/src/Makefile.am index 1ae8e6f8..e7d79464 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,7 +62,8 @@ endif COMMON_SOURCES=common.c common.h system.c system.h setproctitle.c setproctitle.h # Authentication module sources -AUTH_SOURCES=auth/pam.c auth/pam.h auth/plain.c auth/plain.h +AUTH_SOURCES=auth/pam.c auth/pam.h auth/plain.c auth/plain.h auth/radius.c auth/radius.h \ + auth/common.c auth/common.h ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ cookies.c main-misc.c ip-lease.c ip-lease.h \ @@ -73,8 +74,9 @@ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ sec-mod.c sec-mod-db.c sec-mod-auth.c sec-mod-auth.h sec-mod.h sec-mod-ban.c \ script-list.h $(COMMON_SOURCES) $(AUTH_SOURCES) \ icmp-ping.c icmp-ping.h \ - main-sup-config.c main-sup-config.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 \ @@ -87,7 +89,8 @@ ocserv_SOURCES += ipc.pb-c.h ipc.pb-c.c ctl.pb-c.c ctl.pb-c.h ocserv_LDADD = ../gl/libgnu.a $(NEEDED_LIBOPTS) libcmd-ocserv.a ocserv_LDADD += $(LIBGNUTLS_LIBS) $(PAM_LIBS) $(LIBUTIL) \ $(LIBSECCOMP) $(LIBWRAP) $(LIBCRYPT) $(NEEDED_HTTP_PARSER_LIBS) \ - $(LIBPROTOBUF_C_LIBS) $(LIBSYSTEMD_DAEMON) $(LIBTALLOC_LIBS) + $(LIBPROTOBUF_C_LIBS) $(LIBSYSTEMD_DAEMON) $(LIBTALLOC_LIBS) \ + $(FREERADIUS_CLIENT_LIBS) if PCL diff --git a/src/auth/common.c b/src/auth/common.c new file mode 100644 index 00000000..d55242df --- /dev/null +++ b/src/auth/common.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This file is part of ocserv. + * + * ocserv 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. + * + * ocserv 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 + +const char* pass_msg_first = "Please enter your password."; +const char* pass_msg_second = "Please enter your challenge password."; +const char* pass_msg_failed = "Login failed.\nPlease enter your password."; + diff --git a/src/auth/common.h b/src/auth/common.h new file mode 100644 index 00000000..36c4c5d9 --- /dev/null +++ b/src/auth/common.h @@ -0,0 +1,10 @@ +#ifndef AUTH_COMMON_H +# define AUTH_COMMON_H + +#define MAX_TRIES 3 + +extern const char* pass_msg_first; +extern const char* pass_msg_second; +extern const char* pass_msg_failed; + +#endif diff --git a/src/auth/pam.c b/src/auth/pam.c index 96016f78..27a26218 100644 --- a/src/auth/pam.c +++ b/src/auth/pam.c @@ -348,7 +348,7 @@ struct pam_ctx_st * pctx = ctx; talloc_free(pctx); } -static int pam_auth_open_session(void* ctx) +static int pam_auth_open_session(void* ctx, const void *sid, unsigned sid_size) { struct pam_ctx_st * pctx = ctx; int pret; diff --git a/src/auth/plain.c b/src/auth/plain.c index 0e13241f..bf2e80f7 100644 --- a/src/auth/plain.c +++ b/src/auth/plain.c @@ -29,14 +29,11 @@ #include #include #include "plain.h" +#include "auth/common.h" #include #include #define MAX_CPASS_SIZE 128 -#define MAX_TRIES 3 - -const char* pass_msg_first = "Please enter your password."; -const char* pass_msg_failed = "Login failed.\nPlease enter your password."; struct plain_ctx_st { char username[MAX_USERNAME_SIZE]; diff --git a/src/auth/radius.c b/src/auth/radius.c new file mode 100644 index 00000000..fd6cd838 --- /dev/null +++ b/src/auth/radius.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This file is part of ocserv. + * + * ocserv 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. + * + * ocserv 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 /* inet_ntop */ +#include "radius.h" +#include "auth/common.h" + +#ifdef HAVE_RADIUS + +#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; + +static void radius_global_init(void *pool, void *additional) +{ + rh = rc_read_config(additional); + if (rh == NULL) { + fprintf(stderr, "radius initialization error\n"); + exit(1); + } + + if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { + fprintf(stderr, "error reading the radius dictionary\n"); + exit(1); + } + + return; +} + +static void radius_global_deinit() +{ + if (rh != NULL) + rc_destroy(rh); +} + +static int radius_auth_init(void **ctx, void *pool, const char *username, const char *ip, + void *additional) +{ + struct radius_ctx_st *pctx; + char *default_realm; + + pctx = talloc_zero(pool, struct radius_ctx_st); + if (pctx == NULL) + return ERR_AUTH_FAIL; + + snprintf(pctx->username, sizeof(pctx->username), "%s", username); + pctx->config = additional; + pctx->pass_msg = pass_msg_first; + + default_realm = rc_conf_str(rh, "default_realm"); + + if ((strchr(username, '@') == NULL) && default_realm && + default_realm[0] != 0) { + snprintf(pctx->username, sizeof(pctx->username), "%s@%s", username, default_realm); + } else { + strcpy(pctx->username, username); + } + + + *ctx = pctx; + + return 0; +} + +static int radius_auth_group(void *ctx, const char *suggested, char *groupname, int groupname_size) +{ + struct radius_ctx_st *pctx = ctx; + + groupname[0] = 0; + + if (suggested != NULL) { + if (strcmp(suggested, pctx->groupname) == 0) { + snprintf(groupname, groupname_size, "%s", pctx->groupname); + return 0; + } + + syslog(LOG_AUTH, + "user '%s' requested group '%s' but is not a member", + pctx->username, suggested); + return -1; + } + + if (pctx->groupname[0] != 0 && groupname[0] == 0) { + snprintf(groupname, groupname_size, "%s", pctx->groupname); + } + return 0; +} + +static int radius_auth_user(void *ctx, char *username, int username_size) +{ + /* do not update username */ + return -1; +} + +/* Returns 0 if the user is successfully authenticated, and sets the appropriate group name. + */ +static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) +{ + struct radius_ctx_st *pctx = ctx; + VALUE_PAIR *send = NULL, *recvd = NULL; + uint32_t service; + int ret; + + syslog(LOG_DEBUG, "communicating username (%s) and password to radius", pctx->username); + if (rc_avpair_add(rh, &send, PW_USER_NAME, pctx->username, -1, 0) == NULL) { + syslog(LOG_ERR, + "%s:%u: user '%s' auth error", __func__, __LINE__, + pctx->username); + return ERR_AUTH_FAIL; + } + + if (rc_avpair_add(rh, &send, PW_USER_PASSWORD, (char*)pass, -1, 0) == NULL) { + syslog(LOG_ERR, + "%s:%u: user '%s' auth error", __func__, __LINE__, + pctx->username); + ret = ERR_AUTH_FAIL; + goto cleanup; + } + + service = PW_AUTHENTICATE_ONLY; + if (rc_avpair_add(rh, &send, PW_SERVICE_TYPE, &service, -1, 0) == NULL) { + syslog(LOG_ERR, + "%s:%u: user '%s' auth error", __func__, __LINE__, + pctx->username); + ret = ERR_AUTH_FAIL; + goto cleanup; + } + + ret = rc_aaa(rh, 0, send, &recvd, pctx->msg, 1, PW_ACCESS_REQUEST); + + 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, + "%s:%u: unknown radius service type '%d'", __func__, __LINE__, + (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) { + /* 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); + } + vp = vp->next; + } + + ret = 0; + cleanup: + rc_avpair_free(send); + if (recvd != NULL) + rc_avpair_free(recvd); + return ret; + } else { + fail: + if (send != NULL) + rc_avpair_free(send); + + if (recvd != NULL) + rc_avpair_free(recvd); + + if (ret == PW_ACCESS_CHALLENGE) { + pctx->pass_msg = pass_msg_second; + return ERR_AUTH_CONTINUE; + } else if (pctx->retries++ < MAX_TRIES) { + pctx->pass_msg = pass_msg_failed; + return ERR_AUTH_CONTINUE; + } else { + syslog(LOG_AUTH, + "radius-auth: error authenticating user '%s'", + pctx->username); + return ERR_AUTH_FAIL; + } + } +} + +static int radius_auth_msg(void *ctx, char *msg, size_t msg_size) +{ + struct radius_ctx_st *pctx = ctx; + + snprintf(msg, msg_size, "%s", pctx->pass_msg); + return 0; +} + +static void radius_auth_deinit(void *ctx) +{ + struct radius_ctx_st *pctx = ctx; + talloc_free(pctx); +} + + +static void radius_auth_session_stats(void* ctx, uint64_t bytes_in, uint64_t bytes_out) +{ +struct radius_ctx_st * pctx = ctx; +int ret; +uint32_t status_type; +VALUE_PAIR *send = NULL, *recvd = NULL; +uint32_t uin, uout; + + status_type = PW_STATUS_ALIVE; + + syslog(LOG_DEBUG, "sending radius session interim update"); + + if (rc_avpair_add(rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + if (rc_avpair_add(rh, &send, PW_USER_NAME, pctx->username, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + if (rc_avpair_add(rh, &send, PW_ACCT_SESSION_ID, pctx->sid, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + uin = bytes_in; + uout = bytes_out; + + if (rc_avpair_add(rh, &send, PW_ACCT_INPUT_OCTETS, &uin, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + if (rc_avpair_add(rh, &send, PW_ACCT_OUTPUT_OCTETS, &uout, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + uin = bytes_in / 4294967296; + if (rc_avpair_add(rh, &send, PW_ACCT_INPUT_GIGAWORDS, &uin, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + uout = bytes_in / 4294967296; + if (rc_avpair_add(rh, &send, PW_ACCT_OUTPUT_GIGAWORDS, &uout, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + ret = rc_aaa(rh, 0, send, &recvd, pctx->msg, 1, PW_ACCOUNTING_REQUEST); + + if (recvd != NULL) + rc_avpair_free(recvd); + + if (ret != OK_RC) { + syslog(LOG_AUTH, "radius-auth: radius_open_session: %d", ret); + goto cleanup; + } + + cleanup: + rc_avpair_free(send); + return; +} + +static int radius_auth_open_session(void* ctx, const void *sid, unsigned sid_size) +{ +struct radius_ctx_st * pctx = ctx; +int ret; +uint32_t status_type; +VALUE_PAIR *send = NULL, *recvd = NULL; + + status_type = PW_STATUS_START; + + if (sid_size != SID_SIZE) { + syslog(LOG_DEBUG, "incorrect sid size"); + return -1; + } + + base64_encode((char *)sid, sid_size, (char *)pctx->sid, sizeof(pctx->sid)); + + syslog(LOG_DEBUG, "opening session %s with radius", pctx->sid); + + if (rc_avpair_add(rh, &send, PW_USER_NAME, pctx->username, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + if (rc_avpair_add(rh, &send, PW_ACCT_SESSION_ID, pctx->sid, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + if (rc_avpair_add(rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) { + ret = -1; + goto cleanup; + } + + ret = rc_aaa(rh, 0, send, &recvd, pctx->msg, 1, PW_ACCOUNTING_REQUEST); + + if (recvd != NULL) + rc_avpair_free(recvd); + + if (ret != OK_RC) { + syslog(LOG_AUTH, "radius-auth: radius_open_session: %d", ret); + ret = -1; + goto cleanup; + } + + ret = 0; + cleanup: + rc_avpair_free(send); + return ret; +} + +static void radius_auth_close_session(void* ctx) +{ +struct radius_ctx_st * pctx = ctx; +int ret; +uint32_t status_type; +VALUE_PAIR *send = NULL, *recvd = NULL; + + status_type = PW_STATUS_STOP; + + syslog(LOG_DEBUG, "closing session with radius"); + if (rc_avpair_add(rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) + return; + + if (rc_avpair_add(rh, &send, PW_USER_NAME, pctx->username, -1, 0) == NULL) { + goto cleanup; + } + + if (rc_avpair_add(rh, &send, PW_ACCT_SESSION_ID, pctx->sid, -1, 0) == NULL) { + goto cleanup; + } + + + ret = rc_aaa(rh, 0, send, &recvd, pctx->msg, 1, PW_ACCOUNTING_REQUEST); + if (recvd != NULL) + rc_avpair_free(recvd); + + if (ret != OK_RC) { + syslog(LOG_INFO, "radius-auth: radius_close_session: %d", ret); + goto cleanup; + } + + cleanup: + rc_avpair_free(send); + return; +} + +const struct auth_mod_st radius_auth_funcs = { + .type = AUTH_TYPE_RADIUS | AUTH_TYPE_USERNAME_PASS, + .global_init = radius_global_init, + .global_deinit = radius_global_deinit, + .auth_init = radius_auth_init, + .auth_deinit = radius_auth_deinit, + .auth_msg = radius_auth_msg, + .auth_pass = radius_auth_pass, + .auth_user = radius_auth_user, + .auth_group = radius_auth_group, + .open_session = radius_auth_open_session, + .close_session = radius_auth_close_session, + .session_stats = radius_auth_session_stats, + .group_list = NULL +}; + +#endif diff --git a/src/auth/radius.h b/src/auth/radius.h new file mode 100644 index 00000000..586aa387 --- /dev/null +++ b/src/auth/radius.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * 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 RADIUS_H +#define RADIUS_H + +#include +#include + +struct radius_ctx_st { + char username[MAX_USERNAME_SIZE*2]; + char groupname[MAX_GROUPNAME_SIZE]; + char sid[BASE64_LENGTH(SID_SIZE) + 1]; + + 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/common.c b/src/common.c index 0d251fa6..e8213b1c 100644 --- a/src/common.c +++ b/src/common.c @@ -56,6 +56,8 @@ static char tmp[32]; case CMD_CLI_STATS: return "cli stats"; + case SM_CMD_CLI_STATS: + return "sm: cli stats"; case SM_CMD_AUTH_INIT: return "sm: auth init"; case SM_CMD_AUTH_CONT: diff --git a/src/common.h b/src/common.h index 856fd824..d941a4eb 100644 --- a/src/common.h +++ b/src/common.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2013, 2014 Nikos Mavrogiannopoulos + * Copyright (C) 2014 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -42,6 +43,22 @@ ssize_t force_read_timeout(int sockfd, void *buf, size_t len, unsigned sec); ssize_t recv_timeout(int sockfd, void *buf, size_t len, unsigned sec); int ip_cmp(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2, size_t n); char* ipv6_prefix_to_mask(void *pool, unsigned prefix); +inline static int valid_ipv6_prefix(unsigned prefix) +{ + switch (prefix) { + case 16: + case 32: + case 48: + case 64: + case 80: + case 96: + case 112: + case 128: + return 1; + default: + return 0; + } +} typedef size_t (*pack_func)(const void*, uint8_t *); typedef size_t (*pack_size_func)(const void*); diff --git a/src/config.c b/src/config.c index 0b7e029b..bcf18e07 100644 --- a/src/config.c +++ b/src/config.c @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include #include #include @@ -45,6 +47,7 @@ static char pid_file[_POSIX_PATH_MAX] = ""; static const char* cfg_file = DEFAULT_CFG_FILE; +static const struct auth_mod_st *amod = NULL; struct cfg_options { const char* name; @@ -106,6 +109,7 @@ static struct cfg_options available_options[] = { { .name = "net-priority", .type = OPTION_STRING, .mandatory = 0 }, { .name = "output-buffer", .type = OPTION_NUMERIC, .mandatory = 0 }, { .name = "cookie-timeout", .type = OPTION_NUMERIC, .mandatory = 0 }, + { .name = "stats-report-time", .type = OPTION_NUMERIC, .mandatory = 0 }, { .name = "rekey-time", .type = OPTION_NUMERIC, .mandatory = 0 }, { .name = "rekey-method", .type = OPTION_STRING, .mandatory = 0 }, { .name = "auth-timeout", .type = OPTION_NUMERIC, .mandatory = 0 }, @@ -143,7 +147,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) { @@ -300,7 +306,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; @@ -313,10 +319,13 @@ 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; @@ -324,6 +333,45 @@ static char *get_brackets_string(void *pool, const char *str) 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; + + return talloc_strndup(pool, p, len); +} + +const struct auth_mod_st *get_auth_mod(void) +{ + return amod; +} + static void parse_cfg_file(const char* file, struct cfg_st *config, unsigned reload) { tOptionValue const * pov; @@ -332,7 +380,6 @@ unsigned j, i, mand; char** auth = NULL; unsigned auth_size = 0; unsigned prefix = 0, auto_select_group = 0; -const struct auth_mod_st *amod = NULL; char *tmp; unsigned force_cert_auth; @@ -360,6 +407,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 |= AUTH_TYPE_PAM; + config->auth_types |= amod->type; amod = &pam_auth_funcs; #else fprintf(stderr, "PAM support is disabled\n"); @@ -387,7 +436,35 @@ unsigned force_cert_auth; exit(1); } amod = &plain_auth_funcs; - config->auth_types |= AUTH_TYPE_PLAIN; + config->auth_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_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 + fprintf(stderr, "Radius support is disabled\n"); + exit(1); +#endif } else if (c_strcasecmp(auth[j], "certificate") == 0) { config->auth_types |= AUTH_TYPE_CERTIFICATE; } else if (c_strcasecmp(auth[j], "certificate[optional]") == 0) { @@ -452,8 +529,9 @@ unsigned force_cert_auth; if (config->occtl_socket_file == NULL) config->occtl_socket_file = talloc_strdup(config, OCCTL_UNIX_SOCKET); - if (config->auth_types & AUTH_TYPE_USERNAME_PASS) { - READ_TF("session-control", config->session_control, 0); + val = get_option("session-control", NULL); + if (val != NULL) { + fprintf(stderr, "The option 'session-control' is deprecated\n"); } READ_STRING("banner", config->banner); @@ -496,6 +574,8 @@ unsigned force_cert_auth; READ_TF("deny-roaming", config->deny_roaming, 0); + READ_NUMERIC("stats-report-time", config->stats_report_time); + config->rekey_time = -1; READ_NUMERIC("rekey-time", config->rekey_time); if (config->rekey_time == -1) { @@ -561,10 +641,9 @@ unsigned force_cert_auth; READ_NUMERIC("ipv6-prefix", prefix); if (prefix > 0) { - config->network.ipv6_netmask = ipv6_prefix_to_mask(config, prefix); config->network.ipv6_prefix = prefix; - if (config->network.ipv6_netmask == NULL) { + if (valid_ipv6_prefix(prefix) == 0) { fprintf(stderr, "invalid IPv6 prefix: %u\n", prefix); exit(1); } @@ -642,8 +721,8 @@ static void check_cfg(struct cfg_st *config) exit(1); } - if (config->network.ipv6 != NULL && config->network.ipv6_netmask == NULL) { - fprintf(stderr, "No mask found for IPv6 network.\n"); + if (config->network.ipv6 != NULL && config->network.ipv6_prefix == 0) { + fprintf(stderr, "No prefix found for IPv6 network.\n"); exit(1); } @@ -775,7 +854,6 @@ unsigned i; DEL(config->network.ipv4); DEL(config->network.ipv4_netmask); DEL(config->network.ipv6); - DEL(config->network.ipv6_netmask); for (i=0;inetwork.routes_size;i++) DEL(config->network.routes[i]); DEL(config->network.routes); @@ -822,6 +900,9 @@ void print_version(tOptions *opts, tOptDesc *desc) #ifdef HAVE_LIBWRAP fprintf(stderr, "tcp-wrappers, "); #endif +#ifdef HAVE_RADIUS + fprintf(stderr, "radius, "); +#endif #ifdef HAVE_PAM fprintf(stderr, "PAM, "); #endif @@ -846,6 +927,7 @@ void print_version(tOptions *opts, tOptDesc *desc) void reload_cfg_file(void *pool, struct cfg_st* config) { clear_cfg_file(config); + memset(config, 0, sizeof(*config)); parse_cfg_file(cfg_file, config, 1); diff --git a/src/ip-lease.c b/src/ip-lease.c index 8f95b2c8..723de428 100644 --- a/src/ip-lease.c +++ b/src/ip-lease.c @@ -141,7 +141,34 @@ int get_ipv4_lease(main_server_st* s, struct proc_st* proc) int ret; const char* c_network, *c_netmask; char buf[64]; + + if (proc->config.explicit_ipv4) { + /* if an explicit IP is given for that client, then + * don't do any IP accounting */ + ret = + inet_pton(AF_INET, proc->config.explicit_ipv4, SA_IN_P(&network)); + + if (ret != 1) { + mslog(s, NULL, LOG_ERR, "error reading explicit IP: %s", proc->config.explicit_ipv4); + return -1; + } + + proc->ipv4 = talloc_zero(proc, struct ip_lease_st); + if (proc->ipv4 == NULL) + return ERR_MEM; + + ((struct sockaddr_in*)&network)->sin_family = AF_INET; + ((struct sockaddr_in*)&network)->sin_port = 0; + memcpy(&proc->ipv4->lip, &network, sizeof(struct sockaddr_in)); + proc->ipv4->lip_len = sizeof(struct sockaddr_in); + memcpy(&proc->ipv4->rip, &network, sizeof(struct sockaddr_in)); + proc->ipv4->rip_len = sizeof(struct sockaddr_in); + + return 0; + } + + /* Our IP accounting */ if (proc->config.ipv4_network && proc->config.ipv4_netmask) { c_network = proc->config.ipv4_network; c_netmask = proc->config.ipv4_netmask; @@ -273,15 +300,42 @@ int get_ipv6_lease(main_server_st* s, struct proc_st* proc) struct sockaddr_storage tmp, mask, network, rnd; unsigned i, max_loops = MAX_IP_TRIES; int ret; - const char* c_network, *c_netmask; + const char* c_network; + char *c_netmask = NULL; char buf[64]; - if (proc->config.ipv6_network && proc->config.ipv6_netmask) { + if (proc->config.explicit_ipv6) { + /* if an explicit IP is given for that client, then + * don't do any IP accounting */ + ret = + inet_pton(AF_INET6, proc->config.explicit_ipv6, SA_IN6_P(&network)); + + if (ret != 1) { + mslog(s, NULL, LOG_ERR, "error reading explicit IP: %s", proc->config.explicit_ipv6); + return -1; + } + + proc->ipv6 = talloc_zero(proc, struct ip_lease_st); + if (proc->ipv6 == NULL) + return ERR_MEM; + + ((struct sockaddr_in6*)&network)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)&network)->sin6_port = 0; + memcpy(&proc->ipv6->lip, &network, sizeof(struct sockaddr_in6)); + proc->ipv6->lip_len = sizeof(struct sockaddr_in6); + + memcpy(&proc->ipv6->rip, &network, sizeof(struct sockaddr_in6)); + proc->ipv6->rip_len = sizeof(struct sockaddr_in6); + + return 0; + } + + if (proc->config.ipv6_network && proc->config.ipv6_prefix) { c_network = proc->config.ipv6_network; - c_netmask = proc->config.ipv6_netmask; + c_netmask = ipv6_prefix_to_mask(proc, proc->config.ipv6_prefix); } else { c_network = s->config->network.ipv6; - c_netmask = s->config->network.ipv6_netmask; + c_netmask = ipv6_prefix_to_mask(proc, proc->config.ipv6_prefix); } if (c_network && c_netmask) { @@ -392,6 +446,7 @@ int get_ipv6_lease(main_server_st* s, struct proc_st* proc) return 0; fail: talloc_free(proc->ipv6); + talloc_free(c_netmask); proc->ipv6 = NULL; return ret; @@ -399,7 +454,7 @@ fail: } static -int unref_ip_lease(struct ip_lease_st * lease) +int unref_ip_lease(struct ip_lease_st *lease) { if (lease->db) { htable_del(&lease->db->ht, rehash(lease, NULL), lease); @@ -407,7 +462,7 @@ int unref_ip_lease(struct ip_lease_st * lease) return 0; } -int get_ip_leases(main_server_st* s, struct proc_st* proc) +int get_ip_leases(main_server_st *s, struct proc_st *proc) { int ret; char buf[128]; @@ -417,7 +472,7 @@ char buf[128]; if (ret < 0) return ret; - if (proc->ipv4) { + if (proc->ipv4 && proc->ipv4->db) { if (htable_add(&s->ip_leases.ht, rehash(proc->ipv4, NULL), proc->ipv4) == 0) { mslog(s, proc, LOG_ERR, "could not add IPv4 lease to hash table."); return -1; @@ -431,7 +486,7 @@ char buf[128]; if (ret < 0) return ret; - if (proc->ipv6) { + if (proc->ipv6 && proc->ipv4->db) { if (htable_add(&s->ip_leases.ht, rehash(proc->ipv6, NULL), proc->ipv6) == 0) { mslog(s, proc, LOG_ERR, "could not add IPv6 lease to hash table."); return -1; diff --git a/src/ipc.proto b/src/ipc.proto index 9365f6fd..7879bec3 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -86,6 +86,7 @@ message cli_stats_msg { required uint64 bytes_in = 1; required uint64 bytes_out = 2; + optional bytes sid = 3; /* only required by sec-mod */ } /* UDP_FD */ @@ -192,4 +193,24 @@ message sec_auth_session_msg message sec_auth_session_reply_msg { required AUTH_REP reply = 1; + + /* sup - config */ + optional bool no_udp = 10; + optional bool deny_roaming = 11; + optional bool require_cert = 12; + repeated string routes = 13; + repeated string iroutes = 14; + repeated string dns = 15; + repeated string nbns = 16; + optional string ipv4_net = 17; + optional string ipv4_netmask = 18; + optional string ipv6_net = 19; + optional uint32 ipv6_prefix = 20; + optional string cgroup = 21; + optional string xml_config_file = 22; + optional uint32 rx_per_sec = 23; + optional uint32 tx_per_sec = 24; + optional uint32 net_priority = 25; + optional string explicit_ipv4 = 26; + optional string explicit_ipv6 = 27; } diff --git a/src/main-auth.c b/src/main-auth.c index e5dd214f..5520101a 100644 --- a/src/main-auth.c +++ b/src/main-auth.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "str.h" #include @@ -85,7 +84,6 @@ int send_cookie_auth_reply(main_server_st* s, struct proc_st* proc, } msg.ipv4_netmask = proc->config.ipv4_netmask; - msg.ipv6_netmask = proc->config.ipv6_netmask; msg.ipv4_network = proc->config.ipv4_network; msg.ipv6_network = proc->config.ipv6_network; @@ -210,17 +208,16 @@ struct cookie_entry_st *old; memset(&proc->config, 0, sizeof(proc->config)); apply_default_sup_config(s->config, proc); - if (s->config_module) { - ret = s->config_module->get_sup_config(s->config, proc); - if (ret < 0) { - mslog(s, proc, LOG_ERR, - "error reading additional configuration"); - return ERR_READ_CONFIG; - } + /* loads sup config */ + ret = session_open(s, proc); + if (ret < 0) { + mslog(s, proc, LOG_INFO, "could not open session"); + return -1; + } - if (proc->config.cgroup != NULL) { - put_into_cgroup(s, proc->config.cgroup, proc->pid); - } + /* Put into right cgroup */ + if (proc->config.cgroup != NULL) { + put_into_cgroup(s, proc->config.cgroup, proc->pid); } /* check whether the cookie IP matches */ @@ -284,12 +281,6 @@ struct cookie_entry_st *old; memcpy(proc->ipv4_seed, &cmsg->ipv4_seed, sizeof(proc->ipv4_seed)); - ret = session_open(s, proc); - if (ret < 0) { - mslog(s, proc, LOG_INFO, "could not open session"); - return -1; - } - /* add the links to proc hash */ proc_table_add(s, proc); diff --git a/src/main-misc.c b/src/main-misc.c index 6f41b40e..b5f9ac8c 100644 --- a/src/main-misc.c +++ b/src/main-misc.c @@ -46,7 +46,6 @@ #include #include #include -#include #ifdef HAVE_MALLOC_TRIM # include @@ -168,10 +167,8 @@ int session_cmd(main_server_st * s, struct proc_st *proc, unsigned open) int sd, ret, e; SecAuthSessionMsg ireq = SEC_AUTH_SESSION_MSG__INIT; SecAuthSessionReplyMsg *msg = NULL; - unsigned type; - - if (s->config->session_control == 0) - return 0; + unsigned type, i; + PROTOBUF_ALLOCATOR(pa, proc); if (open) type = SM_CMD_AUTH_SESSION_OPEN; @@ -227,6 +224,86 @@ int session_cmd(main_server_st * s, struct proc_st *proc, unsigned open) mslog(s, proc, LOG_INFO, "could not initiate session for '%s'", proc->username); return -1; } + + /* fill in group_cfg_st */ + if (msg->has_no_udp) + proc->config.no_udp = msg->no_udp; + + if (msg->has_deny_roaming) + proc->config.deny_roaming = msg->deny_roaming; + + if (msg->has_require_cert) + proc->config.require_cert = msg->require_cert; + + if (msg->has_ipv6_prefix) + proc->config.ipv6_prefix = msg->ipv6_prefix; + + if (msg->rx_per_sec) + proc->config.rx_per_sec = msg->rx_per_sec; + if (msg->tx_per_sec) + proc->config.tx_per_sec = msg->tx_per_sec; + + if (msg->net_priority) + proc->config.net_priority = msg->net_priority; + + if (msg->ipv4_net) { + proc->config.ipv4_network = talloc_strdup(proc, msg->ipv4_net); + } + if (msg->ipv4_netmask) { + proc->config.ipv4_netmask = talloc_strdup(proc, msg->ipv4_netmask); + } + if (msg->ipv6_net) { + proc->config.ipv6_network = talloc_strdup(proc, msg->ipv6_net); + } + + if (msg->cgroup) { + proc->config.cgroup = talloc_strdup(proc, msg->cgroup); + } + + if (msg->xml_config_file) { + proc->config.xml_config_file = talloc_strdup(proc, msg->xml_config_file); + } + + if (msg->explicit_ipv4) { + proc->config.explicit_ipv4 = talloc_strdup(proc, msg->explicit_ipv4); + } + + if (msg->explicit_ipv6) { + proc->config.explicit_ipv6 = talloc_strdup(proc, msg->explicit_ipv6); + } + + if (msg->n_routes > 0) { + proc->config.routes = talloc_size(proc, sizeof(char*)*msg->n_routes); + for (i=0;in_routes;i++) { + proc->config.routes[i] = talloc_strdup(proc, msg->routes[i]); + } + proc->config.routes_size = msg->n_routes; + } + + if (msg->n_iroutes > 0) { + proc->config.iroutes = talloc_size(proc, sizeof(char*)*msg->n_iroutes); + for (i=0;in_iroutes;i++) { + proc->config.iroutes[i] = talloc_strdup(proc, msg->iroutes[i]); + } + proc->config.iroutes_size = msg->n_iroutes; + } + + if (msg->n_dns > 0) { + proc->config.dns = talloc_size(proc, sizeof(char*)*msg->n_dns); + for (i=0;in_dns;i++) { + proc->config.dns[i] = talloc_strdup(proc, msg->dns[i]); + } + proc->config.dns_size = msg->n_dns; + } + + if (msg->n_nbns > 0) { + proc->config.nbns = talloc_size(proc, sizeof(char*)*msg->n_nbns); + for (i=0;in_nbns;i++) { + proc->config.nbns[i] = talloc_strdup(proc, msg->nbns[i]); + } + proc->config.nbns_size = msg->n_nbns; + } + sec_auth_session_reply_msg__free_unpacked(msg, &pa); } else { close(sd); } @@ -262,7 +339,7 @@ void remove_proc(main_server_st * s, struct proc_st *proc, unsigned k) } /* close any pending sessions */ - if (s->config->session_control != 0 && proc->active_sid) { + if (proc->active_sid) { session_close(s, proc); } @@ -273,26 +350,17 @@ void remove_proc(main_server_st * s, struct proc_st *proc, unsigned k) proc->pid = -1; remove_iroutes(s, proc); - if (s->config_module) { - s->config_module->clear_sup_config(&proc->config); - } if (proc->ipv4 || proc->ipv6) remove_ip_leases(s, proc); /* expire any available cookies */ if (proc->cookie_ptr) { - unsigned timeout = s->config->cookie_timeout; - proc->cookie_ptr->proc = NULL; - if (s->config->session_control != 0) { - /* if we use session control and we closed the session we - * need to invalidate the cookie, so that a new session is - * used on the next connection */ - proc->cookie_ptr->expiration = 1; - } else { - proc->cookie_ptr->expiration = time(0) + timeout; - } + /* if we use session control and we closed the session we + * need to invalidate the cookie, so that a new session is + * used on the next connection */ + proc->cookie_ptr->expiration = 1; } close_tun(s, proc); diff --git a/src/main.c b/src/main.c index 6afc9b20..893c5e21 100644 --- a/src/main.c +++ b/src/main.c @@ -51,7 +51,6 @@ # include #endif #include -#include #include #include #include @@ -987,8 +986,6 @@ int main(int argc, char** argv) run_sec_mod(s); - sup_config_init(s); - ret = ctl_handler_init(s); if (ret < 0) { fprintf(stderr, "Cannot create command handler\n"); diff --git a/src/main.h b/src/main.h index 1a78c11d..25330c28 100644 --- a/src/main.h +++ b/src/main.h @@ -213,7 +213,6 @@ typedef struct main_server_st { time_t start_time; void * auth_extra; - struct config_mod_st *config_module; #ifdef HAVE_DBUS void * ctl_ctx; @@ -305,4 +304,6 @@ int send_socket_msg_to_worker(main_server_st* s, struct proc_st* proc, uint8_t c void request_reload(int signo); void request_stop(int signo); +const struct auth_mod_st *get_auth_mod(void); + #endif diff --git a/src/ocserv-args.def b/src/ocserv-args.def index 146e59a9..5beffa02 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. +# plain, pam, radius[configfile,groupconfig]. #auth = "certificate" #auth = "pam" @@ -96,11 +96,13 @@ An example configuration file follows. # to generate password entries. #auth = "plain[/etc/ocserv/ocpasswd]" -# Whether to enable the authentication method's session control (i.e., PAM). -# That requires more resources on the server, and makes cookies one-time-use; -# thus don't enable unless you need it. That should be used when you have an -# accounting system in place with the PAM modules. -#session-control = true +# The radius option requires specifying freeradius-client configuration +# file. If the groupconfig option is set, then config-per-user will be overriden, +# and all configuration will be read from radius. The supported atributes for +# radius configuration are: +# Group-Name, Framed-IPv6-Address, DNS-Server-IPv6-Address, Framed-IP-Address, +# Framed-IP-Netmask, MS-Primary-DNS-Server, MS-Secondary-DNS-Server +#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 @@ -141,6 +143,12 @@ udp-port = 3333 # combined with certificate authentication. #listen-clear-file = /var/run/ocserv-conn.socket +# Stats report time. The number of seconds after which each +# worker process will report its usage statistics (number of +# bytes transferred etc). This is useful when accounting like +# radius is in use. +#stats-report-time = 360 + # Keepalive in seconds keepalive = 32400 diff --git a/src/sec-mod-auth.c b/src/sec-mod-auth.c index a9d8d2ff..b740f247 100644 --- a/src/sec-mod-auth.c +++ b/src/sec-mod-auth.c @@ -49,19 +49,26 @@ #include #include #include +#include static const struct auth_mod_st *module = NULL; -void sec_auth_init(struct cfg_st *config) +void sec_auth_init(sec_mod_st * sec, struct cfg_st *config) { -#ifdef HAVE_PAM - if ((config->auth_types & pam_auth_funcs.type) == pam_auth_funcs.type) - module = &pam_auth_funcs; - else -#endif - if ((config->auth_types & plain_auth_funcs.type) == - plain_auth_funcs.type) { - module = &plain_auth_funcs; + module = get_auth_mod(); + + if (module && module->global_init) { + module->global_init(sec, config->auth_additional); + } +} + +void sec_auth_reinit(sec_mod_st * sec, struct cfg_st *config) +{ + if (module) { + if (module != get_auth_mod()) { + seclog(sec, LOG_ERR, "Cannot change authentication method on reload"); + exit(1); + } } } @@ -102,7 +109,7 @@ static int generate_cookie(sec_mod_st * sec, client_entry_st * entry) } static -int send_sec_auth_reply(sec_mod_st * sec, client_entry_st * entry, AUTHREP r) +int send_sec_auth_reply(int cfd, sec_mod_st * sec, client_entry_st * entry, AUTHREP r) { SecAuthReplyMsg msg = SEC_AUTH_REPLY_MSG__INIT; int ret; @@ -130,7 +137,7 @@ int send_sec_auth_reply(sec_mod_st * sec, client_entry_st * entry, AUTHREP r) msg.dtls_session_id.data = entry->dtls_session_id; msg.dtls_session_id.len = sizeof(entry->dtls_session_id); - ret = send_msg(entry, sec->fd, SM_CMD_AUTH_REP, + ret = send_msg(entry, cfd, SM_CMD_AUTH_REP, &msg, (pack_size_func) sec_auth_reply_msg__get_packed_size, @@ -138,7 +145,7 @@ int send_sec_auth_reply(sec_mod_st * sec, client_entry_st * entry, AUTHREP r) } else { msg.reply = AUTH__REP__FAILED; - ret = send_msg(entry, sec->fd, SM_CMD_AUTH_REP, + ret = send_msg(entry, cfd, SM_CMD_AUTH_REP, &msg, (pack_size_func) sec_auth_reply_msg__get_packed_size, @@ -155,7 +162,7 @@ int send_sec_auth_reply(sec_mod_st * sec, client_entry_st * entry, AUTHREP r) } static -int send_sec_auth_reply_msg(sec_mod_st * sec, client_entry_st * e) +int send_sec_auth_reply_msg(int cfd, sec_mod_st * sec, client_entry_st * e) { SecAuthReplyMsg msg = SEC_AUTH_REPLY_MSG__INIT; char tmp[MAX_MSG_SIZE] = ""; @@ -176,7 +183,7 @@ int send_sec_auth_reply_msg(sec_mod_st * sec, client_entry_st * e) msg.sid.data = e->sid; msg.sid.len = sizeof(e->sid); - ret = send_msg(e, sec->fd, SM_CMD_AUTH_REP, &msg, + ret = send_msg(e, cfd, SM_CMD_AUTH_REP, &msg, (pack_size_func) sec_auth_reply_msg__get_packed_size, (pack_func) sec_auth_reply_msg__pack); if (ret < 0) { @@ -256,12 +263,12 @@ static int check_user_group_status(sec_mod_st * sec, client_entry_st * e, * @result: the auth result */ static -int handle_sec_auth_res(sec_mod_st * sec, client_entry_st * e, int result) +int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int result) { int ret; if (result == ERR_AUTH_CONTINUE) { - ret = send_sec_auth_reply_msg(sec, e); + ret = send_sec_auth_reply_msg(cfd, sec, e); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_ERR, "could not send reply auth cmd."); @@ -271,7 +278,7 @@ int handle_sec_auth_res(sec_mod_st * sec, client_entry_st * e, int result) } else if (result == 0) { e->status = PS_AUTH_COMPLETED; - ret = send_sec_auth_reply(sec, e, AUTH__REP__OK); + ret = send_sec_auth_reply(cfd, sec, e, AUTH__REP__OK); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_ERR, "could not send reply auth cmd."); @@ -279,14 +286,11 @@ int handle_sec_auth_res(sec_mod_st * sec, client_entry_st * e, int result) } ret = 0; - if (module != NULL && (sec->config->session_control == 0 || module->open_session == NULL)) { - del_client_entry(sec, e); - } /* else do nothing, and wait for session close/open messages */ } else { e->status = PS_AUTH_FAILED; add_ip_to_ban_list(sec, e->ip, time(0) + sec->config->min_reauth_time); - ret = send_sec_auth_reply(sec, e, AUTH__REP__FAILED); + ret = send_sec_auth_reply(cfd, sec, e, AUTH__REP__FAILED); if (ret < 0) { seclog(sec, LOG_ERR, "could not send reply auth cmd."); return ret; @@ -305,19 +309,13 @@ int handle_sec_auth_res(sec_mod_st * sec, client_entry_st * e, int result) /* opens or closes a session. */ -int handle_sec_auth_session_cmd(sec_mod_st * sec, const SecAuthSessionMsg * req, unsigned cmd) +int handle_sec_auth_session_cmd(int cfd, sec_mod_st * sec, const SecAuthSessionMsg * req, + unsigned cmd) { client_entry_st *e; + void *lpool; int ret; - if (module == NULL || module->open_session == NULL) - return 0; - - if (sec->config->session_control == 0) { - seclog(sec, LOG_ERR, "auth session open/close but session control is disabled!"); - return 0; - } - if (req->sid.len != SID_SIZE) { seclog(sec, LOG_ERR, "auth session open/close but with illegal sid size (%d)!", (int)req->sid.len); @@ -331,14 +329,44 @@ int handle_sec_auth_session_cmd(sec_mod_st * sec, const SecAuthSessionMsg * req, } if (cmd == SM_CMD_AUTH_SESSION_OPEN) { - ret = module->open_session(e->auth_ctx); - if (ret < 0) { - e->status = PS_AUTH_FAILED; - seclog(sec, LOG_ERR, "could not open session."); - del_client_entry(sec, e); - return ret; + SecAuthSessionReplyMsg rep = SEC_AUTH_SESSION_REPLY_MSG__INIT; + + if (module != NULL && module->open_session != NULL) { + ret = module->open_session(e->auth_ctx, req->sid.data, req->sid.len); + if (ret < 0) { + e->status = PS_AUTH_FAILED; + seclog(sec, LOG_ERR, "could not open session."); + del_client_entry(sec, e); + rep.reply = AUTH__REP__FAILED; + } else { + e->have_session = 1; + rep.reply = AUTH__REP__OK; + } + } else { + rep.reply = AUTH__REP__OK; } - e->have_session = 1; + + lpool = talloc_new(e); + if (lpool == NULL) { + return ERR_MEM; + } + + if (sec->config_module && sec->config_module->get_sup_config) { + ret = sec->config_module->get_sup_config(sec->config, e, &rep, lpool); + if (ret < 0) { + seclog(sec, LOG_ERR, "error reading additional configuration for '%s'", e->username); + talloc_free(lpool); + return ERR_READ_CONFIG; + } + } + + ret = send_msg(lpool, cfd, SM_CMD_AUTH_SESSION_REPLY, &rep, + (pack_size_func) sec_auth_session_reply_msg__get_packed_size, + (pack_func) sec_auth_session_reply_msg__pack); + if (ret < 0) { + seclog(sec, LOG_WARNING, "sec-mod error in sending session reply"); + } + talloc_free(lpool); } else { del_client_entry(sec, e); } @@ -346,7 +374,38 @@ int handle_sec_auth_session_cmd(sec_mod_st * sec, const SecAuthSessionMsg * req, return 0; } -int handle_sec_auth_cont(sec_mod_st * sec, const SecAuthContMsg * req) +int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req) +{ + client_entry_st *e; + + if (req->sid.len != SID_SIZE) { + seclog(sec, LOG_ERR, "auth session open/close but with illegal sid size (%d)!", + (int)req->sid.len); + return -1; + } + + e = find_client_entry(sec, req->sid.data); + if (e == NULL) { + seclog(sec, LOG_INFO, "session open/close but with non-existing sid!"); + return -1; + } + + if (e->status != PS_AUTH_COMPLETED) { + seclog(sec, LOG_ERR, "session stats received in unauthenticated client!"); + return -1; + } + + e->bytes_in = req->bytes_in; + e->bytes_out = req->bytes_out; + + if (module == NULL || module->session_stats == NULL) + return 0; + + module->session_stats(e->auth_ctx, e->bytes_in, e->bytes_out); + return 0; +} + +int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req) { client_entry_st *e; int ret; @@ -391,10 +450,10 @@ int handle_sec_auth_cont(sec_mod_st * sec, const SecAuthContMsg * req) e->username); } - return handle_sec_auth_res(sec, e, ret); + return handle_sec_auth_res(cfd, sec, e, ret); } -int handle_sec_auth_init(sec_mod_st * sec, const SecAuthInitMsg * req) +int handle_sec_auth_init(int cfd, sec_mod_st * sec, const SecAuthInitMsg * req) { int ret = -1; client_entry_st *e; @@ -488,7 +547,7 @@ int handle_sec_auth_init(sec_mod_st * sec, const SecAuthInitMsg * req) ret = 0; cleanup: - return handle_sec_auth_res(sec, e, ret); + return handle_sec_auth_res(cfd, sec, e, ret); } void sec_auth_user_deinit(sec_mod_st * sec, client_entry_st * e) diff --git a/src/sec-mod-auth.h b/src/sec-mod-auth.h index 1cde226c..f95cf679 100644 --- a/src/sec-mod-auth.h +++ b/src/sec-mod-auth.h @@ -28,13 +28,16 @@ struct auth_mod_st { unsigned int type; + void (*global_init)(void *pool, void* additional); + void (*global_deinit)(void); int (*auth_init)(void** ctx, void *pool, const char* username, const char* ip, void* additional); int (*auth_msg)(void* ctx, char* msg, size_t msg_size); int (*auth_pass)(void* ctx, const char* pass, unsigned pass_len); int (*auth_group)(void* ctx, const char *suggested, char *groupname, int groupname_size); int (*auth_user)(void* ctx, char *groupname, int groupname_size); - int (*open_session)(void *ctx); /* optional, may be null */ + int (*open_session)(void *ctx, const void *sid, unsigned sid_size); /* optional, may be null */ + void (*session_stats)(void *ctx, uint64_t bytes_in, uint64_t bytes_out); /* optional, may be null */ void (*close_session)(void *ctx); /* optional */ void (*auth_deinit)(void* ctx); diff --git a/src/main-sup-config.c b/src/sec-mod-sup-config.c similarity index 75% rename from src/main-sup-config.c rename to src/sec-mod-sup-config.c index edc37351..0caa6376 100644 --- a/src/main-sup-config.c +++ b/src/sec-mod-sup-config.c @@ -27,11 +27,16 @@ #include #include #include -#include +#include #include +#include -void sup_config_init(main_server_st *s) +void sup_config_init(sec_mod_st *sec) { - s->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/main-sup-config.h b/src/sec-mod-sup-config.h similarity index 79% rename from src/main-sup-config.h rename to src/sec-mod-sup-config.h index 17ea7d45..1252bb79 100644 --- a/src/main-sup-config.h +++ b/src/sec-mod-sup-config.h @@ -21,16 +21,19 @@ #ifndef SUP_CONFIG_H # define SUP_CONFIG_H -#include +#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. */ struct config_mod_st { - int (*get_sup_config)(struct cfg_st *global_config, struct proc_st *proc); - void (*clear_sup_config)(struct group_cfg_st *out); + int (*get_sup_config)(struct cfg_st *global_config, client_entry_st *entry, + SecAuthSessionReplyMsg *msg, void *pool); }; -void sup_config_init(main_server_st *s); +void sup_config_init(sec_mod_st *sec); #endif diff --git a/src/sec-mod.c b/src/sec-mod.c index 66c1b9ed..da4e1559 100644 --- a/src/sec-mod.c +++ b/src/sec-mod.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -166,7 +167,7 @@ int load_pins(struct cfg_st *config, struct pin_st *s) return 0; } -static int handle_op(void *pool, sec_mod_st * sec, uint8_t type, uint8_t * rep, +static int handle_op(void *pool, int cfd, sec_mod_st * sec, uint8_t type, uint8_t * rep, size_t rep_size) { SecOpMsg msg = SEC_OP_MSG__INIT; @@ -175,7 +176,7 @@ static int handle_op(void *pool, sec_mod_st * sec, uint8_t type, uint8_t * rep, msg.data.data = rep; msg.data.len = rep_size; - ret = send_msg(pool, sec->fd, type, &msg, + ret = send_msg(pool, cfd, type, &msg, (pack_size_func) sec_op_msg__get_packed_size, (pack_func) sec_op_msg__pack); if (ret < 0) { @@ -186,7 +187,7 @@ static int handle_op(void *pool, sec_mod_st * sec, uint8_t type, uint8_t * rep, } static -int process_packet(void *pool, sec_mod_st * sec, cmd_request_t cmd, +int process_packet(void *pool, int cfd, sec_mod_st * sec, cmd_request_t cmd, uid_t uid, uint8_t * buffer, size_t buffer_size) { unsigned i; @@ -243,11 +244,27 @@ int process_packet(void *pool, sec_mod_st * sec, cmd_request_t cmd, return -1; } - ret = handle_op(pool, sec, cmd, out.data, out.size); + ret = handle_op(pool, cfd, sec, cmd, out.data, out.size); gnutls_free(out.data); return ret; + case SM_CMD_CLI_STATS:{ + CliStatsMsg *tmsg; + + tmsg = cli_stats_msg__unpack(&pa, data.size, data.data); + if (tmsg == NULL) { + seclog(sec, LOG_ERR, "error unpacking data"); + ret = ERR_BAD_COMMAND; + return -1; + } + + ret = handle_sec_auth_stats_cmd(sec, tmsg); + cli_stats_msg__free_unpacked(tmsg, &pa); + return ret; + } + break; + case SM_CMD_AUTH_INIT:{ SecAuthInitMsg *auth_init; @@ -259,7 +276,7 @@ int process_packet(void *pool, sec_mod_st * sec, cmd_request_t cmd, return -1; } - ret = handle_sec_auth_init(sec, auth_init); + ret = handle_sec_auth_init(cfd, sec, auth_init); sec_auth_init_msg__free_unpacked(auth_init, &pa); return ret; } @@ -274,14 +291,13 @@ int process_packet(void *pool, sec_mod_st * sec, cmd_request_t cmd, return -1; } - ret = handle_sec_auth_cont(sec, auth_cont); + ret = handle_sec_auth_cont(cfd, sec, auth_cont); sec_auth_cont_msg__free_unpacked(auth_cont, &pa); return ret; } case SM_CMD_AUTH_SESSION_OPEN: case SM_CMD_AUTH_SESSION_CLOSE:{ SecAuthSessionMsg *msg; - SecAuthSessionReplyMsg rep = SEC_AUTH_SESSION_REPLY_MSG__INIT; if (uid != 0) { seclog(sec, LOG_INFO, "received session open/close from unauthorized uid (%u)\n", (unsigned)uid); @@ -296,23 +312,9 @@ int process_packet(void *pool, sec_mod_st * sec, cmd_request_t cmd, return -1; } - ret = handle_sec_auth_session_cmd(sec, msg, cmd); + ret = handle_sec_auth_session_cmd(cfd, sec, msg, cmd); sec_auth_session_msg__free_unpacked(msg, &pa); - if (cmd == SM_CMD_AUTH_SESSION_OPEN) { - if (ret < 0) - rep.reply = AUTH__REP__FAILED; - else - rep.reply = AUTH__REP__OK; - - ret = send_msg(pool, sec->fd, SM_CMD_AUTH_SESSION_REPLY, &rep, - (pack_size_func) sec_auth_session_reply_msg__get_packed_size, - (pack_func) sec_auth_session_reply_msg__pack); - if (ret < 0) { - seclog(sec, LOG_WARNING, "sec-mod error in sending session reply"); - } - } - return ret; } default: @@ -356,6 +358,7 @@ static void check_other_work(sec_mod_st *sec) if (need_reload) { seclog(sec, LOG_DEBUG, "reloading configuration"); reload_cfg_file(sec, sec->config); + sec_auth_reinit(sec, sec->config); need_reload = 0; } @@ -371,6 +374,54 @@ static void check_other_work(sec_mod_st *sec) } } +static +void serve_request(sec_mod_st *sec, uid_t uid, int cfd, uint8_t *buffer, unsigned buffer_size) +{ + int ret, e; + unsigned cmd, length; + uint16_t l16; + void *pool = buffer; + + /* read request */ + ret = force_read_timeout(cfd, buffer, 3, MAX_WAIT_SECS); + if (ret == 0) + goto leave; + else if (ret < 3) { + e = errno; + seclog(sec, LOG_INFO, "error receiving msg head: %s", + strerror(e)); + goto leave; + } + + cmd = buffer[0]; + memcpy(&l16, &buffer[1], 2); + length = l16; + + if (length > buffer_size - 4) { + seclog(sec, LOG_INFO, "too big message (%d)", length); + goto leave; + } + + /* read the body */ + ret = force_read_timeout(cfd, buffer, length, MAX_WAIT_SECS); + if (ret < 0) { + e = errno; + seclog(sec, LOG_INFO, "error receiving msg body: %s", + strerror(e)); + goto leave; + } + + ret = process_packet(pool, cfd, sec, cmd, uid, buffer, ret); + if (ret < 0) { + seclog(sec, LOG_INFO, "error processing data for '%s' command (%d)", cmd_request_to_str(cmd), ret); + } + + leave: + talloc_free(pool); + close(cfd); + return; +} + /* sec_mod_server: * @config: server configuration * @socket_file: the name of the socket @@ -404,11 +455,9 @@ void sec_mod_server(void *main_pool, struct cfg_st *config, const char *socket_f struct sockaddr_un sa; socklen_t sa_len; int cfd, ret, e; - unsigned cmd, length; unsigned i, buffer_size; uid_t uid; - uint8_t *buffer, *tpool; - uint16_t l16; + uint8_t *buffer; struct pin_st pins; int sd; sec_mod_st *sec; @@ -435,6 +484,8 @@ void sec_mod_server(void *main_pool, struct cfg_st *config, const char *socket_f sec->dcookie_key.size = COOKIE_KEY_SIZE; sec->config = talloc_steal(sec, config); + sup_config_init(sec); + memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", socket_file); @@ -452,7 +503,7 @@ void sec_mod_server(void *main_pool, struct cfg_st *config, const char *socket_f alarm(MAINTAINANCE_TIME); - sec_auth_init(config); + sec_auth_init(sec, config); #ifdef HAVE_PKCS11 ret = gnutls_pkcs11_reinit(); @@ -470,13 +521,6 @@ void sec_mod_server(void *main_pool, struct cfg_st *config, const char *socket_f if (config->min_reauth_time > 0) sec_mod_ban_db_init(sec); - buffer_size = 8 * 1024; - buffer = talloc_size(sec, buffer_size); - if (buffer == NULL) { - seclog(sec, LOG_ERR, "error in memory allocation"); - exit(1); - } - sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd == -1) { @@ -578,50 +622,18 @@ void sec_mod_server(void *main_pool, struct cfg_st *config, const char *socket_f ret = check_upeer_id("sec-mod", cfd, config->uid, config->gid, &uid); if (ret < 0) { seclog(sec, LOG_INFO, "rejected unauthorized connection"); - goto cont; + } else { + buffer_size = 8 * 1024; + buffer = talloc_size(sec, buffer_size); + if (buffer == NULL) { + seclog(sec, LOG_ERR, "error in memory allocation"); + close(cfd); + } else { + serve_request(sec, uid, cfd, buffer, buffer_size); + } } - - /* read request */ - ret = force_read_timeout(cfd, buffer, 3, MAX_WAIT_SECS); - if (ret == 0) - goto cont; - else if (ret < 3) { - e = errno; - seclog(sec, LOG_INFO, "error receiving msg head: %s", - strerror(e)); - goto cont; - } - - cmd = buffer[0]; - memcpy(&l16, &buffer[1], 2); - length = l16; - - if (length > buffer_size - 4) { - seclog(sec, LOG_INFO, "too big message (%d)", length); - goto cont; - } - - /* read the body */ - ret = force_read_timeout(cfd, buffer, length, MAX_WAIT_SECS); - if (ret < 0) { - e = errno; - seclog(sec, LOG_INFO, "error receiving msg body: %s", - strerror(e)); - goto cont; - } - - tpool = talloc_new(sec); - sec->fd = cfd; - ret = process_packet(tpool, sec, cmd, uid, buffer, ret); - if (ret < 0) { - seclog(sec, LOG_INFO, "error processing data for '%s' command (%d)", cmd_request_to_str(cmd), ret); - } - talloc_free(tpool); - #ifdef DEBUG_LEAKS talloc_report_full(sec, stderr); #endif - cont: - close(cfd); } } diff --git a/src/sec-mod.h b/src/sec-mod.h index 3def32c5..45fc2d4d 100644 --- a/src/sec-mod.h +++ b/src/sec-mod.h @@ -19,6 +19,7 @@ * along with this program. If not, see */ #ifndef SEC_MOD_H +# define SEC_MOD_H #include #include @@ -34,7 +35,7 @@ typedef struct sec_mod_st { struct htable *client_db; struct htable *ban_db; - int fd; + struct config_mod_st *config_module; } sec_mod_st; @@ -49,6 +50,11 @@ typedef struct client_entry_st { unsigned have_session; /* whether an auth session is initialized */ unsigned tls_auth_ok; + /* these are filled in after the worker process dies, using the + * Cli stats message. */ + uint64_t bytes_in; + uint64_t bytes_out; + unsigned status; /* PS_AUTH_ */ char ip[MAX_IP_STR]; /* the user's IP */ @@ -83,11 +89,13 @@ void cleanup_client_entries(sec_mod_st *sec); } #endif -void sec_auth_init(struct cfg_st *config); +void sec_auth_init(sec_mod_st *sec, struct cfg_st *config); +void sec_auth_reinit(sec_mod_st *sec, struct cfg_st *config); -int handle_sec_auth_init(sec_mod_st *sec, const SecAuthInitMsg * req); -int handle_sec_auth_cont(sec_mod_st *sec, const SecAuthContMsg * req); -int handle_sec_auth_session_cmd(sec_mod_st * sec, const SecAuthSessionMsg * req, unsigned cmd); +int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg * req); +int handle_sec_auth_cont(int cfd, sec_mod_st *sec, const SecAuthContMsg * req); +int handle_sec_auth_session_cmd(int cfd, sec_mod_st *sec, const SecAuthSessionMsg *req, unsigned cmd); +int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req); void sec_auth_user_deinit(sec_mod_st * sec, client_entry_st * e); void sec_mod_server(void *main_pool, struct cfg_st *config, const char *socket_file, diff --git a/src/sup-config/file.c b/src/sup-config/file.c index c91138f5..ba9202f4 100644 --- a/src/sup-config/file.c +++ b/src/sup-config/file.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Nikos Mavrogiannopoulos + * 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 @@ -33,7 +34,7 @@ #include #include -#include +#include struct cfg_options { const char* name; @@ -68,14 +69,14 @@ static struct cfg_options available_options[] = { if (val != NULL && val->valType == OPARG_TYPE_STRING) { \ if (s_name == NULL) { \ num = 0; \ - s_name = talloc_size(proc, sizeof(char*)*MAX_CONFIG_ENTRIES); \ + s_name = talloc_size(pool, sizeof(char*)*MAX_CONFIG_ENTRIES); \ } \ do { \ if (num >= MAX_CONFIG_ENTRIES) \ break; \ if (val && !strcmp(val->pzName, name)==0) \ continue; \ - s_name[num] = talloc_strdup(proc, val->v.strVal); \ + s_name[num] = talloc_strdup(pool, val->v.strVal); \ num++; \ } while((val = optionNextValue(pov, val)) != NULL); \ s_name[num] = NULL; \ @@ -86,28 +87,33 @@ static struct cfg_options available_options[] = { if (val != NULL && val->valType == OPARG_TYPE_STRING) { \ if (s_name != NULL) \ talloc_free(s_name); \ - s_name = talloc_strdup(proc, val->v.strVal); \ + s_name = talloc_strdup(pool, val->v.strVal); \ }} -#define READ_RAW_NUMERIC(name, s_name) { \ +#define READ_RAW_NUMERIC(name, s_name, def) { \ val = optionGetValue(pov, name); \ if (val != NULL) { \ - if (val->valType == OPARG_TYPE_NUMERIC) \ + if (val->valType == OPARG_TYPE_NUMERIC) {\ s_name = val->v.longVal; \ - else if (val->valType == OPARG_TYPE_STRING) \ + def = 1; \ + } else if (val->valType == OPARG_TYPE_STRING) {\ s_name = atoi(val->v.strVal); \ + def = 1; \ + } \ }} -#define READ_RAW_PRIO_TOS(name, s_name) { \ +#define READ_RAW_PRIO_TOS(name, s_name, def) { \ val = optionGetValue(pov, name); \ if (val != NULL) { \ if (val->valType == OPARG_TYPE_STRING) { \ if (strncmp(val->v.strVal, "0x", 2) == 0) { \ s_name = strtol(val->v.strVal, NULL, 16); \ s_name = TOS_PACK(s_name); \ + def = 1; \ } else { \ s_name = atoi(val->v.strVal); \ s_name++; \ + def = 1; \ } \ } \ }} @@ -115,12 +121,13 @@ static struct cfg_options available_options[] = { #define READ_TF(name, s_name, def) { \ { char* tmp_tf = NULL; \ READ_RAW_STRING(name, tmp_tf); \ - if (tmp_tf == NULL) s_name = def; \ - else { \ + if (tmp_tf == NULL) { def = 0; \ + } else { \ if (c_strcasecmp(tmp_tf, "true") == 0 || c_strcasecmp(tmp_tf, "yes") == 0) \ s_name = 1; \ else \ s_name = 0; \ + def = 1; \ } \ talloc_free(tmp_tf); \ }} @@ -143,13 +150,13 @@ unsigned j; * already allocated using this function. */ static -int parse_group_cfg_file(struct cfg_st *global_config, struct proc_st *proc, +int parse_group_cfg_file(struct cfg_st *global_config, + SecAuthSessionReplyMsg *msg, void *pool, const char* file) { tOptionValue const * pov; const tOptionValue* val, *prev; unsigned prefix = 0; -struct group_cfg_st *sconfig = &proc->config; pov = configFileLoad(file); if (pov == NULL) { @@ -171,60 +178,58 @@ struct group_cfg_st *sconfig = &proc->config; prev = val; } while((val = optionNextValue(pov, prev)) != NULL); - READ_TF("no-udp", sconfig->no_udp, (global_config->udp_port!=0)?0:1); - READ_TF("deny-roaming", sconfig->deny_roaming, global_config->deny_roaming); - READ_TF("require-cert", sconfig->require_cert, 0); + READ_TF("no-udp", msg->no_udp, msg->has_no_udp); + READ_TF("deny-roaming", msg->deny_roaming, msg->has_deny_roaming); + READ_TF("require-cert", msg->require_cert, msg->has_require_cert); - READ_RAW_MULTI_LINE("route", sconfig->routes, sconfig->routes_size); - READ_RAW_MULTI_LINE("iroute", sconfig->iroutes, sconfig->iroutes_size); + READ_RAW_MULTI_LINE("route", msg->routes, msg->n_routes); + READ_RAW_MULTI_LINE("iroute", msg->iroutes, msg->n_iroutes); - READ_RAW_MULTI_LINE("dns", sconfig->dns, sconfig->dns_size); - if (sconfig->dns_size == 0) { + READ_RAW_MULTI_LINE("dns", msg->dns, msg->n_dns); + if (msg->n_dns == 0) { /* try aliases */ - READ_RAW_MULTI_LINE("ipv6-dns", sconfig->dns, sconfig->dns_size); - READ_RAW_MULTI_LINE("ipv4-dns", sconfig->dns, sconfig->dns_size); + READ_RAW_MULTI_LINE("ipv6-dns", msg->dns, msg->n_dns); + READ_RAW_MULTI_LINE("ipv4-dns", msg->dns, msg->n_dns); } - READ_RAW_MULTI_LINE("nbns", sconfig->nbns, sconfig->nbns_size); - if (sconfig->nbns_size == 0) { + READ_RAW_MULTI_LINE("nbns", msg->nbns, msg->n_nbns); + if (msg->n_nbns == 0) { /* try aliases */ - READ_RAW_MULTI_LINE("ipv6-nbns", sconfig->nbns, sconfig->nbns_size); - READ_RAW_MULTI_LINE("ipv4-nbns", sconfig->nbns, sconfig->nbns_size); + READ_RAW_MULTI_LINE("ipv6-nbns", msg->nbns, msg->n_nbns); + READ_RAW_MULTI_LINE("ipv4-nbns", msg->nbns, msg->n_nbns); } - READ_RAW_STRING("cgroup", sconfig->cgroup); - READ_RAW_STRING("ipv4-network", sconfig->ipv4_network); - READ_RAW_STRING("ipv6-network", sconfig->ipv6_network); - READ_RAW_STRING("ipv4-netmask", sconfig->ipv4_netmask); + READ_RAW_STRING("cgroup", msg->cgroup); + READ_RAW_STRING("ipv4-network", msg->ipv4_net); + READ_RAW_STRING("ipv6-network", msg->ipv6_net); + READ_RAW_STRING("ipv4-netmask", msg->ipv4_netmask); - READ_RAW_NUMERIC("ipv6-prefix", prefix); - if (prefix > 0) { - sconfig->ipv6_netmask = ipv6_prefix_to_mask(proc, prefix); - sconfig->ipv6_prefix = prefix; - - if (sconfig->ipv6_netmask == NULL) { + READ_RAW_NUMERIC("ipv6-prefix", msg->ipv6_prefix, msg->has_ipv6_prefix); + if (msg->has_ipv6_prefix != 0) { + if (valid_ipv6_prefix(msg->ipv6_prefix) == 0) { syslog(LOG_ERR, "unknown ipv6-prefix '%u' in %s", prefix, file); } } - READ_RAW_NUMERIC("rx-data-per-sec", sconfig->rx_per_sec); - READ_RAW_NUMERIC("tx-data-per-sec", sconfig->tx_per_sec); - sconfig->rx_per_sec /= 1000; /* in kb */ - sconfig->tx_per_sec /= 1000; /* in kb */ + READ_RAW_NUMERIC("rx-data-per-sec", msg->rx_per_sec, msg->has_rx_per_sec); + READ_RAW_NUMERIC("tx-data-per-sec", msg->tx_per_sec, msg->has_tx_per_sec); + msg->rx_per_sec /= 1000; /* in kb */ + msg->tx_per_sec /= 1000; /* in kb */ /* net-priority will contain the actual priority + 1, * to allow having zero as uninitialized. */ - READ_RAW_PRIO_TOS("net-priority", sconfig->net_priority); + READ_RAW_PRIO_TOS("net-priority", msg->net_priority, msg->has_net_priority); - READ_RAW_STRING("user-profile", sconfig->xml_config_file); + READ_RAW_STRING("user-profile", msg->xml_config_file); optionUnloadNested(pov); return 0; } -static int read_sup_config_file(struct cfg_st *global_config, struct proc_st *proc, - const char *file, const char *fallback, const char *type) +static int read_sup_config_file(struct cfg_st *global_config, + SecAuthSessionReplyMsg *msg, void *pool, + const char *file, const char *fallback, const char *type) { int ret; @@ -232,44 +237,41 @@ static int read_sup_config_file(struct cfg_st *global_config, struct proc_st *pr syslog(LOG_DEBUG, "Loading %s configuration '%s'", type, file); - ret = parse_group_cfg_file(global_config, proc, file); + ret = parse_group_cfg_file(global_config, msg, pool, file); if (ret < 0) return ERR_READ_CONFIG; } else { if (fallback != NULL) { syslog(LOG_DEBUG, "Loading default %s configuration '%s'", type, fallback); - ret = parse_group_cfg_file(global_config, proc, fallback); + ret = parse_group_cfg_file(global_config, msg, pool, fallback); if (ret < 0) return ERR_READ_CONFIG; - } else { - syslog(LOG_DEBUG, "No %s configuration for '%s'", type, - proc->username); } } return 0; } -static int get_sup_config(struct cfg_st *global_config, struct proc_st *proc) +static int get_sup_config(struct cfg_st *cfg, client_entry_st *entry, + SecAuthSessionReplyMsg *msg, void *pool) { char file[_POSIX_PATH_MAX]; int ret; - if (global_config->per_group_dir != NULL && proc->groupname[0] != 0) { - snprintf(file, sizeof(file), "%s/%s", global_config->per_group_dir, - proc->groupname); + if (cfg->per_group_dir != NULL && entry->groupname[0] != 0) { + snprintf(file, sizeof(file), "%s/%s", cfg->per_group_dir, + entry->groupname); - ret = read_sup_config_file(global_config, proc, file, global_config->default_group_conf, "group"); + ret = read_sup_config_file(cfg, msg, pool, file, cfg->default_group_conf, "group"); if (ret < 0) return ret; } - if (global_config->per_user_dir != NULL) { - snprintf(file, sizeof(file), "%s/%s", global_config->per_user_dir, - proc->username); - - ret = read_sup_config_file(global_config, proc, file, global_config->default_user_conf, "user"); + if (cfg->per_user_dir != NULL) { + snprintf(file, sizeof(file), "%s/%s", cfg->per_user_dir, + entry->username); + ret = read_sup_config_file(cfg, msg, pool, file, cfg->default_user_conf, "user"); if (ret < 0) return ret; } @@ -277,41 +279,6 @@ static int get_sup_config(struct cfg_st *global_config, struct proc_st *proc) return 0; } - -static -void clear_sup_config(struct group_cfg_st* config) -{ -unsigned i; - - for(i=0;iroutes_size;i++) { - talloc_free(config->routes[i]); - } - talloc_free(config->routes); - - for(i=0;iiroutes_size;i++) { - talloc_free(config->iroutes[i]); - } - talloc_free(config->iroutes); - - for(i=0;idns_size;i++) { - talloc_free(config->dns[i]); - } - talloc_free(config->dns); - - for(i=0;inbns_size;i++) { - talloc_free(config->nbns[i]); - } - talloc_free(config->nbns); - - talloc_free(config->cgroup); - talloc_free(config->ipv4_network); - talloc_free(config->ipv6_network); - talloc_free(config->ipv4_netmask); - talloc_free(config->ipv6_netmask); - safe_memset(config, 0, sizeof(*config)); -} - struct config_mod_st file_sup_config = { .get_sup_config = get_sup_config, - .clear_sup_config = clear_sup_config, }; diff --git a/src/sup-config/file.h b/src/sup-config/file.h index fb37ca53..cb1f5c77 100644 --- a/src/sup-config/file.h +++ b/src/sup-config/file.h @@ -21,7 +21,7 @@ #ifndef SUP_CONFIG_FILE_H #define SUP_CONFIG_FILE_H -#include +#include extern struct config_mod_st file_sup_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 03721e12..5d134e39 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -76,6 +76,7 @@ extern int syslog_open; #define AUTH_TYPE_PLAIN (1<<2 | AUTH_TYPE_USERNAME_PASS) #define AUTH_TYPE_CERTIFICATE (1<<3) #define AUTH_TYPE_CERTIFICATE_OPT (1<<4|AUTH_TYPE_CERTIFICATE) +#define AUTH_TYPE_RADIUS (1<<5 | AUTH_TYPE_USERNAME_PASS) #define ERR_SUCCESS 0 #define ERR_BAD_COMMAND -2 @@ -122,6 +123,7 @@ typedef enum { SM_CMD_AUTH_SESSION_OPEN, SM_CMD_AUTH_SESSION_CLOSE, SM_CMD_AUTH_SESSION_REPLY, + SM_CMD_CLI_STATS, } cmd_request_t; #define MAX_IP_STR 46 @@ -145,8 +147,10 @@ struct group_cfg_st { char *ipv6_network; unsigned ipv6_prefix; char *ipv4_netmask; - char *ipv6_netmask; - + + char *explicit_ipv4; + char *explicit_ipv6; + char *cgroup; char *xml_config_file; @@ -166,7 +170,6 @@ struct vpn_st { char *ipv4_network; char *ipv4; char *ipv4_local; /* local IPv4 address */ - char *ipv6_netmask; char *ipv6_network; unsigned ipv6_prefix; @@ -190,6 +193,8 @@ struct cfg_st { unsigned int udp_port; unsigned int is_dyndns; char* unix_conn_file; + unsigned int sup_config_type; /* one of SUP_CONFIG_ */ + unsigned int stats_report_time; char *pin_file; char *srk_pin_file; @@ -204,7 +209,6 @@ struct cfg_st { char *cert_user_oid; /* The OID that will be used to extract the username */ char *cert_group_oid; /* The OID that will be used to extract the groupname */ unsigned int auth_types; /* or'ed sequence of AUTH_TYPE */ - unsigned session_control; /* whether to use the session control part of authentication (PAM) */ char *auth_additional; /* the additional string specified in the auth methode */ gnutls_certificate_request_t cert_req; char *priorities; diff --git a/src/worker-auth.c b/src/worker-auth.c index 4351e3d1..d644b61e 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -548,12 +548,6 @@ static int recv_cookie_auth_reply(worker_st * ws) talloc_strdup(ws, msg->ipv4_netmask); } - if (msg->ipv6_netmask != NULL) { - talloc_free(ws->config->network.ipv6_netmask); - ws->config->network.ipv6_netmask = - talloc_strdup(ws, msg->ipv6_netmask); - } - if (msg->ipv4_network != NULL) { talloc_free(ws->config->network.ipv4_network); ws->config->network.ipv4_network = @@ -647,7 +641,7 @@ static int recv_cookie_auth_reply(worker_st * ws) } /* returns the fd */ -static int connect_to_secmod(worker_st * ws) +int connect_to_secmod(worker_st * ws) { int sd, ret, e; @@ -673,16 +667,6 @@ static int connect_to_secmod(worker_st * ws) return sd; } -static -int send_msg_to_secmod(worker_st * ws, int sd, uint8_t cmd, - const void *msg, pack_size_func get_size, pack_func pack) -{ - oclog(ws, LOG_DEBUG, "sending message '%s' to secmod", - cmd_request_to_str(cmd)); - - return send_msg(ws, sd, cmd, msg, get_size, pack); -} - static int recv_auth_reply(worker_st * ws, int sd, char *txt, size_t max_txt_size) { diff --git a/src/worker-misc.c b/src/worker-misc.c index d520a28c..73085ac9 100644 --- a/src/worker-misc.c +++ b/src/worker-misc.c @@ -227,7 +227,6 @@ int complete_vpn_info(worker_st * ws, struct vpn_st *vinfo) vinfo->ipv6_network = ws->config->network.ipv6_network; vinfo->ipv4_netmask = ws->config->network.ipv4_netmask; - vinfo->ipv6_netmask = ws->config->network.ipv6_netmask; vinfo->ipv6_prefix = ws->config->network.ipv6_prefix; if (ws->config->network.mtu != 0) { diff --git a/src/worker-vpn.c b/src/worker-vpn.c index 4cb5d99b..7fc1c5a8 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -992,6 +992,34 @@ int periodic_check(worker_st * ws, unsigned mtu_overhead, time_t now, } + if (ws->config->stats_report_time > 0 && + now - ws->last_stats_msg >= ws->config->stats_report_time && + ws->sid_set) { + CliStatsMsg msg = CLI_STATS_MSG__INIT; + int sd; + + ws->last_stats_msg = now; + + sd = connect_to_secmod(ws); + if (sd >= 0) { + msg.bytes_in = ws->tun_bytes_in; + msg.bytes_out = ws->tun_bytes_out; + msg.sid.len = sizeof(ws->sid); + msg.sid.data = ws->sid; + msg.has_sid = 1; + + send_msg_to_secmod(ws, sd, SM_CMD_CLI_STATS, &msg, + (pack_size_func)cli_stats_msg__get_packed_size, + (pack_func) cli_stats_msg__pack); + close(sd); + + oclog(ws, LOG_DEBUG, + "sending periodic stats (in: %lu, out: %lu) to sec-mod", + (unsigned long)msg.bytes_in, + (unsigned long)msg.bytes_out); + } + } + /* check DPD. Otherwise exit */ if (ws->udp_state == UP_ACTIVE && now - ws->last_msg_udp > DPD_TRIES * dpd && dpd > 0) { diff --git a/src/worker.h b/src/worker.h index f1f0c507..578eacd4 100644 --- a/src/worker.h +++ b/src/worker.h @@ -172,6 +172,9 @@ typedef struct worker_st { time_t last_tls_rehandshake; time_t last_dtls_rehandshake; + /* the time the last stats message was sent */ + time_t last_stats_msg; + /* for mtu trials */ unsigned last_good_mtu; unsigned last_bad_mtu; @@ -274,6 +277,17 @@ int handle_worker_commands(struct worker_st *ws); int disable_system_calls(struct worker_st *ws); void ocsigaltstack(struct worker_st *ws); +int connect_to_secmod(worker_st * ws); +inline static +int send_msg_to_secmod(worker_st * ws, int sd, uint8_t cmd, + const void *msg, pack_size_func get_size, pack_func pack) +{ + oclog(ws, LOG_DEBUG, "sending message '%s' to secmod", + cmd_request_to_str(cmd)); + + return send_msg(ws, sd, cmd, msg, get_size, pack); +} + inline static int send_msg_to_main(worker_st *ws, uint8_t cmd, const void* msg, pack_size_func get_size, pack_func pack)