-/* $OpenBSD: parse.y,v 1.19 2024/07/02 00:00:12 yasuoka Exp $ */
+/* $OpenBSD: parse.y,v 1.20 2024/07/02 00:33:51 yasuoka Exp $ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
%}
%token INCLUDE LISTEN ON PORT CLIENT SECRET LOAD MODULE MSGAUTH_REQUIRED
-%token AUTHENTICATE AUTHENTICATE_BY BY DECORATE_BY SET
-%token ERROR YES NO
+%token ACCOUNT ACCOUNTING AUTHENTICATE AUTHENTICATE_BY BY DECORATE_BY QUICK
+%token SET TO ERROR YES NO
%token <v.string> STRING
%token <v.number> NUMBER
-%type <v.number> optport
+%type <v.number> optport optacct
%type <v.listen> listen_addr
%type <v.str_l> str_l optdeco
%type <v.prefix> prefix
-%type <v.yesno> yesno
+%type <v.yesno> yesno optquick
%type <v.string> strnum
%type <v.string> key
%type <v.string> optstring
| grammar client '\n'
| grammar module '\n'
| grammar authenticate '\n'
+ | grammar account '\n'
| grammar error '\n'
;
*n = $3;
TAILQ_INSERT_TAIL(&conf->listen, n, next);
}
-listen_addr : STRING optport {
+listen_addr : STRING optacct optport {
int gai_errno;
struct addrinfo hints, *res;
free($1);
$$.stype = res->ai_socktype;
$$.sproto = res->ai_protocol;
+ $$.accounting = $2;
memcpy(&$$.addr, res->ai_addr, res->ai_addrlen);
- $$.addr.ipv4.sin_port = ($2 == 0)?
- htons(RADIUS_DEFAULT_PORT) : htons($2);
+ if ($3 != 0)
+ $$.addr.ipv4.sin_port = htons($3);
+ else if ($2)
+ $$.addr.ipv4.sin_port =
+ htons(RADIUS_ACCT_DEFAULT_PORT);
+ else
+ $$.addr.ipv4.sin_port =
+ htons(RADIUS_DEFAULT_PORT);
+
freeaddrinfo(res);
}
+optacct : ACCOUNTING { $$ = 1; }
+ | { $$ = 0; }
+ ;
optport : { $$ = 0; }
| PORT NUMBER { $$ = $2; }
;
free_str_l(&$2);
}
;
+
+account : ACCOUNT optquick str_l TO STRING optdeco {
+ int i, error = 1;
+ struct radiusd_accounting *acct;
+ struct radiusd_module_ref *modref, *modreft;
+
+ if ((acct = calloc(1,
+ sizeof(struct radiusd_authentication))) == NULL) {
+ yyerror("Out of memory: %s", strerror(errno));
+ goto account_error;
+ }
+ if ((acct->acct = create_module_ref($5)) == NULL)
+ goto account_error;
+ acct->username = $3.v;
+ acct->quick = $2;
+ TAILQ_INIT(&acct->deco);
+ for (i = 0; i < $6.c; i++) {
+ if ((modref = create_module_ref($6.v[i]))
+ == NULL)
+ goto account_error;
+ TAILQ_INSERT_TAIL(&acct->deco, modref, next);
+ }
+ TAILQ_INSERT_TAIL(&conf->account, acct, next);
+ acct = NULL;
+ error = 0;
+ account_error:
+ if (acct != NULL) {
+ free(acct->acct);
+ TAILQ_FOREACH_SAFE(modref, &acct->deco, next,
+ modreft) {
+ TAILQ_REMOVE(&acct->deco, modref, next);
+ free(modref);
+ }
+ free_str_l(&$3);
+ }
+ free(acct);
+ free($5);
+ free_str_l(&$6);
+ if (error > 0)
+ YYERROR;
+ }
+ ;
+
+optquick : { $$ = 0; }
+ | QUICK { $$ = 1; }
+
str_l : str_l strnum {
int i;
char **v;
{
/* this has to be sorted always */
static const struct keywords keywords[] = {
+ { "account", ACCOUNT},
+ { "accounting", ACCOUNTING},
{ "authenticate", AUTHENTICATE},
{ "authenticate-by", AUTHENTICATE_BY},
{ "by", BY},
{ "no", NO},
{ "on", ON},
{ "port", PORT},
+ { "quick", QUICK},
{ "secret", SECRET},
{ "set", SET},
+ { "to", TO},
{ "yes", YES},
};
const struct keywords *p;
-/* $OpenBSD: radiusd.c,v 1.43 2024/07/01 23:53:30 yasuoka Exp $ */
+/* $OpenBSD: radiusd.c,v 1.44 2024/07/02 00:33:51 yasuoka Exp $ */
/*
* Copyright (c) 2013, 2023 Internet Initiative Japan Inc.
static void radiusd_on_sigchld(int, short, void *);
static void raidus_query_access_request(struct radius_query *);
static void radius_query_access_response(struct radius_query *);
+static void raidus_query_accounting_request(
+ struct radiusd_accounting *, struct radius_query *);
+static void radius_query_accounting_response(struct radius_query *);
static const char *radius_code_string(int);
+static const char *radius_acct_status_type_string(uint32_t);
static int radiusd_access_response_fixup (struct radius_query *);
struct radiusd_module *, struct radius_query *);
static void radiusd_module_response_decoration(
struct radiusd_module *, struct radius_query *);
-static void close_stdio(void);
+static void radiusd_module_account_request(struct radiusd_module *,
+ struct radius_query *);
static int imsg_compose_radius_packet(struct imsgbuf *,
uint32_t, u_int, RADIUS_PACKET *);
+static void close_stdio(void);
static u_int radius_query_id_seq = 0;
int debug = 0;
static char username[256];
char peerstr[NI_MAXHOST + NI_MAXSERV + 30];
struct radiusd_authentication *authen;
+ struct radiusd_accounting *accounting;
struct radiusd_client *client;
struct radius_query *q = NULL;
+ uint32_t acct_status;
#define in(_x) (((struct sockaddr_in *)_x)->sin_addr)
#define in6(_x) (((struct sockaddr_in6 *)_x)->sin6_addr)
goto on_error;
}
+ /* Check the request authenticator if accounting */
+ if ((req_code == RADIUS_CODE_ACCOUNTING_REQUEST ||
+ listn->accounting) && radius_check_accounting_request_authenticator(
+ packet, client->secret) != 0) {
+ log_warnx("Received %s(code=%d) from %s id=%d: bad request "
+ "authenticator", radius_code_string(req_code), req_code,
+ peerstr, req_id);
+ goto on_error;
+ }
+
/* Check the client's Message-Authenticator */
- if (client->msgauth_required && !radius_has_attr(packet,
- RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) {
+ if (client->msgauth_required && !listn->accounting &&
+ !radius_has_attr(packet, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) {
log_warnx("Received %s(code=%d) from %s id=%d: no message "
"authenticator", radius_code_string(req_code), req_code,
peerstr, req_id);
switch (req_code) {
case RADIUS_CODE_ACCESS_REQUEST:
+ if (listn->accounting) {
+ log_info("Received %s(code=%d) from %s id=%d: "
+ "ignored because the port is for authentication",
+ radius_code_string(req_code), req_code, peerstr,
+ req_id);
+ break;
+ }
/*
* Find a matching `authenticate' entry
*/
raidus_query_access_request(q);
return;
+ case RADIUS_CODE_ACCOUNTING_REQUEST:
+ if (!listn->accounting) {
+ log_info("Received %s(code=%d) from %s id=%d: "
+ "ignored because the port is for accounting",
+ radius_code_string(req_code), req_code, peerstr,
+ req_id);
+ break;
+ }
+ if (radius_get_uint32_attr(q->req, RADIUS_TYPE_ACCT_STATUS_TYPE,
+ &acct_status) != 0)
+ acct_status = 0;
+ /*
+ * Find a matching `accounting' entry
+ */
+ TAILQ_FOREACH(accounting, &listn->radiusd->account, next) {
+ if (acct_status == RADIUS_ACCT_STATUS_TYPE_ACCT_ON ||
+ acct_status == RADIUS_ACCT_STATUS_TYPE_ACCT_OFF) {
+ raidus_query_accounting_request(accounting, q);
+ continue;
+ }
+ for (i = 0; accounting->username[i] != NULL; i++) {
+ if (fnmatch(accounting->username[i], username,
+ 0) == 0)
+ break;
+ }
+ if (accounting->username[i] == NULL)
+ continue;
+ raidus_query_accounting_request(accounting, q);
+ if (accounting->quick)
+ break;
+ }
+ /* pass NULL to hadnle this self without module */
+ raidus_query_accounting_request(NULL, q);
+
+ if ((q->res = radius_new_response_packet(
+ RADIUS_CODE_ACCOUNTING_RESPONSE, q->req)) == NULL)
+ log_warn("%s: radius_new_response_packet() failed",
+ __func__);
+ else
+ radius_query_accounting_response(q);
+ break;
default:
log_info("Received %s(code=%d) from %s id=%d: %s is not "
"supported in this implementation", radius_code_string(
radiusd_access_request_aborted(q);
}
+static void
+raidus_query_accounting_request(struct radiusd_accounting *accounting,
+ struct radius_query *q)
+{
+ int req_code;
+ uint32_t acct_status;
+ char buf0[NI_MAXHOST + NI_MAXSERV + 30];
+
+ if (accounting != NULL) {
+ /* handle by the module */
+ if (MODULE_DO_ACCTREQ(accounting->acct->module))
+ radiusd_module_account_request(accounting->acct->module,
+ q);
+ return;
+ }
+ req_code = radius_get_code(q->req);
+ if (radius_get_uint32_attr(q->req, RADIUS_TYPE_ACCT_STATUS_TYPE,
+ &acct_status) != 0)
+ acct_status = 0;
+ log_info("Received %s(code=%d) type=%s(%lu) from %s id=%d username=%s "
+ "q=%u", radius_code_string(req_code), req_code,
+ radius_acct_status_type_string(acct_status), (unsigned long)
+ acct_status, addrport_tostring((struct sockaddr *)&q->clientaddr,
+ q->clientaddrlen, buf0, sizeof(buf0)), q->req_id, q->username,
+ q->id);
+}
+
+static void
+radius_query_accounting_response(struct radius_query *q)
+{
+ int sz, res_id, res_code;
+ char buf[NI_MAXHOST + NI_MAXSERV + 30];
+
+ radius_set_response_authenticator(q->res, q->client->secret);
+ res_id = radius_get_id(q->res);
+ res_code = radius_get_code(q->res);
+
+ log_info("Sending %s(code=%d) to %s id=%u q=%u",
+ radius_code_string(res_code), res_code,
+ addrport_tostring((struct sockaddr *)&q->clientaddr,
+ q->clientaddrlen, buf, sizeof(buf)), res_id, q->id);
+
+ if ((sz = sendto(q->listen->sock, radius_get_data(q->res),
+ radius_get_length(q->res), 0,
+ (struct sockaddr *)&q->clientaddr, q->clientaddrlen)) <= 0)
+ log_warn("Sending a RADIUS response failed");
+}
/***********************************************************************
* Callback functions from the modules
***********************************************************************/
return ("Unknown");
}
+static const char *
+radius_acct_status_type_string(uint32_t type)
+{
+ int i;
+ struct _typestrings {
+ uint32_t type;
+ const char *string;
+ } typestrings[] = {
+ { RADIUS_ACCT_STATUS_TYPE_START, "Start" },
+ { RADIUS_ACCT_STATUS_TYPE_STOP, "Stop" },
+ { RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE, "Interim-Update" },
+ { RADIUS_ACCT_STATUS_TYPE_ACCT_ON, "Accounting-On" },
+ { RADIUS_ACCT_STATUS_TYPE_ACCT_OFF, "Accounting-Off" },
+ { -1, NULL }
+ };
+
+ for (i = 0; typestrings[i].string != NULL; i++)
+ if (typestrings[i].type == type)
+ return (typestrings[i].string);
+
+ return ("Unknown");
+}
+
void
radiusd_conf_init(struct radiusd *conf)
{
TAILQ_INIT(&conf->listen);
TAILQ_INIT(&conf->module);
TAILQ_INIT(&conf->authen);
+ TAILQ_INIT(&conf->account);
TAILQ_INIT(&conf->client);
return;
radiusd_module_reset_ev_handler(module);
}
+static void
+radiusd_module_account_request(struct radiusd_module *module,
+ struct radius_query *q)
+{
+ RADIUS_PACKET *radpkt;
+
+ if ((radpkt = radius_convert_packet(radius_get_data(q->req),
+ radius_get_length(q->req))) == NULL) {
+ log_warn("q=%u Could not send ACCSREQ to `%s'", q->id,
+ module->name);
+ radiusd_access_request_aborted(q);
+ return;
+ }
+ if (imsg_compose_radius_packet(&module->ibuf,
+ IMSG_RADIUSD_MODULE_ACCTREQ, q->id, radpkt) == -1) {
+ log_warn("q=%u Could not send ACCTREQ to `%s'", q->id,
+ module->name);
+ radiusd_access_request_aborted(q);
+ }
+ radiusd_module_reset_ev_handler(module);
+ radius_delete_packet(radpkt);
+}
+
static int
imsg_compose_radius_packet(struct imsgbuf *ibuf, uint32_t type, u_int q_id,
RADIUS_PACKET *radpkt)
-.\" $OpenBSD: radiusd.conf.5,v 1.20 2024/07/02 00:00:12 yasuoka Exp $
+.\" $OpenBSD: radiusd.conf.5,v 1.21 2024/07/02 00:33:51 yasuoka Exp $
.\"
.\" Copyright (c) 2014 Esdenera Networks GmbH
.\" Copyright (c) 2014, 2023 Internet Initiative Japan Inc.
The configuration options are as follows:
.Bl -tag -width Ds
.It Xo
-.Ic listen on Ar address
+.Ic listen on Oo Ic accounting Oc Ar address
.Ic port Ar port
.Xc
Specify an
and a
.Ar port
to listen on.
+When
+.Ar accouting
+is specified,
+it is used for waiting for RADIUS accounting messages.
+The default port number is 1812 for authentication and 1813 for accounting.
.It Ic client Ar address/mask Brq ...
Allow access to a client with the specified
.Ar address
Optionally decoration modules can be specified by
.Ar deco .
The specified modules decorate the RADIUS messages in the configured order.
+.It Ic account Oo Ic quick Oc Ar username-pattern ... Ic to Ar module \
+Oo Ic decoratd by Ar deco ... Oc
+Specify an accounting configuration for the users specified by
+.Ar username-pattern .
+The accounting messages for the users matched by the pattern are handled
+by the module specified by the
+.Ar module .
+Use shell globbing rules for the patterns;
+multiple patterns can be determined by separating them with space characters.
+When multiple
+.Ic account
+lines are selected,
+all account settings whose
+.Ar username-pattern
+matches an accounting users are used.
+until the user matches the setting with the
+.Ar quick
+option.
+.Pp
+Optionally decoration modules can be specified by
+.Ar deco .
+The specified modules decorate the RADIUS messages in the configured order.
.El
.Sh FILES
.Bl -tag -width "/etc/examples/radiusd.conf" -compact
.Sh EXAMPLES
.Bd -literal -offset indent
listen on 0.0.0.0
+listen on 0.0.0.0 accounting
listen on ::
+listen on :: accounting
client 127.0.0.1/32 {
secret "secret"
authenticate *@local by bsdauth decorate-by strip-realm
authenticate * by radius
+
+account * to standard
.Ed
.Sh SEE ALSO
.Xr radiusd 8 ,
-/* $OpenBSD: radiusd.h,v 1.6 2024/01/08 04:16:48 yasuoka Exp $ */
+/* $OpenBSD: radiusd.h,v 1.7 2024/07/02 00:33:51 yasuoka Exp $ */
#ifndef RADIUSD_H
#define RADIUSD_H 1
IMSG_RADIUSD_MODULE_RESDECO0_REQ, /* request pkt for RESDECO */
IMSG_RADIUSD_MODULE_RESDECO,
IMSG_RADIUSD_MODULE_RESDECO_DONE,
- IMSG_RADIUSD_MODULE_STOP
+ IMSG_RADIUSD_MODULE_ACCTREQ,
+ IMSG_RADIUSD_MODULE_STOP,
};
/* Module sends LOAD when it becomes ready */
struct radiusd_module_load_arg {
uint32_t cap; /* module capabity bits */
-#define RADIUSD_MODULE_CAP_USERPASS 0x1
-#define RADIUSD_MODULE_CAP_ACCSREQ 0x2
-#define RADIUSD_MODULE_CAP_REQDECO 0x4
-#define RADIUSD_MODULE_CAP_RESDECO 0x8
+#define RADIUSD_MODULE_CAP_USERPASS 0x01
+#define RADIUSD_MODULE_CAP_ACCSREQ 0x02
+#define RADIUSD_MODULE_CAP_REQDECO 0x04
+#define RADIUSD_MODULE_CAP_RESDECO 0x08
+#define RADIUSD_MODULE_CAP_ACCTREQ 0x10
};
struct radiusd_module_object {
-/* $OpenBSD: radiusd_local.h,v 1.10 2024/07/01 05:20:01 yasuoka Exp $ */
+/* $OpenBSD: radiusd_local.h,v 1.11 2024/07/02 00:33:51 yasuoka Exp $ */
/*
* Copyright (c) 2013 Internet Initiative Japan Inc.
struct radiusd *radiusd;
struct event ev;
int sock;
+ int accounting;
union {
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
TAILQ_ENTRY(radiusd_authentication) next;
};
+struct radiusd_accounting {
+ char **username;
+ char *secret;
+ struct radiusd_module_ref *acct;
+ int quick;
+ TAILQ_HEAD(,radiusd_module_ref) deco;
+ TAILQ_ENTRY(radiusd_accounting) next;
+};
+
struct radiusd {
struct radiusd_listen_head listen;
struct event ev_sigterm;
struct event ev_sigchld;
TAILQ_HEAD(,radiusd_module) module;
TAILQ_HEAD(,radiusd_authentication) authen;
+ TAILQ_HEAD(,radiusd_accounting) account;
TAILQ_HEAD(,radiusd_client) client;
TAILQ_HEAD(,radius_query) query;
int error;
#define MODULE_DO_ACCSREQ(_m) \
((_m)->fd >= 0 && \
((_m)->capabilities & RADIUSD_MODULE_CAP_ACCSREQ) != 0)
+#define MODULE_DO_ACCTREQ(_m) \
+ ((_m)->fd >= 0 && \
+ ((_m)->capabilities & RADIUSD_MODULE_CAP_ACCTREQ) != 0)
#define MODULE_DO_REQDECO(_m) \
((_m)->fd >= 0 && \
((_m)->capabilities & RADIUSD_MODULE_CAP_REQDECO) != 0)
-/* $OpenBSD: radiusd_module.c,v 1.16 2024/02/09 07:41:32 yasuoka Exp $ */
+/* $OpenBSD: radiusd_module.c,v 1.17 2024/07/02 00:33:51 yasuoka Exp $ */
/*
* Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
size_t) = NULL;
static void (*module_response_decoration) (void *, u_int, const u_char *,
size_t, const u_char *, size_t) = NULL;
+static void (*module_accounting_request) (void *, u_int, const u_char *,
+ size_t) = NULL;
struct module_base {
void *ctx;
module_config_set = handler->config_set;
module_request_decoration = handler->request_decoration;
module_response_decoration = handler->response_decoration;
+ module_accounting_request = handler->accounting_request;
module_start_module = handler->start;
module_stop_module = handler->stop;
load.cap |= RADIUSD_MODULE_CAP_REQDECO;
if (module_response_decoration != NULL)
load.cap |= RADIUSD_MODULE_CAP_RESDECO;
+ if (module_accounting_request != NULL)
+ load.cap |= RADIUSD_MODULE_CAP_ACCTREQ;
imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load,
sizeof(load));
imsg_flush(&base->ibuf);
case IMSG_RADIUSD_MODULE_REQDECO:
case IMSG_RADIUSD_MODULE_RESDECO0_REQ:
case IMSG_RADIUSD_MODULE_RESDECO:
+ case IMSG_RADIUSD_MODULE_ACCTREQ:
{
struct radiusd_module_radpkt_arg *accessreq;
int chunklen;
break;
}
typestr = "ACCSREQ";
+ } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCTREQ) {
+ if (module_accounting_request == NULL) {
+ syslog(LOG_ERR, "Received ACCTREQ message, but "
+ "module doesn't support");
+ break;
+ }
+ typestr = "ACCTREQ";
} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) {
if (module_request_decoration == NULL) {
syslog(LOG_ERR, "Received REQDECO message, but "
}
memcpy(base->radpkt2, base->radpkt, base->radpktoff);
base->radpkt2len = base->radpktoff;
- } else {
+ } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO) {
module_response_decoration(base->ctx, accessreq->q_id,
base->radpkt2, base->radpkt2len, base->radpkt,
base->radpktoff);
base->radpkt2len = 0;
- }
+ } else
+ module_accounting_request(base->ctx, accessreq->q_id,
+ base->radpkt, base->radpktoff);
base->radpktoff = 0;
-accsreq_out:
+ accsreq_out:
break;
}
}
#include "radiusd.h"
struct module_ctx;
+struct imsg;
struct module_handlers {
/* Should send IMSG_OK or IMSG_NG */
void (*response_decoration)(void *ctx, u_int query_id,
const u_char *req, size_t reqlen, const u_char *res, size_t reslen);
+
+ void (*accounting_request)(void *ctx, u_int query_id, const u_char *pkt,
+ size_t pktlen);
+
+ void (*dispatch_control)(void *ctx, struct imsg *);
};
#define SYNTAX_ASSERT(_cond, _msg) \
const u_char *, size_t);
int module_resdeco_done(struct module_base *, u_int,
const u_char *, size_t);
+int module_imsg_compose(struct module_base *, uint32_t,
+ uint32_t, pid_t, int, const void *, size_t);
+int module_imsg_composev(struct module_base *, uint32_t,
+ uint32_t, pid_t, int, const struct iovec *, int);
__END_DECLS
-.\" $OpenBSD: radiusd_standard.8,v 1.1 2024/07/02 00:00:12 yasuoka Exp $
+.\" $OpenBSD: radiusd_standard.8,v 1.2 2024/07/02 00:33:51 yasuoka Exp $
.\"
.\" Copyright (c) 2014 Esdenera Networks GmbH
.\" Copyright (c) 2014, 2024 Internet Initiative Japan Inc.
.Sh DESCRIPTION
The
.Nm
-utility processes files ...
+utility is executed by
+.Xr radiusd 8
+as a module to provide various standard functionalities.
+It can be configured as a module for decoration which modifies request and
+response RADIUS messages.
+Also it can be configured as an accounting module that logs accounting
+information through
+.Xr syslog 3 .
.Sh CONFIGURATIONS
The
.Nm
-/* $OpenBSD: radiusd_standard.c,v 1.5 2024/04/23 13:34:51 jsg Exp $ */
+/* $OpenBSD: radiusd_standard.c,v 1.6 2024/07/02 00:33:51 yasuoka Exp $ */
/*
* Copyright (c) 2013, 2023 Internet Initiative Japan Inc.
*/
#include <sys/types.h>
#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
struct attrs remove_resattrs;
};
+struct radius_const_str {
+ const unsigned constval;
+ const char *label;
+};
+
+static void radius_const_print(FILE *, RADIUS_PACKET *, uint8_t,
+ const char *, struct radius_const_str *);
static void module_standard_config_set(void *, const char *, int,
char * const *);
static void module_standard_reqdeco(void *, u_int, const u_char *, size_t);
static void module_standard_resdeco(void *, u_int, const u_char *, size_t,
const u_char *, size_t);
+static void module_accounting_request(void *, u_int, const u_char *,
+ size_t);
+static void radius_u32_print(FILE *, RADIUS_PACKET *, uint8_t,
+ const char *);
+static void radius_str_print(FILE *, RADIUS_PACKET *, uint8_t,
+ const char *);
+static void radius_ipv4_print(FILE *, RADIUS_PACKET *, uint8_t,
+ const char *);
+static void radius_ipv6_print(FILE *, RADIUS_PACKET *, uint8_t,
+ const char *);
+
+static struct radius_const_str
+ nas_port_type_consts[], tunnel_type_consts[],
+ service_type_consts[], framed_protocol_consts[],
+ acct_status_type_consts[], acct_authentic_consts[],
+ terminate_cause_consts[], tunnel_medium_type_consts[];
int
main(int argc, char *argv[])
struct module_handlers handlers = {
.config_set = module_standard_config_set,
.request_decoration = module_standard_reqdeco,
- .response_decoration = module_standard_resdeco
+ .response_decoration = module_standard_resdeco,
+ .accounting_request = module_accounting_request
};
struct attr *attr;
if (radres != NULL)
radius_delete_packet(radres);
}
+
+static void
+module_accounting_request(void *ctx, u_int query_id, const u_char *pkt,
+ size_t pktlen)
+{
+ RADIUS_PACKET *radpkt = NULL;
+ struct module_standard *module = ctx;
+ FILE *fp;
+ char *buf = NULL;
+ size_t size = 0;
+
+ if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) {
+ syslog(LOG_ERR,
+ "%s: radius_convert_packet() failed: %m", __func__);
+ module_stop(module->base);
+ return;
+ }
+
+ if ((fp = open_memstream(&buf, &size)) == NULL) {
+ syslog(LOG_ERR, "%s: open_memstream() failed: %m", __func__);
+ module_stop(module->base);
+ goto out;
+ }
+ radius_const_print(fp, radpkt, RADIUS_TYPE_ACCT_STATUS_TYPE,
+ "Acct-Status-Type", acct_status_type_consts);
+
+ radius_ipv4_print(fp, radpkt, RADIUS_TYPE_NAS_IP_ADDRESS,
+ "NAS-IP-Address");
+ radius_ipv6_print(fp, radpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS,
+ "NAS-IPv6-Address");
+ radius_const_print(fp, radpkt, RADIUS_TYPE_NAS_PORT_TYPE,
+ "NAS-Port-Type", nas_port_type_consts);
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_NAS_PORT, "NAS-Port");
+ radius_str_print(fp, radpkt, RADIUS_TYPE_NAS_IDENTIFIER,
+ "NAS-Identifier");
+ radius_str_print(fp, radpkt, RADIUS_TYPE_CALLING_STATION_ID,
+ "Calling-Station-ID");
+ radius_str_print(fp, radpkt, RADIUS_TYPE_CALLED_STATION_ID,
+ "Called-Station-ID");
+
+ radius_const_print(fp, radpkt, RADIUS_TYPE_TUNNEL_MEDIUM_TYPE,
+ "Tunnel-Medium-Type", tunnel_medium_type_consts);
+ radius_str_print(fp, radpkt, RADIUS_TYPE_TUNNEL_CLIENT_ENDPOINT,
+ "Tunnel-Client-Endpoint");
+ radius_str_print(fp, radpkt, RADIUS_TYPE_TUNNEL_SERVER_ENDPOINT,
+ "Tunnel-Server-Endpoint");
+ radius_str_print(fp, radpkt, RADIUS_TYPE_TUNNEL_ASSIGNMENT_ID,
+ "Tunnel-Assignment-ID");
+ radius_str_print(fp, radpkt, RADIUS_TYPE_ACCT_TUNNEL_CONNECTION,
+ "Acct-Tunnel-Connection");
+
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_SESSION_TIME,
+ "Acct-Session-Time");
+ radius_const_print(fp, radpkt,
+ RADIUS_TYPE_TUNNEL_TYPE, "Tunnel-Type", tunnel_type_consts);
+ radius_str_print(fp, radpkt, RADIUS_TYPE_USER_NAME, "User-Name");
+ radius_const_print(fp, radpkt,
+ RADIUS_TYPE_SERVICE_TYPE, "Service-Type", service_type_consts);
+ radius_const_print(fp, radpkt, RADIUS_TYPE_FRAMED_PROTOCOL,
+ "Framed-Protocol", framed_protocol_consts);
+ radius_ipv4_print(fp, radpkt, RADIUS_TYPE_FRAMED_IP_ADDRESS,
+ "Framed-IP-Address");
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_DELAY_TIME,
+ "Acct-Delay-Time");
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_INPUT_OCTETS,
+ "Acct-Input-Octets");
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_OUTPUT_OCTETS,
+ "Acct-Output-Octets");
+ radius_str_print(fp, radpkt, RADIUS_TYPE_ACCT_SESSION_ID,
+ "Acct-Session-ID");
+ radius_const_print(fp, radpkt, RADIUS_TYPE_ACCT_AUTHENTIC,
+ "Acct-Authentic", acct_authentic_consts);
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_SESSION_TIME,
+ "Acct-Sesion-Time");
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_INPUT_PACKETS,
+ "Acct-Input-Packets");
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_OUTPUT_PACKETS,
+ "Acct-Output-Packets");
+ radius_const_print(fp, radpkt, RADIUS_TYPE_ACCT_TERMINATE_CAUSE,
+ "Acct-Terminate-Cause", terminate_cause_consts);
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_INPUT_GIGAWORDS,
+ "Acct-Input-Gigawords");
+ radius_u32_print(fp, radpkt, RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS,
+ "Acct-Output-Gigawords");
+
+ fputc('\0', fp);
+ fclose(fp);
+ syslog(LOG_INFO, "Accounting q=%u %s", query_id, buf + 1);
+ out:
+ radius_delete_packet(radpkt);
+ freezero(buf, size);
+}
+
+/***********************************************************************
+ * print RADIUS attribute
+ ***********************************************************************/
+static void
+radius_const_print(FILE *fout, RADIUS_PACKET *radpkt, uint8_t attr_type,
+ const char *attr_name, struct radius_const_str *consts)
+{
+ struct radius_const_str *const_;
+ uint32_t u32val;
+
+ if (radius_get_uint32_attr(radpkt, attr_type, &u32val) != 0)
+ return;
+
+ for (const_ = consts; const_->label != NULL; const_++) {
+ if (const_->constval == u32val)
+ break;
+ }
+
+ fprintf(fout, " %s=%s(%u)", attr_name, (const_ != NULL)? const_->label
+ : "unknown", (unsigned)u32val);
+}
+
+static void
+radius_u32_print(FILE *fout, RADIUS_PACKET *radpkt, uint8_t attr_type,
+ const char *attr_name)
+{
+ uint32_t u32val;
+
+ if (radius_get_uint32_attr(radpkt, attr_type, &u32val) != 0)
+ return;
+ fprintf(fout, " %s=%u", attr_name, u32val);
+}
+
+static void
+radius_str_print(FILE *fout, RADIUS_PACKET *radpkt, uint8_t attr_type,
+ const char *attr_name)
+{
+ char strval[256];
+
+ if (radius_get_string_attr(radpkt, attr_type, strval, sizeof(strval))
+ != 0)
+ return;
+ fprintf(fout, " %s=%s", attr_name, strval);
+}
+
+static void
+radius_ipv4_print(FILE *fout, RADIUS_PACKET *radpkt, uint8_t attr_type,
+ const char *attr_name)
+{
+ struct in_addr ipv4;
+ char buf[128];
+
+ if (radius_get_ipv4_attr(radpkt, attr_type, &ipv4) != 0)
+ return;
+ fprintf(fout, " %s=%s", attr_name,
+ inet_ntop(AF_INET, &ipv4, buf, sizeof(buf)));
+}
+
+static void
+radius_ipv6_print(FILE *fout, RADIUS_PACKET *radpkt, uint8_t attr_type,
+ const char *attr_name)
+{
+ struct in6_addr ipv6;
+ char buf[128];
+
+ if (radius_get_ipv6_attr(radpkt, attr_type, &ipv6) != 0)
+ return;
+
+ fprintf(fout, " %s=%s", attr_name,
+ inet_ntop(AF_INET6, &ipv6, buf, sizeof(buf)));
+}
+
+static struct radius_const_str nas_port_type_consts[] = {
+ { RADIUS_NAS_PORT_TYPE_ASYNC, "\"Async\"" },
+ { RADIUS_NAS_PORT_TYPE_SYNC, "\"Sync\"" },
+ { RADIUS_NAS_PORT_TYPE_ISDN_SYNC, "\"ISDN Sync\"" },
+ { RADIUS_NAS_PORT_TYPE_ISDN_ASYNC_V120, "\"ISDN Async V.120\"" },
+ { RADIUS_NAS_PORT_TYPE_ISDN_ASYNC_V110, "\"ISDN Async V.110\"" },
+ { RADIUS_NAS_PORT_TYPE_VIRTUAL, "\"Virtual\"" },
+ { RADIUS_NAS_PORT_TYPE_PIAFS, "\"PIAFS\"" },
+ { RADIUS_NAS_PORT_TYPE_HDLC_CLEAR_CHANNEL, "\"HDLC Clear Channel\"" },
+ { RADIUS_NAS_PORT_TYPE_X_25, "\"X.25\"" },
+ { RADIUS_NAS_PORT_TYPE_X_75, "\"X.75\"" },
+ { RADIUS_NAS_PORT_TYPE_G3_FAX, "\"G.3 Fax\"" },
+ { RADIUS_NAS_PORT_TYPE_SDSL, "\"SDSL\"" },
+ { RADIUS_NAS_PORT_TYPE_ADSL_CAP, "\"ADSL-CAP\"" },
+ { RADIUS_NAS_PORT_TYPE_ADSL_DMT, "\"ADSL-DMT\"" },
+ { RADIUS_NAS_PORT_TYPE_IDSL, "\"IDSL\"" },
+ { RADIUS_NAS_PORT_TYPE_ETHERNET, "\"Ethernet\"" },
+ { RADIUS_NAS_PORT_TYPE_XDSL, "\"xDSL\"" },
+ { RADIUS_NAS_PORT_TYPE_CABLE, "\"Cable\"" },
+ { RADIUS_NAS_PORT_TYPE_WIRELESS, "\"Wireless\"" },
+ { RADIUS_NAS_PORT_TYPE_WIRELESS_802_11, "\"Wireless - IEEE 802.11\"" },
+ { 0, NULL }
+};
+
+static struct radius_const_str tunnel_type_consts[] = {
+ { RADIUS_TUNNEL_TYPE_PPTP, "PPTP" },
+ { RADIUS_TUNNEL_TYPE_L2F, "L2F" },
+ { RADIUS_TUNNEL_TYPE_L2TP, "L2TP" },
+ { RADIUS_TUNNEL_TYPE_ATMP, "ATMP" },
+ { RADIUS_TUNNEL_TYPE_VTP, "VTP" },
+ { RADIUS_TUNNEL_TYPE_AH, "AH" },
+ { RADIUS_TUNNEL_TYPE_IP, "IP" },
+ { RADIUS_TUNNEL_TYPE_MOBILE, "MIN-IP-IP" },
+ { RADIUS_TUNNEL_TYPE_ESP, "ESP" },
+ { RADIUS_TUNNEL_TYPE_GRE, "GRE" },
+ { RADIUS_TUNNEL_TYPE_VDS, "DVS" },
+ { 0, NULL }
+};
+
+static struct radius_const_str service_type_consts[] = {
+ { RADIUS_SERVICE_TYPE_LOGIN, "\"Login\"" },
+ { RADIUS_SERVICE_TYPE_FRAMED, "\"Framed\"" },
+ { RADIUS_SERVICE_TYPE_CB_LOGIN, "\"Callback Login\"" },
+ { RADIUS_SERVICE_TYPE_CB_FRAMED, "\"Callback Framed\"" },
+ { RADIUS_SERVICE_TYPE_OUTBOUND, "\"Outbound\"" },
+ { RADIUS_SERVICE_TYPE_ADMINISTRATIVE, "\"Administrative\"" },
+ { RADIUS_SERVICE_TYPE_NAS_PROMPT, "\"NAS Propmt\"" },
+/* there had been a typo in radius.h */
+#if !defined(RADIUS_SERVICE_TYPE_CB_NAS_PROMPT) && \
+ defined(RADIUS_SERVICE_TYPE_CB_NAS_PROMPTi)
+#define RADIUS_SERVICE_TYPE_CB_NAS_PROMPT RADIUS_SERVICE_TYPE_CB_NAS_PROMPTi
+#endif
+ { RADIUS_SERVICE_TYPE_AUTHENTICAT_ONLY, "\"Authenticat Only\"" },
+ { RADIUS_SERVICE_TYPE_CB_NAS_PROMPT, "\"Callback NAS Prompt\"" },
+ { RADIUS_SERVICE_TYPE_CALL_CHECK, "\"Call Check\"" },
+ { RADIUS_SERVICE_TYPE_CB_ADMINISTRATIVE, "\"Callback Administrative\"" },
+ { 0, NULL }
+};
+
+static struct radius_const_str framed_protocol_consts[] = {
+ { RADIUS_FRAMED_PROTOCOL_PPP, "PPP" },
+ { RADIUS_FRAMED_PROTOCOL_SLIP, "SLIP" },
+ { RADIUS_FRAMED_PROTOCOL_ARAP, "ARAP" },
+ { RADIUS_FRAMED_PROTOCOL_GANDALF, "Gandalf" },
+ { RADIUS_FRAMED_PROTOCOL_XYLOGICS, "Xylogics" },
+ { RADIUS_FRAMED_PROTOCOL_X75, "X.75" },
+ { 0, NULL }
+};
+
+static struct radius_const_str acct_status_type_consts[] = {
+ { RADIUS_ACCT_STATUS_TYPE_START, "Start" },
+ { RADIUS_ACCT_STATUS_TYPE_STOP, "Stop" },
+ { RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE, "Interim-Update" },
+ { RADIUS_ACCT_STATUS_TYPE_ACCT_ON, "Accounting-On" },
+ { RADIUS_ACCT_STATUS_TYPE_ACCT_OFF, "Accounting-Off" },
+ { 0, NULL }
+};
+
+static struct radius_const_str acct_authentic_consts[] = {
+ { RADIUS_ACCT_AUTHENTIC_RADIUS, "RADIUS" },
+ { RADIUS_ACCT_AUTHENTIC_LOCAL, "Local" },
+ { RADIUS_ACCT_AUTHENTIC_REMOTE, "Remote" },
+ { 0, NULL }
+};
+
+static struct radius_const_str terminate_cause_consts[] = {
+ { RADIUS_TERMNATE_CAUSE_USER_REQUEST, "\"User Request\"" },
+ { RADIUS_TERMNATE_CAUSE_LOST_CARRIER, "\"Lost Carrier\"" },
+ { RADIUS_TERMNATE_CAUSE_LOST_SERVICE, "\"Lost Service\"" },
+ { RADIUS_TERMNATE_CAUSE_IDLE_TIMEOUT, "\"Idle Timeout\"" },
+ { RADIUS_TERMNATE_CAUSE_SESSION_TIMEOUT, "\"Session Timeout\"" },
+ { RADIUS_TERMNATE_CAUSE_ADMIN_RESET, "\"Admin Reset\"" },
+ { RADIUS_TERMNATE_CAUSE_ADMIN_REBOOT, "\"Admin Reboot\"" },
+ { RADIUS_TERMNATE_CAUSE_PORT_ERROR, "\"Port Error\"" },
+ { RADIUS_TERMNATE_CAUSE_NAS_ERROR, "\"NAS Error\"" },
+ { RADIUS_TERMNATE_CAUSE_NAS_RESET, "\"NAS Request\"" },
+ { RADIUS_TERMNATE_CAUSE_NAS_REBOOT, "\"NAS Reboot\"" },
+ { RADIUS_TERMNATE_CAUSE_PORT_UNNEEDED, "\"Port Unneeded\"" },
+ { RADIUS_TERMNATE_CAUSE_PORT_PREEMPTED, "\"Port Preempted\"" },
+ { RADIUS_TERMNATE_CAUSE_PORT_SUSPENDED, "\"Port Suspended\"" },
+ { RADIUS_TERMNATE_CAUSE_SERVICE_UNAVAIL, "\"Service Unavailable\"" },
+ { RADIUS_TERMNATE_CAUSE_CALLBACK, "\"Callback\"" },
+ { RADIUS_TERMNATE_CAUSE_USER_ERROR, "\"User Error\"" },
+ { RADIUS_TERMNATE_CAUSE_HOST_REQUEST, "\"Host Request\"" },
+ { 0, NULL }
+};
+
+static struct radius_const_str tunnel_medium_type_consts[] = {
+ { RADIUS_TUNNEL_MEDIUM_TYPE_IPV4, "IPv4" },
+ { RADIUS_TUNNEL_MEDIUM_TYPE_IPV6, "IPv6" },
+ { RADIUS_TUNNEL_MEDIUM_TYPE_NSAP, "NSAP" },
+ { RADIUS_TUNNEL_MEDIUM_TYPE_HDLC, "HDLC" },
+ { RADIUS_TUNNEL_MEDIUM_TYPE_BBN1822, "BBN1822" },
+ { RADIUS_TUNNEL_MEDIUM_TYPE_802, "802" },
+ { RADIUS_TUNNEL_MEDIUM_TYPE_E163, "E.163" },
+ { RADIUS_TUNNEL_MEDIUM_TYPE_E164, "E.164" },
+ { 0, NULL }
+};