Add Dynamic Authorization Extensions (DAE) for RADIUS server feature
authoryasuoka <yasuoka@openbsd.org>
Thu, 11 Jul 2024 14:05:59 +0000 (14:05 +0000)
committeryasuoka <yasuoka@openbsd.org>
Thu, 11 Jul 2024 14:05:59 +0000 (14:05 +0000)
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
usr.sbin/npppd/npppd/npppd.conf.5
usr.sbin/npppd/npppd/npppd.h
usr.sbin/npppd/npppd/npppd_config.c
usr.sbin/npppd/npppd/npppd_local.h
usr.sbin/npppd/npppd/npppd_radius.c
usr.sbin/npppd/npppd/npppd_radius.h
usr.sbin/npppd/npppd/parse.y

index e23f830..295cb9e 100644 (file)
@@ -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 <sys/param.h> /* 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;
index ab2ed0b..559ce22 100644 (file)
@@ -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 <yasuoka@openbsd.org>
 .\"
@@ -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
index 3cf9b2b..1d33ce4 100644 (file)
@@ -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
 
index d5a93fb..614ac71 100644 (file)
@@ -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;
 }
index 37205b6..d3e692f 100644 (file)
@@ -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.
 #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 */
index c9d030e..8503bd4 100644 (file)
@@ -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.
 #include <string.h>
 #include <stdbool.h>
 #include <radius.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
 
 #include <event.h>
 
 #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;
+       }
+}
index a133454..a7c309b 100644 (file)
@@ -1,15 +1,35 @@
 #ifndef NPPPD_RADIUS_H
 #define NPPPD_RADIUS_H 1
 
+#include <sys/tree.h>
+#include <netinet/in.h>
+#include <event.h>
+
+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
 }
index a589c5a..2017fe9 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -32,6 +32,7 @@
 #include <inttypes.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -134,6 +135,7 @@ typedef struct {
 %token  INTERFACE ADDRESS IPCP
 %token BIND FROM AUTHENTICATED BY TO
 %token ERROR
+%token DAE CLIENT NAS_ID
 %token <v.string>              STRING
 %token <v.number>              NUMBER
 %type  <v.yesno>               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);