sync with OpenBSD -current

This commit is contained in:
purplerain 2024-07-02 03:24:35 +00:00
parent f5750f4f6e
commit e42955765e
Signed by: purplerain
GPG key ID: F42C07F07E2E35B7
32 changed files with 1329 additions and 320 deletions

View file

@ -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);
}
}