sync with OpenBSD -current
This commit is contained in:
parent
f5750f4f6e
commit
e42955765e
32 changed files with 1329 additions and 320 deletions
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: radiusd.c,v 1.36 2024/02/14 02:44:58 jsg Exp $ */
|
||||
/* $OpenBSD: radiusd.c,v 1.44 2024/07/02 00:33:51 yasuoka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013, 2023 Internet Initiative Japan Inc.
|
||||
|
@ -33,6 +33,7 @@
|
|||
#include <imsg.h>
|
||||
#include <md5.h>
|
||||
#include <netdb.h>
|
||||
#include <paths.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -62,15 +63,18 @@ static void radiusd_on_sighup(int, short, void *);
|
|||
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 *);
|
||||
|
||||
|
||||
|
||||
static void radiusd_module_reset_ev_handler(
|
||||
struct radiusd_module *);
|
||||
static int radiusd_module_imsg_read(struct radiusd_module *,
|
||||
bool);
|
||||
static int radiusd_module_imsg_read(struct radiusd_module *);
|
||||
static void radiusd_module_imsg(struct radiusd_module *,
|
||||
struct imsg *);
|
||||
|
||||
|
@ -89,8 +93,11 @@ static void radiusd_module_request_decoration(
|
|||
struct radiusd_module *, struct radius_query *);
|
||||
static void radiusd_module_response_decoration(
|
||||
struct radiusd_module *, struct radius_query *);
|
||||
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;
|
||||
|
@ -144,16 +151,20 @@ main(int argc, char *argv[])
|
|||
TAILQ_INIT(&radiusd->listen);
|
||||
TAILQ_INIT(&radiusd->query);
|
||||
|
||||
log_init(debug);
|
||||
if (!noaction && debug == 0)
|
||||
daemon(0, 1); /* pend closing stdio files */
|
||||
|
||||
if (parse_config(conffile, radiusd) != 0)
|
||||
errx(EXIT_FAILURE, "config error");
|
||||
log_init(debug);
|
||||
if (noaction) {
|
||||
fprintf(stderr, "configuration OK\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (debug == 0)
|
||||
daemon(0, 0);
|
||||
close_stdio(); /* close stdio files now */
|
||||
|
||||
event_init();
|
||||
|
||||
if ((pw = getpwnam(RADIUSD_USER)) == NULL)
|
||||
|
@ -187,10 +198,16 @@ main(int argc, char *argv[])
|
|||
if (event_loop(0) < 0)
|
||||
radiusd_stop(radiusd);
|
||||
|
||||
if (radiusd->error != 0)
|
||||
log_warnx("exiting on error");
|
||||
|
||||
radiusd_free(radiusd);
|
||||
event_base_free(NULL);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
if (radiusd->error != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -198,7 +215,7 @@ radiusd_start(struct radiusd *radiusd)
|
|||
{
|
||||
struct radiusd_listen *l;
|
||||
struct radiusd_module *module;
|
||||
int s;
|
||||
int s, on;
|
||||
char hbuf[NI_MAXHOST];
|
||||
|
||||
TAILQ_FOREACH(l, &radiusd->listen, next) {
|
||||
|
@ -214,6 +231,12 @@ radiusd_start(struct radiusd *radiusd)
|
|||
hbuf, (int)htons(l->addr.ipv4.sin_port));
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))
|
||||
== -1)
|
||||
log_warn("%s: setsockopt(,,SO_REUSEADDR) failed: %m",
|
||||
__func__);
|
||||
if (bind(s, (struct sockaddr *)&l->addr, l->addr.ipv4.sin_len)
|
||||
!= 0) {
|
||||
log_warn("Listen %s port %d is failed: bind()",
|
||||
|
@ -251,6 +274,7 @@ radiusd_start(struct radiusd *radiusd)
|
|||
|
||||
return (0);
|
||||
on_error:
|
||||
radiusd->error++;
|
||||
radiusd_stop(radiusd);
|
||||
|
||||
return (-1);
|
||||
|
@ -387,8 +411,10 @@ radiusd_listen_handle_packet(struct radiusd_listen *listn,
|
|||
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)
|
||||
|
||||
|
@ -421,9 +447,19 @@ radiusd_listen_handle_packet(struct radiusd_listen *listn,
|
|||
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);
|
||||
|
@ -485,6 +521,13 @@ radiusd_listen_handle_packet(struct radiusd_listen *listn,
|
|||
|
||||
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
|
||||
*/
|
||||
|
@ -521,6 +564,47 @@ radiusd_listen_handle_packet(struct radiusd_listen *listn,
|
|||
|
||||
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(
|
||||
|
@ -609,6 +693,53 @@ on_error:
|
|||
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
|
||||
***********************************************************************/
|
||||
|
@ -691,7 +822,7 @@ radiusd_on_sigchld(int fd, short evmask, void *ctx)
|
|||
struct radiusd *radiusd = ctx;
|
||||
struct radiusd_module *module;
|
||||
pid_t pid;
|
||||
int status;
|
||||
int status, ndeath = 0;
|
||||
|
||||
log_debug("Received SIGCHLD");
|
||||
while ((pid = wait3(&status, WNOHANG, NULL)) != 0) {
|
||||
|
@ -707,6 +838,7 @@ radiusd_on_sigchld(int fd, short evmask, void *ctx)
|
|||
log_warnx("module `%s'(pid=%d) exited "
|
||||
"by signal %d", module->name,
|
||||
(int)pid, WTERMSIG(status));
|
||||
ndeath++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -721,6 +853,10 @@ radiusd_on_sigchld(int fd, short evmask, void *ctx)
|
|||
WTERMSIG(status));
|
||||
}
|
||||
}
|
||||
if (ndeath > 0) {
|
||||
radiusd->error++;
|
||||
event_loopbreak();
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
|
@ -749,6 +885,29 @@ radius_code_string(int code)
|
|||
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)
|
||||
{
|
||||
|
@ -756,6 +915,7 @@ 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;
|
||||
|
@ -1085,9 +1245,8 @@ radiusd_module_on_imsg_io(int fd, short evmask, void *ctx)
|
|||
if (evmask & EV_WRITE)
|
||||
module->writeready = true;
|
||||
|
||||
if (evmask & EV_READ || module->ibuf.r.wpos > IMSG_HEADER_SIZE) {
|
||||
if (radiusd_module_imsg_read(module,
|
||||
(evmask & EV_READ)? true : false) == -1)
|
||||
if (evmask & EV_READ) {
|
||||
if (radiusd_module_imsg_read(module) == -1)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
|
@ -1124,8 +1283,7 @@ radiusd_module_reset_ev_handler(struct radiusd_module *module)
|
|||
evmask |= EV_WRITE;
|
||||
else
|
||||
tvp = &tv; /* fire immediately */
|
||||
} else if (module->ibuf.r.wpos > IMSG_HEADER_SIZE)
|
||||
tvp = &tv; /* fire immediately */
|
||||
}
|
||||
|
||||
/* module stopped and no event handler is set */
|
||||
if (evmask & EV_WRITE && tvp == NULL && module->stopped) {
|
||||
|
@ -1144,22 +1302,20 @@ radiusd_module_reset_ev_handler(struct radiusd_module *module)
|
|||
}
|
||||
|
||||
static int
|
||||
radiusd_module_imsg_read(struct radiusd_module *module, bool doread)
|
||||
radiusd_module_imsg_read(struct radiusd_module *module)
|
||||
{
|
||||
int n;
|
||||
struct imsg imsg;
|
||||
|
||||
if (doread) {
|
||||
if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) {
|
||||
if (n == -1 && errno == EAGAIN)
|
||||
return (0);
|
||||
if (n == -1)
|
||||
log_warn("Receiving a message from module `%s' "
|
||||
"failed: imsg_read", module->name);
|
||||
/* else closed */
|
||||
radiusd_module_close(module);
|
||||
return (-1);
|
||||
}
|
||||
if ((n = imsg_read(&module->ibuf)) == -1 || n == 0) {
|
||||
if (n == -1 && errno == EAGAIN)
|
||||
return (0);
|
||||
if (n == -1)
|
||||
log_warn("Receiving a message from module `%s' "
|
||||
"failed: imsg_read", module->name);
|
||||
/* else closed */
|
||||
radiusd_module_close(module);
|
||||
return (-1);
|
||||
}
|
||||
for (;;) {
|
||||
if ((n = imsg_get(&module->ibuf, &imsg)) == -1) {
|
||||
|
@ -1275,6 +1431,14 @@ radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg)
|
|||
module->radpktoff = 0;
|
||||
switch (imsg->hdr.type) {
|
||||
case IMSG_RADIUSD_MODULE_REQDECO_DONE:
|
||||
if (q->deco == NULL || q->deco->type !=
|
||||
IMSG_RADIUSD_MODULE_REQDECO) {
|
||||
log_warnx("q=%u received %s "
|
||||
"but not requested", q->id, typestr);
|
||||
if (radpkt != NULL)
|
||||
radius_delete_packet(radpkt);
|
||||
break;
|
||||
}
|
||||
if (radpkt != NULL) {
|
||||
radius_delete_packet(q->req);
|
||||
q->req = radpkt;
|
||||
|
@ -1283,7 +1447,7 @@ radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg)
|
|||
break;
|
||||
case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER:
|
||||
if (radpkt == NULL) {
|
||||
log_warn("q=%u wrong pkt from module",
|
||||
log_warnx("q=%u wrong pkt from module",
|
||||
q->id);
|
||||
radiusd_access_request_aborted(q);
|
||||
break;
|
||||
|
@ -1292,6 +1456,14 @@ radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg)
|
|||
radiusd_access_request_answer(q);
|
||||
break;
|
||||
case IMSG_RADIUSD_MODULE_RESDECO_DONE:
|
||||
if (q->deco == NULL || q->deco->type !=
|
||||
IMSG_RADIUSD_MODULE_RESDECO) {
|
||||
log_warnx("q=%u received %s but not "
|
||||
"requested", q->id, typestr);
|
||||
if (radpkt != NULL)
|
||||
radius_delete_packet(radpkt);
|
||||
break;
|
||||
}
|
||||
if (radpkt != NULL) {
|
||||
radius_delete_packet(q->res);
|
||||
radius_set_request_packet(radpkt,
|
||||
|
@ -1306,6 +1478,11 @@ radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg)
|
|||
}
|
||||
case IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED:
|
||||
{
|
||||
if (datalen < (ssize_t)sizeof(u_int)) {
|
||||
log_warnx("Received ACCSREQ_ABORTED message, but "
|
||||
"length is wrong");
|
||||
break;
|
||||
}
|
||||
q_id = *((u_int *)imsg->data);
|
||||
q = radiusd_find_query(module->radiusd, q_id);
|
||||
if (q == NULL) {
|
||||
|
@ -1531,6 +1708,8 @@ radiusd_module_request_decoration(struct radiusd_module *module,
|
|||
radiusd_access_request_aborted(q);
|
||||
return;
|
||||
}
|
||||
RADIUSD_ASSERT(q->deco != NULL);
|
||||
q->deco->type = IMSG_RADIUSD_MODULE_REQDECO;
|
||||
radiusd_module_reset_ev_handler(module);
|
||||
}
|
||||
|
||||
|
@ -1558,9 +1737,34 @@ radiusd_module_response_decoration(struct radiusd_module *module,
|
|||
radiusd_access_request_aborted(q);
|
||||
return;
|
||||
}
|
||||
RADIUSD_ASSERT(q->deco != NULL);
|
||||
q->deco->type = IMSG_RADIUSD_MODULE_RESDECO;
|
||||
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)
|
||||
|
@ -1593,3 +1797,17 @@ imsg_compose_radius_packet(struct imsgbuf *ibuf, uint32_t type, u_int q_id,
|
|||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
close_stdio(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if ((fd = open(_PATH_DEVNULL, O_RDWR)) != -1) {
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
if (fd > STDERR_FILENO)
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue