From 9791a9c53bac18b903663aae776de0cb76b3663a Mon Sep 17 00:00:00 2001 From: yasuoka Date: Thu, 11 Jul 2024 14:05:59 +0000 Subject: [PATCH] Add Dynamic Authorization Extensions (DAE) for RADIUS server feature to npppd. It can be configured now so that it accepts disconnect requests and this works together with radiusd_ipcp(8) module. Also "nas-id" becomes configurable. --- usr.sbin/npppd/npppd/npppd.c | 12 +- usr.sbin/npppd/npppd/npppd.conf.5 | 32 ++- usr.sbin/npppd/npppd/npppd.h | 148 +++++++------ usr.sbin/npppd/npppd/npppd_config.c | 7 +- usr.sbin/npppd/npppd/npppd_local.h | 10 +- usr.sbin/npppd/npppd/npppd_radius.c | 316 +++++++++++++++++++++++++++- usr.sbin/npppd/npppd/npppd_radius.h | 30 ++- usr.sbin/npppd/npppd/parse.y | 91 +++++++- 8 files changed, 566 insertions(+), 80 deletions(-) diff --git a/usr.sbin/npppd/npppd/npppd.c b/usr.sbin/npppd/npppd/npppd.c index e23f8308d06..295cb9ea33f 100644 --- a/usr.sbin/npppd/npppd/npppd.c +++ b/usr.sbin/npppd/npppd/npppd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: npppd.c,v 1.53 2022/07/01 09:57:24 mvs Exp $ */ +/* $OpenBSD: npppd.c,v 1.54 2024/07/11 14:05:59 yasuoka Exp $ */ /*- * Copyright (c) 2005-2008,2009 Internet Initiative Japan Inc. @@ -29,7 +29,7 @@ * Next pppd(nppd). This file provides a npppd daemon process and operations * for npppd instance. * @author Yasuoka Masahiko - * $Id: npppd.c,v 1.53 2022/07/01 09:57:24 mvs Exp $ + * $Id: npppd.c,v 1.54 2024/07/11 14:05:59 yasuoka Exp $ */ #include "version.h" #include /* ALIGNED_POINTER */ @@ -101,7 +101,6 @@ static void npppd_timer(int, short, void *); static void npppd_auth_finalizer_periodic(npppd *); static int rd2slist_walk (struct radish *, void *); static int rd2slist (struct radish_head *, slist *); -static slist *npppd_get_ppp_by_user (npppd *, const char *); static int npppd_get_all_users (npppd *, slist *); static struct ipcpstat *npppd_get_ipcp_stat(struct ipcpstat_head *, const char *); @@ -255,6 +254,7 @@ npppd_init(npppd *_this, const char *config_file) _this->pid = getpid(); slist_init(&_this->realms); npppd_conf_init(&_this->conf); + TAILQ_INIT(&_this->raddae_listens); log_printf(LOG_NOTICE, "Starting npppd pid=%u version=%s", _this->pid, VERSION); @@ -444,6 +444,10 @@ npppd_stop(npppd *_this) _this->finalizing = 1; npppd_reset_timer(_this); + +#ifdef USE_NPPPD_RADIUS + npppd_radius_dae_fini(_this); +#endif } static void @@ -763,7 +767,7 @@ npppd_get_ppp_by_ip(npppd *_this, struct in_addr ipaddr) * @return {@link slist} that contains the {@link npppd_ppp} instances. * NULL may be returned if no instance has been found. */ -static slist * +slist * npppd_get_ppp_by_user(npppd *_this, const char *username) { hash_link *hl; diff --git a/usr.sbin/npppd/npppd/npppd.conf.5 b/usr.sbin/npppd/npppd/npppd.conf.5 index ab2ed0b5d60..559ce22f9c6 100644 --- a/usr.sbin/npppd/npppd/npppd.conf.5 +++ b/usr.sbin/npppd/npppd/npppd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: npppd.conf.5,v 1.34 2024/07/01 14:56:19 jmc Exp $ +.\" $OpenBSD: npppd.conf.5,v 1.35 2024/07/11 14:05:59 yasuoka Exp $ .\" .\" Copyright (c) 2012 YASUOKA Masahiko .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 1 2024 $ +.Dd $Mdocdate: July 11 2024 $ .Dt NPPPD.CONF 5 .Os .Sh NAME @@ -41,6 +41,8 @@ Interface settings. Authentication settings. .It Sy Bind Bind settings. +.It Sy RADIUS +RADIUS settings. .El .Sh GLOBAL The global options are as follows: @@ -664,6 +666,32 @@ settings so that they are used together. .Pp .Ic bind tunnel from Ar tunnel Ic authenticated by Ar authentication .Ic to Ar ifname +.Sh RADIUS +.Ic radius +configures the RADIUS features. +The supported options are as follows: +.Bl -tag -width Ds +.It Ic radius nas-id Ar identifier +Specify the +.Ar identifier +that is noticed to the RADIUS peers in the NAS-Identifier attribute. +.It Ic radius dae listen on Ar address Oo port Ar number Oc +Enable the Dynamic Authorization Extensions for RADIUS +.Po DAE, RFC 5176 Pc +server. +Specify the local +.Ar address +.Xr npppd 8 +should listen on for the DAE requests. +Optionally specify a port +.Ar number , +the default port number is 3799. +.It Ic radius dae client Ar address Ic secret Ar secret +Specify +.Ar address +for a DAE client and +.Ar secret . +.El .Sh EXAMPLES A very simple configuration example is below: .Bd -literal -offset indent diff --git a/usr.sbin/npppd/npppd/npppd.h b/usr.sbin/npppd/npppd/npppd.h index 3cf9b2bcbcc..1d33ce4bd5e 100644 --- a/usr.sbin/npppd/npppd/npppd.h +++ b/usr.sbin/npppd/npppd/npppd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: npppd.h,v 1.20 2024/07/01 07:09:07 yasuoka Exp $ */ +/* $OpenBSD: npppd.h,v 1.21 2024/07/11 14:05:59 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. @@ -43,6 +43,7 @@ #include "l2tp_conf.h" #include "pptp_conf.h" #include "pppoe_conf.h" +#include "slist.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) @@ -170,6 +171,25 @@ struct authconf { } data; }; +struct radclientconf { + union { + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } addr; + TAILQ_ENTRY(radclientconf) entry; + char secret[]; +}; +TAILQ_HEAD(radclientconfs,radclientconf); + +struct radlistenconf { + union { + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } addr; + TAILQ_ENTRY(radlistenconf) entry; +}; +TAILQ_HEAD(radlistenconfs,radlistenconf); + struct ipcpconf { TAILQ_ENTRY(ipcpconf) entry; char name[NPPPD_GENERIC_NAME_LEN]; @@ -207,6 +227,9 @@ struct npppd_conf { TAILQ_HEAD(ipcpconfs, ipcpconf) ipcpconfs; TAILQ_HEAD(ifaces, iface) ifaces; TAILQ_HEAD(confbinds, confbind) confbinds; + struct radclientconfs raddaeclientconfs; + struct radlistenconfs raddaelistenconfs; + char nas_id[NPPPD_GENERIC_NAME_LEN]; struct l2tp_confs l2tp_confs; struct pptp_confs pptp_confs; struct pppoe_confs pppoe_confs; @@ -266,65 +289,70 @@ TAILQ_HEAD(ctl_conn_list, ctl_conn); extern struct ctl_conn_list ctl_conns; __BEGIN_DECLS -npppd *npppd_get_npppd (void); -int npppd_init (npppd *, const char *); -void npppd_start (npppd *); -void npppd_stop (npppd *); -void npppd_fini (npppd *); -int npppd_reset_routing_table (npppd *, int); -int npppd_get_user_password (npppd *, npppd_ppp *, const char *, char *, int *); -struct in_addr *npppd_get_user_framed_ip_address (npppd *, npppd_ppp *, const char *); -int npppd_check_calling_number (npppd *, npppd_ppp *); -npppd_ppp *npppd_get_ppp_by_ip (npppd *, struct in_addr); -npppd_ppp *npppd_get_ppp_by_id (npppd *, u_int); -int npppd_check_user_max_session (npppd *, npppd_ppp *); -void npppd_network_output (npppd *, npppd_ppp *, int, u_char *, int); -int npppd_ppp_pipex_enable (npppd *, npppd_ppp *); -int npppd_ppp_pipex_disable (npppd *, npppd_ppp *); -int npppd_prepare_ip (npppd *, npppd_ppp *); -void npppd_release_ip (npppd *, npppd_ppp *); -void npppd_set_ip_enabled (npppd *, npppd_ppp *, int); -int npppd_assign_ip_addr (npppd *, npppd_ppp *, uint32_t); -int npppd_set_radish (npppd *, void *); -int npppd_ppp_bind_realm (npppd *, npppd_ppp *, const char *, int); -int npppd_ppp_is_realm_local (npppd *, npppd_ppp *); -int npppd_ppp_is_realm_radius (npppd *, npppd_ppp *); -int npppd_ppp_is_realm_ready (npppd *, npppd_ppp *); -const char *npppd_ppp_get_realm_name (npppd *, npppd_ppp *); -const char *npppd_ppp_get_iface_name (npppd *, npppd_ppp *); -int npppd_ppp_iface_is_ready (npppd *, npppd_ppp *); -int npppd_ppp_bind_iface (npppd *, npppd_ppp *); -void npppd_ppp_unbind_iface (npppd *, npppd_ppp *); -void *npppd_get_radius_auth_setting (npppd *, npppd_ppp *); -int sockaddr_npppd_match (void *, void *); -const char *npppd_ppp_get_username_for_auth (npppd *, npppd_ppp *, const char *, char *); -const char *npppd_ppp_tunnel_protocol_name (npppd *, npppd_ppp *); -const char *npppd_tunnel_protocol_name (int); -struct tunnconf *npppd_get_tunnconf (npppd *, const char *); -int npppd_reload_config (npppd *); -int npppd_modules_reload (npppd *); -int npppd_ifaces_load_config (npppd *); - -int npppd_conf_parse (struct npppd_conf *, const char *); -void npppd_conf_init (struct npppd_conf *); -void npppd_conf_fini (struct npppd_conf *); -int npppd_config_check (const char *); -void npppd_on_ppp_start (npppd *, npppd_ppp *); -void npppd_on_ppp_stop (npppd *, npppd_ppp *); -void imsg_event_add(struct imsgev *); - -int control_init (struct control_sock *); -int control_listen (struct control_sock *); -void control_cleanup (struct control_sock *); -struct npppd_ctl *npppd_ctl_create (npppd *); -void npppd_ctl_destroy (struct npppd_ctl *); -int npppd_ctl_who (struct npppd_ctl *); -int npppd_ctl_monitor (struct npppd_ctl *); -int npppd_ctl_who_and_monitor (struct npppd_ctl *); -int npppd_ctl_add_started_ppp_id (struct npppd_ctl *, uint32_t); -int npppd_ctl_add_stopped_ppp (struct npppd_ctl *, npppd_ppp *); -int npppd_ctl_imsg_compose (struct npppd_ctl *, struct imsgbuf *); -int npppd_ctl_disconnect (struct npppd_ctl *, u_int *, int); +npppd *npppd_get_npppd(void); +int npppd_init(npppd *, const char *); +void npppd_start(npppd *); +void npppd_stop(npppd *); +void npppd_fini(npppd *); +int npppd_reset_routing_table(npppd *, int); +int npppd_get_user_password(npppd *, npppd_ppp *, const char *, + char *, int *); +struct in_addr *npppd_get_user_framed_ip_address(npppd *, npppd_ppp *, + const char *); +int npppd_check_calling_number(npppd *, npppd_ppp *); +npppd_ppp *npppd_get_ppp_by_ip(npppd *, struct in_addr); +npppd_ppp *npppd_get_ppp_by_id(npppd *, u_int); +slist *npppd_get_ppp_by_user(npppd *, const char *); +int npppd_check_user_max_session(npppd *, npppd_ppp *); +void npppd_network_output(npppd *, npppd_ppp *, int, u_char *, int); +int npppd_ppp_pipex_enable(npppd *, npppd_ppp *); +int npppd_ppp_pipex_disable(npppd *, npppd_ppp *); +int npppd_prepare_ip(npppd *, npppd_ppp *); +void npppd_release_ip(npppd *, npppd_ppp *); +void npppd_set_ip_enabled(npppd *, npppd_ppp *, int); +int npppd_assign_ip_addr(npppd *, npppd_ppp *, uint32_t); +int npppd_set_radish(npppd *, void *); +int npppd_ppp_bind_realm(npppd *, npppd_ppp *, const char *, int); +int npppd_ppp_is_realm_local(npppd *, npppd_ppp *); +int npppd_ppp_is_realm_radius(npppd *, npppd_ppp *); +int npppd_ppp_is_realm_ready(npppd *, npppd_ppp *); +const char *npppd_ppp_get_realm_name(npppd *, npppd_ppp *); +const char *npppd_ppp_get_iface_name(npppd *, npppd_ppp *); +int npppd_ppp_iface_is_ready(npppd *, npppd_ppp *); +int npppd_ppp_bind_iface(npppd *, npppd_ppp *); +void npppd_ppp_unbind_iface(npppd *, npppd_ppp *); +void *npppd_get_radius_auth_setting(npppd *, npppd_ppp *); +int sockaddr_npppd_match(void *, void *); +const char *npppd_ppp_get_username_for_auth(npppd *, npppd_ppp *, + const char *, char *); +const char *npppd_ppp_tunnel_protocol_name(npppd *, npppd_ppp *); +const char *npppd_tunnel_protocol_name(int); +struct tunnconf *npppd_get_tunnconf(npppd *, const char *); +int npppd_reload_config(npppd *); +int npppd_modules_reload(npppd *); +int npppd_ifaces_load_config(npppd *); + +int npppd_conf_parse(struct npppd_conf *, const char *); +void npppd_conf_init(struct npppd_conf *); +void npppd_conf_fini(struct npppd_conf *); +int npppd_config_check(const char *); +void npppd_on_ppp_start(npppd *, npppd_ppp *); +void npppd_on_ppp_stop(npppd *, npppd_ppp *); +void imsg_event_add(struct imsgev *); + +int control_init(struct control_sock *); +int control_listen(struct control_sock *); +void control_cleanup(struct control_sock *); +struct npppd_ctl + *npppd_ctl_create(npppd *); +void npppd_ctl_destroy(struct npppd_ctl *); +int npppd_ctl_who(struct npppd_ctl *); +int npppd_ctl_monitor(struct npppd_ctl *); +int npppd_ctl_who_and_monitor(struct npppd_ctl *); +int npppd_ctl_add_started_ppp_id(struct npppd_ctl *, uint32_t); +int npppd_ctl_add_stopped_ppp(struct npppd_ctl *, npppd_ppp *); +int npppd_ctl_imsg_compose(struct npppd_ctl *, struct imsgbuf *); +int npppd_ctl_disconnect(struct npppd_ctl *, u_int *, int); __END_DECLS diff --git a/usr.sbin/npppd/npppd/npppd_config.c b/usr.sbin/npppd/npppd/npppd_config.c index d5a93fbcc2e..614ac71dbfc 100644 --- a/usr.sbin/npppd/npppd/npppd_config.c +++ b/usr.sbin/npppd/npppd/npppd_config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: npppd_config.c,v 1.14 2015/01/19 01:48:59 deraadt Exp $ */ +/* $OpenBSD: npppd_config.c,v 1.15 2024/07/11 14:05:59 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. @@ -25,7 +25,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -/* $Id: npppd_config.c,v 1.14 2015/01/19 01:48:59 deraadt Exp $ */ +/* $Id: npppd_config.c,v 1.15 2024/07/11 14:05:59 yasuoka Exp $ */ /*@file * This file provides functions which operates configuration and so on. */ @@ -131,6 +131,9 @@ npppd_modules_reload(npppd *_this) #ifdef USE_NPPPD_PPPOE rval |= pppoed_reload(&_this->pppoed, &_this->conf.pppoe_confs); #endif +#ifdef USE_NPPPD_RADIUS + npppd_radius_dae_init(_this); +#endif return rval; } diff --git a/usr.sbin/npppd/npppd/npppd_local.h b/usr.sbin/npppd/npppd/npppd_local.h index 37205b65f6a..d3e692f510d 100644 --- a/usr.sbin/npppd/npppd/npppd_local.h +++ b/usr.sbin/npppd/npppd/npppd_local.h @@ -1,4 +1,4 @@ -/* $OpenBSD: npppd_local.h,v 1.18 2024/02/26 08:29:37 yasuoka Exp $ */ +/* $OpenBSD: npppd_local.h,v 1.19 2024/07/11 14:05:59 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. @@ -73,6 +73,10 @@ #include "npppd_pool.h" #include "npppd_ctl.h" +#ifdef USE_NPPPD_RADIUS +#include "npppd_radius.h" +#endif + /** structure of pool */ struct _npppd_pool { /** base of npppd structure */ @@ -169,6 +173,10 @@ struct _npppd { struct control_sock ctl_sock; +#ifdef USE_NPPPD_RADIUS + struct npppd_radius_dae_listens raddae_listens; +#endif + u_int /** whether finalizing or not */ finalizing:1, /** whether finalize completed or not */ diff --git a/usr.sbin/npppd/npppd/npppd_radius.c b/usr.sbin/npppd/npppd/npppd_radius.c index c9d030e2b45..8503bd41d77 100644 --- a/usr.sbin/npppd/npppd/npppd_radius.c +++ b/usr.sbin/npppd/npppd/npppd_radius.c @@ -1,4 +1,4 @@ -/* $Id: npppd_radius.c,v 1.11 2024/07/01 07:09:07 yasuoka Exp $ */ +/* $Id: npppd_radius.c,v 1.12 2024/07/11 14:05:59 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. * All rights reserved. @@ -45,18 +45,22 @@ #include #include #include +#include +#include +#include #include #include "radius_req.h" #include "npppd_local.h" #include "npppd_radius.h" +#include "net_utils.h" #ifdef NPPPD_RADIUS_DEBUG #define NPPPD_RADIUS_DBG(x) ppp_log x #define NPPPD_RADIUS_ASSERT(x) ASSERT(x) #else -#define NPPPD_RADIUS_DBG(x) +#define NPPPD_RADIUS_DBG(x) #define NPPPD_RADIUS_ASSERT(x) #endif @@ -72,7 +76,7 @@ static void npppd_ppp_radius_acct_reqcb(void *, RADIUS_PACKET *, int, RADIUS_REQ /** * Retribute Framed-IP-Address and Framed-IP-Netmask attribute of from * the given RADIUS packet and set them as the fields of ppp context. - */ + */ void ppp_process_radius_attrs(npppd_ppp *_this, RADIUS_PACKET *pkt) { @@ -269,7 +273,7 @@ radius_acct_request(npppd *pppd, npppd_ppp *ppp, int stop) /* npppd has no physical / virtual ports in design. */ /* RFC 2865 5.32. NAS-Identifier */ - ATTR_STR(RADIUS_TYPE_NAS_IDENTIFIER, "npppd"); + ATTR_STR(RADIUS_TYPE_NAS_IDENTIFIER, pppd->conf.nas_id); /* RFC 2865 5.31. Calling-Station-Id */ if (ppp->calling_number[0] != '\0') @@ -398,7 +402,7 @@ radius_acct_on(npppd *pppd, radius_req_setting *rad_setting) /* RFC 2866 5.1. Acct-Status-Type */ ATTR_INT32(RADIUS_TYPE_ACCT_STATUS_TYPE, RADIUS_ACCT_STATUS_TYPE_ACCT_ON); /* RFC 2865 5.32. NAS-Identifier */ - ATTR_STR(RADIUS_TYPE_NAS_IDENTIFIER, "npppd"); + ATTR_STR(RADIUS_TYPE_NAS_IDENTIFIER, pppd->conf.nas_id); /* Send the request */ radius_request(radctx, radpkt); @@ -562,3 +566,305 @@ ppp_set_radius_attrs_for_authreq(npppd_ppp *_this, fail: return 1; } + +/*********************************************************************** + * Dynamic Authorization Extensions for RADIUS + ***********************************************************************/ +static int npppd_radius_dae_listen_start(struct npppd_radius_dae_listen *); +static void npppd_radius_dae_on_event(int, short, void *); +static void npppd_radius_dae_listen_stop(struct npppd_radius_dae_listen *); + +void +npppd_radius_dae_init(npppd *_this) +{ + struct npppd_radius_dae_listens listens; + struct npppd_radius_dae_listen *listen, *listent; + struct radlistenconf *listenconf; + + TAILQ_INIT(&listens); + + TAILQ_FOREACH(listenconf, &_this->conf.raddaelistenconfs, entry) { + TAILQ_FOREACH_SAFE(listen, &_this->raddae_listens, entry, + listent) { + if ((listen->addr.sin4.sin_family == AF_INET && + listenconf->addr.sin4.sin_family == AF_INET && + memcmp(&listen->addr.sin4, &listenconf->addr.sin4, + sizeof(struct sockaddr_in)) == 0) || + (listen->addr.sin6.sin6_family == AF_INET6 && + listenconf->addr.sin6.sin6_family == AF_INET6 && + memcmp(&listen->addr.sin6, &listenconf->addr.sin6, + sizeof(struct sockaddr_in6)) == 0)) + break; + } + if (listen != NULL) + /* keep using this */ + TAILQ_REMOVE(&_this->raddae_listens, listen, entry); + else { + if ((listen = calloc(1, sizeof(*listen))) == NULL) { + log_printf(LOG_ERR, "%s: calloc failed: %m", + __func__); + goto fail; + } + listen->pppd = _this; + listen->sock = -1; + if (listenconf->addr.sin4.sin_family == AF_INET) + listen->addr.sin4 = listenconf->addr.sin4; + else + listen->addr.sin6 = listenconf->addr.sin6; + } + TAILQ_INSERT_TAIL(&listens, listen, entry); + } + + /* listen on the new addresses */ + TAILQ_FOREACH(listen, &listens, entry) { + if (listen->sock == -1) + npppd_radius_dae_listen_start(listen); + } + + /* stop listening on the old addresses */ + TAILQ_FOREACH_SAFE(listen, &_this->raddae_listens, entry, listent) { + TAILQ_REMOVE(&_this->raddae_listens, listen, entry); + npppd_radius_dae_listen_stop(listen); + free(listen); + } + fail: + TAILQ_CONCAT(&_this->raddae_listens, &listens, entry); + + return; +} + +void +npppd_radius_dae_fini(npppd *_this) +{ + struct npppd_radius_dae_listen *listen, *listent; + + TAILQ_FOREACH_SAFE(listen, &_this->raddae_listens, entry, listent) { + TAILQ_REMOVE(&_this->raddae_listens, listen, entry); + npppd_radius_dae_listen_stop(listen); + free(listen); + } +} + +int +npppd_radius_dae_listen_start(struct npppd_radius_dae_listen *listen) +{ + char buf[80]; + int sock = -1, on = 1; + + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + log_printf(LOG_ERR, "%s: socket(): %m", __func__); + goto on_error; + } + on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + log_printf(LOG_WARNING, "%s: setsockopt(,,SO_REUSEADDR): %m", + __func__); + goto on_error; + } + if (bind(sock, (struct sockaddr *)&listen->addr, + listen->addr.sin4.sin_len) == -1) { + log_printf(LOG_ERR, "%s: bind(): %m", __func__); + goto on_error; + } + + listen->sock = sock; + event_set(&listen->evsock, listen->sock, EV_READ | EV_PERSIST, + npppd_radius_dae_on_event, listen); + event_add(&listen->evsock, NULL); + log_printf(LOG_INFO, "radius Listening %s/udp (DAE)", + addrport_tostring((struct sockaddr *)&listen->addr, + listen->addr.sin4.sin_len, buf, sizeof(buf))); + + return (0); + on_error: + if (sock >= 0) + close(sock); + + return (-1); +} + +void +npppd_radius_dae_on_event(int fd, short ev, void *ctx) +{ + char buf[80], attr[256], username[256]; + char *endp; + const char *reason, *nakcause = NULL; + struct npppd_radius_dae_listen *listen = ctx; + struct radclientconf *client; + npppd *_this = listen->pppd; + RADIUS_PACKET *req = NULL, *res = NULL; + struct sockaddr_storage ss; + socklen_t sslen; + unsigned long long ppp_id; + int code, n = 0; + uint32_t cause = 0; + struct in_addr ina; + slist *users; + npppd_ppp *ppp; + + reason = "disconnect requested"; + sslen = sizeof(ss); + req = radius_recvfrom(listen->sock, 0, (struct sockaddr *)&ss, &sslen); + if (req == NULL) { + log_printf(LOG_WARNING, "%s: receiving a RADIUS message " + "failed: %m", __func__); + return; + } + TAILQ_FOREACH(client, &_this->conf.raddaeclientconfs, entry) { + if (ss.ss_family == AF_INET && + ((struct sockaddr_in *)&ss)->sin_addr.s_addr == + client->addr.sin4.sin_addr.s_addr) + break; + else if (ss.ss_family == AF_INET6 && + IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)&ss)->sin6_addr, + &client->addr.sin6.sin6_addr)) + break; + } + + if (client == NULL) { + log_printf(LOG_WARNING, "radius received a RADIUS message from " + "%s: unknown client", addrport_tostring( + (struct sockaddr *)&ss, ss.ss_len, buf, sizeof(buf))); + goto out; + } + + if (radius_check_accounting_request_authenticator(req, + client->secret) != 0) { + log_printf(LOG_WARNING, "radius received an invalid RADIUS " + "message from %s: bad response authenticator", + addrport_tostring( + (struct sockaddr *)&ss, ss.ss_len, buf, sizeof(buf))); + goto out; + } + if ((code = radius_get_code(req)) != RADIUS_CODE_DISCONNECT_REQUEST) { + /* Code other than Disconnect-Request is not supported */ + if (code == RADIUS_CODE_COA_REQUEST) { + log_printf(LOG_INFO, "received CoA-Request from %s", + addrport_tostring( + (struct sockaddr *)&ss, ss.ss_len, buf, + sizeof(buf))); + code = RADIUS_CODE_COA_NAK; + cause = RADIUS_ERROR_CAUSE_ADMINISTRATIVELY_PROHIBITED; + goto send; + } + log_printf(LOG_WARNING, "radius received an invalid RADIUS " + "message from %s: unknown code %d", + addrport_tostring((struct sockaddr *)&ss, ss.ss_len, buf, + sizeof(buf)), code); + goto out; + } + + log_printf(LOG_INFO, "radius received Disconnect-Request from %s", + addrport_tostring((struct sockaddr *)&ss, ss.ss_len, buf, + sizeof(buf))); + + if (radius_get_string_attr(req, RADIUS_TYPE_NAS_IDENTIFIER, attr, + sizeof(attr)) == 0 && strcmp(attr, _this->conf.nas_id) != 0) { + cause = RADIUS_ERROR_CAUSE_NAS_IDENTIFICATION_MISMATCH; + nakcause = "NAS Identification is mimatch"; + goto search_done; + } + + /* prepare User-Name attribute */ + memset(&username, 0, sizeof(username)); + radius_get_string_attr(req, RADIUS_TYPE_USER_NAME, username, + sizeof(username)); + + cause = RADIUS_ERROR_CAUSE_SESSION_NOT_FOUND; + /* Our Session-Id is represented in "%08X%08x" (boot_id, ppp_id) */ + snprintf(buf, sizeof(buf), "%08X", _this->boot_id); + if (radius_get_string_attr(req, RADIUS_TYPE_ACCT_SESSION_ID, attr, + sizeof(attr)) == 0) { + ppp = NULL; + /* the client is to disconnect a session */ + if (strlen(attr) != 16 || strncmp(buf, attr, 8) != 0) { + cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE; + nakcause = "Session-Id is wrong"; + goto search_done; + } + ppp_id = strtoull(attr + 8, &endp, 16); + if (*endp != '\0' || errno == ERANGE || ppp_id == ULLONG_MAX) { + cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE; + nakcause = "Session-Id is invalid"; + goto search_done; + } + if ((ppp = npppd_get_ppp_by_id(_this, ppp_id)) == NULL) + goto search_done; + if (username[0] != '\0' && + strcmp(username, ppp->username) != 0) { + /* specified User-Name attribute is mismatched */ + cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE; + nakcause = "User-Name is mismatched"; + goto search_done; + } + ppp_stop(ppp, reason); + n++; + } else if (username[0] != '\0') { + users = npppd_get_ppp_by_user(_this, username); + if (users == NULL) + goto search_done; + memset(&ina, 0, sizeof(ina)); + radius_get_uint32_attr(req, RADIUS_TYPE_FRAMED_IP_ADDRESS, + &ina.s_addr); + slist_itr_first(users); + while ((ppp = slist_itr_next(users)) != NULL) { + if (ntohl(ina.s_addr) != 0 && + ina.s_addr != ppp->ppp_framed_ip_address.s_addr) + continue; + ppp_stop(ppp, reason); + n++; + } + } else if (radius_get_uint32_attr(req, RADIUS_TYPE_FRAMED_IP_ADDRESS, + &ina.s_addr) == 0) { + ppp = npppd_get_ppp_by_ip(_this, ina); + if (ppp != NULL) { + ppp_stop(ppp, reason); + n++; + } + } + search_done: + if (n > 0) + code = RADIUS_CODE_DISCONNECT_ACK; + else { + if (nakcause == NULL) + nakcause = "session not found"; + code = RADIUS_CODE_DISCONNECT_NAK; + } + send: + res = radius_new_response_packet(code, req); + if (res == NULL) { + log_printf(LOG_WARNING, "%s: radius_new_response_packet: %m", + __func__); + goto out; + } + if (cause != 0) + radius_put_uint32_attr(res, RADIUS_TYPE_ERROR_CAUSE, cause); + radius_set_response_authenticator(res, client->secret); + if (radius_sendto(listen->sock, res, 0, (struct sockaddr *)&ss, sslen) + == -1) + log_printf(LOG_WARNING, "%s: sendto(): %m", __func__); + log_printf(LOG_INFO, "radius send %s to %s%s%s", + (code == RADIUS_CODE_DISCONNECT_ACK)? "Disconnect-ACK" : + (code == RADIUS_CODE_DISCONNECT_NAK)? "Disconnect-NAK" : "CoA-NAK", + addrport_tostring((struct sockaddr *)&ss, ss.ss_len, buf, + sizeof(buf)), (nakcause)? ": " : "", (nakcause)? nakcause : ""); + out: + radius_delete_packet(req); + if (res != NULL) + radius_delete_packet(res); +} + +void +npppd_radius_dae_listen_stop(struct npppd_radius_dae_listen *listen) +{ + char buf[80]; + + if (listen->sock >= 0) { + log_printf(LOG_INFO, "radius Shutdown %s/udp (DAE)", + addrport_tostring((struct sockaddr *)&listen->addr, + listen->addr.sin4.sin_len, buf, sizeof(buf))); + event_del(&listen->evsock); + close(listen->sock); + listen->sock = -1; + } +} diff --git a/usr.sbin/npppd/npppd/npppd_radius.h b/usr.sbin/npppd/npppd/npppd_radius.h index a1334545e2b..a7c309bd419 100644 --- a/usr.sbin/npppd/npppd/npppd_radius.h +++ b/usr.sbin/npppd/npppd/npppd_radius.h @@ -1,15 +1,35 @@ #ifndef NPPPD_RADIUS_H #define NPPPD_RADIUS_H 1 +#include +#include +#include + +struct npppd_radius_dae_listen { + int sock; + struct event evsock; + union { + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } addr; + npppd *pppd; + TAILQ_ENTRY(npppd_radius_dae_listen) entry; +}; + +TAILQ_HEAD(npppd_radius_dae_listens, npppd_radius_dae_listen); + #ifdef __cplusplus extern "C" { #endif -void ppp_proccess_radius_framed_ip (npppd_ppp *, RADIUS_PACKET *); -int ppp_set_radius_attrs_for_authreq (npppd_ppp *, radius_req_setting *, RADIUS_PACKET *); -void npppd_ppp_radius_acct_start (npppd *, npppd_ppp *); -void npppd_ppp_radius_acct_stop (npppd *, npppd_ppp *); -void radius_acct_on(npppd *, radius_req_setting *); +void ppp_proccess_radius_framed_ip(npppd_ppp *, RADIUS_PACKET *); +int ppp_set_radius_attrs_for_authreq(npppd_ppp *, radius_req_setting *, + RADIUS_PACKET *); +void npppd_ppp_radius_acct_start(npppd *, npppd_ppp *); +void npppd_ppp_radius_acct_stop(npppd *, npppd_ppp *); +void radius_acct_on(npppd *, radius_req_setting *); +void npppd_radius_dae_init(npppd *); +void npppd_radius_dae_fini(npppd *); #ifdef __cplusplus } diff --git a/usr.sbin/npppd/npppd/parse.y b/usr.sbin/npppd/npppd/parse.y index a589c5a7047..2017fe9a7ac 100644 --- a/usr.sbin/npppd/npppd/parse.y +++ b/usr.sbin/npppd/npppd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.28 2024/07/01 07:09:07 yasuoka Exp $ */ +/* $OpenBSD: parse.y,v 1.29 2024/07/11 14:05:59 yasuoka Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,7 @@ typedef struct { %token INTERFACE ADDRESS IPCP %token BIND FROM AUTHENTICATED BY TO %token ERROR +%token DAE CLIENT NAS_ID %token STRING %token NUMBER %type yesno @@ -164,6 +166,7 @@ grammar : /* empty */ | grammar ipcp '\n' | grammar interface '\n' | grammar bind '\n' + | grammar radius '\n' | grammar error '\n' { file->errors++; } ; @@ -513,6 +516,80 @@ tunnopt : LISTEN ON addressport { curr_tunnconf->debug_dump_pktout = $2; } ; +radius : RADIUS NAS_ID STRING { + if (strlcpy(conf->nas_id, $3, sizeof(conf->nas_id)) + >= sizeof(conf->nas_id)) { + yyerror("`radius nas-id' is too long. use " + "less than %u chars.", + (unsigned)sizeof(conf->nas_id) - 1); + free($3); + YYERROR; + } + free($3); + } + | RADIUS DAE CLIENT address SECRET STRING { + struct radclientconf *client; + int secretsiz; + + secretsiz = strlen($6) + 1; + if ((client = calloc(1, offsetof(struct radclientconf, + secret[secretsiz]))) == NULL) { + yyerror("%s", strerror(errno)); + free($6); + YYERROR; + } + strlcpy(client->secret, $6, secretsiz); + + switch ($4.ss_family) { + case AF_INET: + memcpy(&client->addr, &$4, + sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(&client->addr, &$4, + sizeof(struct sockaddr_in6)); + break; + default: + yyerror("address family %d not supported", + $4.ss_family); + free($6); + YYERROR; + break; + } + TAILQ_INSERT_TAIL(&conf->raddaeclientconfs, client, + entry); + free($6); + } + | RADIUS DAE LISTEN ON addressport { + struct radlistenconf *listen; + + if (ntohs(((struct sockaddr_in *)&$5)->sin_port) == 0) + ((struct sockaddr_in *)&$5)->sin_port = htons( + RADIUS_DAE_DEFAULT_PORT); + + if ((listen = calloc(1, sizeof(*listen))) == NULL) { + yyerror("%s", strerror(errno)); + YYERROR; + } + switch ($5.ss_family) { + case AF_INET: + memcpy(&listen->addr, &$5, + sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(&listen->addr, &$5, + sizeof(struct sockaddr_in6)); + break; + default: + yyerror("address family %d not supported", + $5.ss_family); + YYERROR; + break; + } + TAILQ_INSERT_TAIL(&conf->raddaelistenconfs, listen, + entry); + } + ; tunnelproto : L2TP { $$ = NPPPD_TUNNEL_L2TP; } | PPTP { $$ = NPPPD_TUNNEL_PPTP; } @@ -1011,6 +1088,8 @@ lookup(char *s) { "ccp-timeout", CCP_TIMEOUT}, { "chap", CHAP}, { "chap-name", CHAP_NAME}, + { "client", CLIENT}, + { "dae", DAE}, { "debug-dump-pktin", DEBUG_DUMP_PKTIN}, { "debug-dump-pktout", DEBUG_DUMP_PKTOUT}, { "dns-servers", DNS_SERVERS}, @@ -1061,6 +1140,7 @@ lookup(char *s) { "mppe-key-state", MPPE_KEY_STATE}, { "mru", MRU}, { "mschapv2", MSCHAPV2}, + { "nas-id", NAS_ID}, { "nbns-servers", NBNS_SERVERS}, { "no", NO}, { "on", ON}, @@ -1429,6 +1509,9 @@ npppd_conf_init(struct npppd_conf *xconf) TAILQ_INIT(&xconf->l2tp_confs); TAILQ_INIT(&xconf->pptp_confs); TAILQ_INIT(&xconf->pppoe_confs); + TAILQ_INIT(&xconf->raddaeclientconfs); + TAILQ_INIT(&xconf->raddaelistenconfs); + strlcpy(xconf->nas_id, "npppd", sizeof(xconf->nas_id)); } void @@ -1439,6 +1522,8 @@ npppd_conf_fini(struct npppd_conf *xconf) struct ipcpconf *ipcp, *ipcp0; struct iface *iface, *iface0; struct confbind *confbind, *confbind0; + struct radclientconf *radc, *radct; + struct radlistenconf *radl, *radlt; TAILQ_FOREACH_SAFE(tunn, &xconf->tunnconfs, entry, tunn0) { tunnconf_fini(tunn); @@ -1455,6 +1540,10 @@ npppd_conf_fini(struct npppd_conf *xconf) TAILQ_FOREACH_SAFE(confbind, &xconf->confbinds, entry, confbind0) { free(confbind); } + TAILQ_FOREACH_SAFE(radc, &xconf->raddaeclientconfs, entry, radct) + free(radc); + TAILQ_FOREACH_SAFE(radl, &xconf->raddaelistenconfs, entry, radlt) + free(radl); TAILQ_INIT(&xconf->l2tp_confs); TAILQ_INIT(&xconf->pptp_confs); TAILQ_INIT(&xconf->pppoe_confs); -- 2.20.1