-/* $OpenBSD: parse.y,v 1.24 2024/07/14 13:44:30 yasuoka Exp $ */
+/* $OpenBSD: parse.y,v 1.25 2024/07/14 15:27:57 yasuoka Exp $ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
static struct radiusd_module *conf_module = NULL;
static struct radiusd_client client;
-static struct radiusd_module *find_module(const char *);
+static struct radiusd_authentication
+ *create_authen(const char *, char **, int, char **);
+static struct radiusd_module
+ *find_module(const char *);
static void free_str_l(void *);
static struct radiusd_module_ref *create_module_ref(const char *);
static void radiusd_authentication_init(struct radiusd_authentication *);
%}
%token INCLUDE LISTEN ON PORT CLIENT SECRET LOAD MODULE MSGAUTH_REQUIRED
-%token ACCOUNT ACCOUNTING AUTHENTICATE AUTHENTICATE_BY BY DECORATE_BY QUICK
-%token SET TO ERROR YES NO
+%token ACCOUNT ACCOUNTING AUTHENTICATE AUTHENTICATE_BY AUTHENTICATION_FILTER
+%token BY DECORATE_BY QUICK SET TO ERROR YES NO
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> optport optacct
;
authenticate : AUTHENTICATE str_l BY STRING optdeco {
- int i;
struct radiusd_authentication *auth;
- struct radiusd_module_ref *modref, *modreft;
- if ((auth = calloc(1,
- sizeof(struct radiusd_authentication))) == NULL) {
- yyerror("Out of memory: %s", strerror(errno));
- goto authenticate_error;
- }
- if ((auth->auth = create_module_ref($4)) == NULL)
- goto authenticate_error;
- auth->username = $2.v;
- TAILQ_INIT(&auth->deco);
- for (i = 0; i < $5.c; i++) {
- if ((modref = create_module_ref($5.v[i]))
- == NULL)
- goto authenticate_error;
- TAILQ_INSERT_TAIL(&auth->deco, modref, next);
- }
- TAILQ_INSERT_TAIL(&conf->authen, auth, next);
- auth = NULL;
- authenticate_error:
- if (auth != NULL) {
- free(auth->auth);
- TAILQ_FOREACH_SAFE(modref, &auth->deco, next,
- modreft) {
- TAILQ_REMOVE(&auth->deco, modref, next);
- free(modref);
- }
+ auth = create_authen($4, $2.v, $5.c, $5.v);
+ free($4);
+ free_str_l(&$5);
+ if (auth == NULL) {
free_str_l(&$2);
- }
- free(auth);
+ YYERROR;
+ } else
+ TAILQ_INSERT_TAIL(&conf->authen, auth, next);
+ }
+ | AUTHENTICATION_FILTER str_l BY STRING optdeco {
+ struct radiusd_authentication *auth;
+
+ auth = create_authen($4, $2.v, $5.c, $5.v);
free($4);
free_str_l(&$5);
+ if (auth == NULL) {
+ free_str_l(&$2);
+ YYERROR;
+ } else {
+ auth->isfilter = true;
+ TAILQ_INSERT_TAIL(&conf->authen, auth, next);
+ }
}
/* the followings are for backward compatibilities */
| AUTHENTICATE str_l optnl '{' {
{ "accounting", ACCOUNTING},
{ "authenticate", AUTHENTICATE},
{ "authenticate-by", AUTHENTICATE_BY},
+ { "authentication-filter", AUTHENTICATION_FILTER},
{ "by", BY},
{ "client", CLIENT},
{ "decorate-by", DECORATE_BY},
return (errors ? -1 : 0);
}
+static struct radiusd_authentication *
+create_authen(const char *byname, char **username, int decoc, char **deco)
+{
+ int i;
+ struct radiusd_authentication *auth;
+ struct radiusd_module_ref *modref, *modreft;
+
+ if ((auth = calloc(1, sizeof(struct radiusd_authentication)))
+ == NULL) {
+ yyerror("Out of memory: %s", strerror(errno));
+ return (NULL);
+ }
+ if ((auth->auth = create_module_ref(byname)) == NULL)
+ goto on_error;
+
+ auth->username = username;
+ TAILQ_INIT(&auth->deco);
+ for (i = 0; i < decoc; i++) {
+ if ((modref = create_module_ref(deco[i])) == NULL)
+ goto on_error;
+ TAILQ_INSERT_TAIL(&auth->deco, modref, next);
+ }
+ return (auth);
+ on_error:
+ TAILQ_FOREACH_SAFE(modref, &auth->deco, next, modreft) {
+ TAILQ_REMOVE(&auth->deco, modref, next);
+ free(modref);
+ }
+ free(auth);
+ return (NULL);
+}
+
static struct radiusd_module *
find_module(const char *name)
{
-/* $OpenBSD: radiusd.c,v 1.48 2024/07/14 13:36:44 yasuoka Exp $ */
+/* $OpenBSD: radiusd.c,v 1.49 2024/07/14 15:27:57 yasuoka Exp $ */
/*
* Copyright (c) 2013, 2023 Internet Initiative Japan Inc.
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 void radius_query_accounting_response(
+ struct radius_query *);
+static const char *radius_query_client_secret(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 int radiusd_access_response_fixup(struct radius_query *,
+ struct radius_query *, bool);
static void radiusd_module_reset_ev_handler(
struct radiusd_module *);
static int radiusd_module_imsg_read(struct radiusd_module *);
struct radius_query *);
static void radiusd_module_access_request(struct radiusd_module *,
struct radius_query *);
+static void radiusd_module_next_response(struct radiusd_module *,
+ struct radius_query *, RADIUS_PACKET *);
static void radiusd_module_request_decoration(
struct radiusd_module *, struct radius_query *);
static void radiusd_module_response_decoration(
static void
radius_query_access_response(struct radius_query *q)
{
- int sz, res_id, res_code;
- char buf[NI_MAXHOST + NI_MAXSERV + 30];
+ int sz, res_id, res_code;
+ char buf[NI_MAXHOST + NI_MAXSERV + 30];
+ struct radius_query *q_last, *q0;
+ q_last = q;
+ next:
/* first or next response decoration */
for (;;) {
if (q->deco == NULL)
return;
}
- if (radiusd_access_response_fixup(q) != 0)
+ if (q->prev != NULL) {
+ if (MODULE_DO_NEXTRES(q->prev->authen->auth->module)) {
+ if (radiusd_access_response_fixup(q->prev, q_last, 0)
+ != 0)
+ goto on_error;
+ q0 = q;
+ q = q->prev;
+ radiusd_module_next_response(q->authen->auth->module,
+ q, q_last->res);
+ q0->prev = NULL;
+ radiusd_access_request_aborted(q0);
+ return;
+ }
+ q = q->prev;
+ goto next;
+ }
+
+ if (radiusd_access_response_fixup(q, q_last, 1) != 0)
goto on_error;
res_id = radius_get_id(q->res);
(struct sockaddr *)&q->clientaddr, q->clientaddrlen)) <= 0)
log_warn("Sending a RADIUS response failed");
}
+
+static const char *
+radius_query_client_secret(struct radius_query *q)
+{
+ struct radius_query *q0;
+ const char *client_secret = NULL;
+
+ for (q0 = q; q0 != NULL && client_secret == NULL; q0 = q0->prev) {
+ if (q0->client != NULL)
+ client_secret = q0->client->secret;
+ }
+ RADIUSD_ASSERT(client_secret != NULL);
+
+ return (client_secret);
+}
/***********************************************************************
* Callback functions from the modules
***********************************************************************/
radiusd_access_request_aborted(q);
}
+void
+radiusd_access_request_next(struct radius_query *q, RADIUS_PACKET *pkt)
+{
+ struct radius_query *q_next = NULL;
+ static char username[256];
+ struct radiusd_authentication *authen;
+ int i;
+
+ RADIUSD_ASSERT(q->deco == NULL);
+
+ if (!q->authen->isfilter) {
+ log_warnx("q=%u `%s' requested next authentication, but it's "
+ "not authentication-filter", q->id,
+ q->authen->auth->module->name);
+ goto on_error;
+ }
+ if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, username,
+ sizeof(username)) != 0)
+ username[0] = '\0';
+
+ for (authen = TAILQ_NEXT(q->authen, next); authen != NULL;
+ authen = TAILQ_NEXT(authen, next)) {
+ for (i = 0; authen->username[i] != NULL; i++) {
+ if (fnmatch(authen->username[i], username, 0)
+ == 0)
+ goto found;
+ }
+ }
+ found:
+ if (authen == NULL) { /* no more authentication */
+ log_warnx("q=%u module `%s' requested next authentication "
+ "no more `authenticate' matches", q->id,
+ q->authen->auth->module->name);
+ goto on_error;
+ }
+
+ if ((q_next = calloc(1, sizeof(struct radius_query))) == NULL) {
+ log_warn("%s: q=%u calloc: %m", __func__, q->id);
+ goto on_error;
+ }
+ q_next->id = ++radius_query_id_seq;
+ q_next->radiusd = q->radiusd;
+ q_next->req_id = q->req_id;
+ q_next->req = pkt;
+ radius_get_authenticator(pkt, q_next->req_auth);
+ q_next->authen = authen;
+ q_next->prev = q;
+ strlcpy(q_next->username, username, sizeof(q_next->username));
+ TAILQ_INSERT_TAIL(&q->radiusd->query, q_next, next);
+
+ raidus_query_access_request(q_next);
+ return;
+ on_error:
+ RADIUSD_ASSERT(q_next == NULL);
+ radius_delete_packet(pkt);
+ radiusd_access_request_aborted(q);
+}
+
void
radiusd_access_request_aborted(struct radius_query *q)
{
+ if (q->prev != NULL)
+ radiusd_access_request_aborted(q->prev);
if (q->req != NULL)
radius_delete_packet(q->req);
if (q->res != NULL)
* Fix some attributes which depend the secret value.
*/
static int
-radiusd_access_response_fixup(struct radius_query *q)
+radiusd_access_response_fixup(struct radius_query *q, struct radius_query *q0,
+ bool islast)
{
- int res_id;
- size_t attrlen;
- u_char req_auth[16], attrbuf[256];
- const char *authen_secret = q->authen->auth->module->secret;
-
- radius_get_authenticator(q->req, req_auth);
-
- if ((authen_secret != NULL &&
- strcmp(authen_secret, q->client->secret) != 0) ||
- timingsafe_bcmp(q->req_auth, req_auth, 16) != 0) {
- const char *olds = q->client->secret;
- const char *news = authen_secret;
-
- if (news == NULL)
- news = olds;
-
+ int res_id;
+ size_t attrlen;
+ u_char authen_req_auth[16], attrbuf[256];
+ const char *client_req_auth;
+ const char *authen_secret, *client_secret;
+
+ authen_secret = q0->authen->auth->module->secret;
+ client_secret = (islast)? q->client->secret :
+ q->authen->auth->module->secret;
+
+ radius_get_authenticator(q0->req, authen_req_auth);
+ client_req_auth = q->req_auth;
+
+ if (client_secret == NULL && authen_secret == NULL)
+ return (0);
+ if (!(authen_secret != NULL && client_secret != NULL &&
+ strcmp(authen_secret, client_secret) == 0 &&
+ timingsafe_bcmp(authen_req_auth, client_req_auth, 16) == 0)) {
/* RFC 2865 Tunnel-Password */
attrlen = sizeof(attrbuf);
- if (radius_get_raw_attr(q->res, RADIUS_TYPE_TUNNEL_PASSWORD,
+ if (radius_get_raw_attr(q0->res, RADIUS_TYPE_TUNNEL_PASSWORD,
attrbuf, &attrlen) == 0) {
- radius_attr_unhide(news, req_auth,
- attrbuf, attrbuf + 3, attrlen - 3);
- radius_attr_hide(olds, q->req_auth,
- attrbuf, attrbuf + 3, attrlen - 3);
-
- radius_del_attr_all(q->res,
+ if (authen_secret != NULL)
+ radius_attr_unhide(authen_secret,
+ authen_req_auth, attrbuf, attrbuf + 3,
+ attrlen - 3);
+ if (client_secret != NULL)
+ radius_attr_hide(client_secret, client_req_auth,
+ attrbuf, attrbuf + 3, attrlen - 3);
+ radius_del_attr_all(q0->res,
RADIUS_TYPE_TUNNEL_PASSWORD);
- radius_put_raw_attr(q->res,
+ radius_put_raw_attr(q0->res,
RADIUS_TYPE_TUNNEL_PASSWORD, attrbuf, attrlen);
}
/* RFC 2548 Microsoft MPPE-{Send,Recv}-Key */
attrlen = sizeof(attrbuf);
- if (radius_get_vs_raw_attr(q->res, RADIUS_VENDOR_MICROSOFT,
+ if (radius_get_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MPPE_SEND_KEY, attrbuf, &attrlen) == 0) {
-
- /* Re-crypt the KEY */
- radius_attr_unhide(news, req_auth,
- attrbuf, attrbuf + 2, attrlen - 2);
- radius_attr_hide(olds, q->req_auth,
- attrbuf, attrbuf + 2, attrlen - 2);
-
- radius_del_vs_attr_all(q->res, RADIUS_VENDOR_MICROSOFT,
+ if (authen_secret != NULL)
+ radius_attr_unhide(authen_secret,
+ authen_req_auth, attrbuf, attrbuf + 2,
+ attrlen - 2);
+ if (client_secret != NULL)
+ radius_attr_hide(client_secret, client_req_auth,
+ attrbuf, attrbuf + 2, attrlen - 2);
+ radius_del_vs_attr_all(q0->res, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MPPE_SEND_KEY);
- radius_put_vs_raw_attr(q->res, RADIUS_VENDOR_MICROSOFT,
+ radius_put_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MPPE_SEND_KEY, attrbuf, attrlen);
}
attrlen = sizeof(attrbuf);
- if (radius_get_vs_raw_attr(q->res, RADIUS_VENDOR_MICROSOFT,
+ if (radius_get_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MPPE_RECV_KEY, attrbuf, &attrlen) == 0) {
-
- /* Re-crypt the KEY */
- radius_attr_unhide(news, req_auth,
- attrbuf, attrbuf + 2, attrlen - 2);
- radius_attr_hide(olds, q->req_auth,
- attrbuf, attrbuf + 2, attrlen - 2);
-
- radius_del_vs_attr_all(q->res, RADIUS_VENDOR_MICROSOFT,
+ if (authen_secret != NULL)
+ radius_attr_unhide(authen_secret,
+ authen_req_auth, attrbuf, attrbuf + 2,
+ attrlen - 2);
+ if (client_secret != NULL)
+ radius_attr_hide(client_secret, client_req_auth,
+ attrbuf, attrbuf + 2, attrlen - 2);
+
+ radius_del_vs_attr_all(q0->res, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MPPE_RECV_KEY);
- radius_put_vs_raw_attr(q->res, RADIUS_VENDOR_MICROSOFT,
+ radius_put_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MPPE_RECV_KEY, attrbuf, attrlen);
}
}
-
- res_id = radius_get_id(q->res);
+ res_id = radius_get_id(q0->res);
if (res_id != q->req_id) {
/* authentication server change the id */
- radius_set_id(q->res, q->req_id);
+ radius_set_id(q0->res, q->req_id);
}
return (0);
radius_put_string_attr(q->res,
RADIUS_TYPE_REPLY_MESSAGE, msg);
radius_set_response_authenticator(q->res,
- q->client->secret);
+ radius_query_client_secret(q));
radiusd_access_request_answer(q);
}
break;
}
case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER:
+ case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT:
case IMSG_RADIUSD_MODULE_REQDECO_DONE:
case IMSG_RADIUSD_MODULE_RESDECO_DONE:
{
case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER:
typestr = "ACCSREQ_ANSWER";
break;
+ case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT:
+ typestr = "ACCSREQ_NEXT";
+ break;
case IMSG_RADIUSD_MODULE_REQDECO_DONE:
typestr = "REQDECO_DONE";
break;
q->res = radpkt;
radiusd_access_request_answer(q);
break;
+ case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT:
+ if (radpkt == NULL) {
+ log_warnx("q=%u wrong pkt from module",
+ q->id);
+ radiusd_access_request_aborted(q);
+ break;
+ }
+ radiusd_access_request_next(q, radpkt);
+ break;
case IMSG_RADIUSD_MODULE_RESDECO_DONE:
if (q->deco == NULL || q->deco->type !=
IMSG_RADIUSD_MODULE_RESDECO) {
userpass.q_id = q->id;
if (radius_get_user_password_attr(q->req, userpass.pass,
- sizeof(userpass.pass), q->client->secret) == 0)
+ sizeof(userpass.pass), radius_query_client_secret(q)) == 0)
userpass.has_pass = true;
else
userpass.has_pass = false;
radiusd_module_access_request(struct radiusd_module *module,
struct radius_query *q)
{
- RADIUS_PACKET *radpkt;
- char pass[256];
+ RADIUS_PACKET *radpkt;
+ char pass[256];
if ((radpkt = radius_convert_packet(radius_get_data(q->req),
radius_get_length(q->req))) == NULL) {
radius_delete_packet(radpkt);
}
+static void
+radiusd_module_next_response(struct radiusd_module *module,
+ struct radius_query *q, RADIUS_PACKET *pkt)
+{
+ if (imsg_compose_radius_packet(&module->ibuf,
+ IMSG_RADIUSD_MODULE_NEXTRES, q->id, pkt) == -1) {
+ log_warn("q=%u Could not send NEXTRES to `%s'", q->id,
+ module->name);
+ radiusd_access_request_aborted(q);
+ }
+ radiusd_module_reset_ev_handler(module);
+}
+
static void
radiusd_module_request_decoration(struct radiusd_module *module,
struct radius_query *q)
-/* $OpenBSD: radiusd_module.c,v 1.18 2024/07/09 17:26:14 yasuoka Exp $ */
+/* $OpenBSD: radiusd_module.c,v 1.19 2024/07/14 15:27:57 yasuoka Exp $ */
/*
* Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
= NULL;
static void (*module_access_request) (void *, u_int, const u_char *,
size_t) = NULL;
+static void (*module_next_response) (void *, u_int, const u_char *,
+ size_t) = NULL;
static void (*module_request_decoration) (void *, u_int, const u_char *,
size_t) = NULL;
static void (*module_response_decoration) (void *, u_int, const u_char *,
module_userpass = handler->userpass;
module_access_request = handler->access_request;
+ module_next_response = handler->next_response;
module_config_set = handler->config_set;
module_request_decoration = handler->request_decoration;
module_response_decoration = handler->response_decoration;
load.cap |= RADIUSD_MODULE_CAP_USERPASS;
if (module_access_request != NULL)
load.cap |= RADIUSD_MODULE_CAP_ACCSREQ;
+ if (module_next_response != NULL)
+ load.cap |= RADIUSD_MODULE_CAP_NEXTRES;
if (module_request_decoration != NULL)
load.cap |= RADIUSD_MODULE_CAP_REQDECO;
if (module_response_decoration != NULL)
q_id, pkt, pktlen));
}
+int
+module_accsreq_next(struct module_base *base, u_int q_id, const u_char *pkt,
+ size_t pktlen)
+{
+ return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_NEXT,
+ q_id, pkt, pktlen));
+}
+
int
module_accsreq_aborted(struct module_base *base, u_int q_id)
{
break;
}
case IMSG_RADIUSD_MODULE_ACCSREQ:
+ case IMSG_RADIUSD_MODULE_NEXTRES:
case IMSG_RADIUSD_MODULE_REQDECO:
case IMSG_RADIUSD_MODULE_RESDECO0_REQ:
case IMSG_RADIUSD_MODULE_RESDECO:
break;
}
typestr = "ACCSREQ";
+ } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES) {
+ if (module_next_response == NULL) {
+ syslog(LOG_ERR, "Received NEXTRES message, but "
+ "module doesn't support");
+ break;
+ }
+ typestr = "NEXTRES";
} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCTREQ) {
if (module_accounting_request == NULL) {
syslog(LOG_ERR, "Received ACCTREQ message, but "
if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ)
module_access_request(base->ctx, accessreq->q_id,
base->radpkt, base->radpktoff);
+ else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES)
+ module_next_response(base->ctx, accessreq->q_id,
+ base->radpkt, base->radpktoff);
else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO)
module_request_decoration(base->ctx, accessreq->q_id,
base->radpkt, base->radpktoff);