From: yasuoka Date: Fri, 8 Sep 2023 05:56:22 +0000 (+0000) Subject: Add request or response decoration feature which is used through the X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=237e61d9d5611da4b6b841edf6be6475fcd05b21;p=openbsd Add request or response decoration feature which is used through the radiusd module interface. This makes additional modules can modify RADIUS request or response messages. Also add new "radius_standard" module which uses this new feature, provides some generic features like "strip-atmark-realm" which removes the realm part from the User-Name attribute. from IIJ. --- diff --git a/usr.sbin/radiusd/Makefile b/usr.sbin/radiusd/Makefile index 315e14901b7..cdfb12a2ee9 100644 --- a/usr.sbin/radiusd/Makefile +++ b/usr.sbin/radiusd/Makefile @@ -1,6 +1,7 @@ -# $OpenBSD: Makefile,v 1.1 2015/07/21 04:06:04 yasuoka Exp $ +# $OpenBSD: Makefile,v 1.2 2023/09/08 05:56:22 yasuoka Exp $ SUBDIR= radiusd SUBDIR+= radiusd_bsdauth SUBDIR+= radiusd_radius +SUBDIR+= radiusd_standard .include diff --git a/usr.sbin/radiusd/parse.y b/usr.sbin/radiusd/parse.y index ef17ae1e752..a382dd49995 100644 --- a/usr.sbin/radiusd/parse.y +++ b/usr.sbin/radiusd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.16 2023/09/05 00:32:01 yasuoka Exp $ */ +/* $OpenBSD: parse.y,v 1.17 2023/09/08 05:56:22 yasuoka Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -349,7 +349,6 @@ authopt : AUTHENTICATE_BY STRING { YYERROR; authen.auth = modref; } - /* XXX decoration doesn't work for this moment. */ | DECORATE_BY str_l { int i; struct radiusd_module_ref *modref; diff --git a/usr.sbin/radiusd/radiusd.c b/usr.sbin/radiusd/radiusd.c index 8e63f9783e9..22fd6b50572 100644 --- a/usr.sbin/radiusd/radiusd.c +++ b/usr.sbin/radiusd/radiusd.c @@ -1,7 +1,7 @@ -/* $OpenBSD: radiusd.c,v 1.31 2023/09/05 00:32:01 yasuoka Exp $ */ +/* $OpenBSD: radiusd.c,v 1.32 2023/09/08 05:56:22 yasuoka Exp $ */ /* - * Copyright (c) 2013 Internet Initiative Japan Inc. + * Copyright (c) 2013, 2023 Internet Initiative Japan Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,13 +17,14 @@ */ #include -#include +#include +#include #include +#include +#include #include #include -#include -#include #include #include #include @@ -40,7 +41,6 @@ #include #include #include -#include #include @@ -58,9 +58,8 @@ static void radiusd_on_sigterm(int, short, void *); static void radiusd_on_sigint(int, short, void *); static void radiusd_on_sighup(int, short, void *); static void radiusd_on_sigchld(int, short, void *); -static int radius_query_request_decoration(struct radius_query *); -static int radius_query_response_decoration( - struct radius_query *); +static void radius_query_request(struct radius_query *); +static void radius_query_response(struct radius_query *); static const char *radius_code_string(int); static int radiusd_access_response_fixup (struct radius_query *); @@ -84,6 +83,12 @@ static void radiusd_module_userpass(struct radiusd_module *, struct radius_query *); static void radiusd_module_access_request(struct radiusd_module *, struct radius_query *); +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 int imsg_compose_radius_packet(struct imsgbuf *, + uint32_t, u_int, RADIUS_PACKET *); static u_int radius_query_id_seq = 0; int debug = 0; @@ -482,6 +487,7 @@ radiusd_listen_on_event(int fd, short evmask, void *ctx) goto found; } } +found: if (authen == NULL) { log_warnx("Received %s(code=%d) from %s id=%d " "username=%s: no `authenticate' matches.", @@ -489,7 +495,6 @@ radiusd_listen_on_event(int fd, short evmask, void *ctx) req_id, username); goto on_error; } -found: RADIUSD_ASSERT(authen->auth != NULL); if (!MODULE_DO_USERPASS(authen->auth->module) && @@ -515,25 +520,13 @@ found: q->req_id = req_id; radius_get_authenticator(packet, q->req_auth); - if (radius_query_request_decoration(q) != 0) { - log_warnx( - "Received %s(code=%d) from %s id=%d username=%s " - "q=%u: failed to decorate the request", - radius_code_string(req_code), req_code, peerstr, - q->req_id, q->username, q->id); - radiusd_access_request_aborted(q); - return; - } log_info("Received %s(code=%d) from %s id=%d username=%s " "q=%u: `%s' authentication is starting", radius_code_string(req_code), req_code, peerstr, q->req_id, q->username, q->id, q->authen->auth->module->name); TAILQ_INSERT_TAIL(&listn->radiusd->query, q, next); - if (MODULE_DO_ACCSREQ(authen->auth->module)) { - radiusd_module_access_request(authen->auth->module, q); - } else if (MODULE_DO_USERPASS(authen->auth->module)) - radiusd_module_userpass(authen->auth->module, q); + radius_query_request(q); return; } @@ -546,40 +539,77 @@ on_error: return; } -static int -radius_query_request_decoration(struct radius_query *q) +static void +radius_query_request(struct radius_query *q) { - struct radiusd_module_ref *deco; - - TAILQ_FOREACH(deco, &q->authen->deco, next) { - /* XXX decoration doesn't work for this moment. */ - if (deco->module->request_decoration != NULL && - deco->module->request_decoration(NULL, q) != 0) { - log_warnx("q=%u request decoration `%s' failed", q->id, - deco->module->name); - return (-1); - } + struct radiusd_authentication *authen = q->authen; + + /* first or next request decoration */ + for (;;) { + if (q->deco == NULL) + q->deco = TAILQ_FIRST(&q->authen->deco); + else + q->deco = TAILQ_NEXT(q->deco, next); + if (q->deco == NULL || MODULE_DO_REQDECO(q->deco->module)) + break; } - return (0); + if (q->deco != NULL) + radiusd_module_request_decoration(q->deco->module, q); + else { + RADIUSD_ASSERT(authen->auth != NULL); + if (MODULE_DO_ACCSREQ(authen->auth->module)) + radiusd_module_access_request(authen->auth->module, q); + else if (MODULE_DO_USERPASS(authen->auth->module)) + radiusd_module_userpass(authen->auth->module, q); + } } -static int -radius_query_response_decoration(struct radius_query *q) +static void +radius_query_response(struct radius_query *q) { - struct radiusd_module_ref *deco; - - TAILQ_FOREACH(deco, &q->authen->deco, next) { - /* XXX decoration doesn't work for this moment. */ - if (deco->module->response_decoration != NULL && - deco->module->response_decoration(NULL, q) != 0) { - log_warnx("q=%u response decoration `%s' failed", q->id, - deco->module->name); - return (-1); - } + int sz, res_id, res_code; + char buf[NI_MAXHOST + NI_MAXSERV + 30]; + + /* first or next response decoration */ + for (;;) { + if (q->deco == NULL) + q->deco = TAILQ_FIRST(&q->authen->deco); + else + q->deco = TAILQ_NEXT(q->deco, next); + if (q->deco == NULL || MODULE_DO_RESDECO(q->deco->module)) + break; } - return (0); + if (q->deco != NULL) { + radiusd_module_response_decoration(q->deco->module, q); + return; + } + + if (radiusd_access_response_fixup(q) != 0) + goto on_error; + + res_id = radius_get_id(q->res); + res_code = radius_get_code(q->res); + + /* Reset response/message authenticator */ + if (radius_has_attr(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) + radius_del_attr_all(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR); + radius_put_message_authenticator(q->res, q->client->secret); + radius_set_response_authenticator(q->res, q->client->secret); + + 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"); +on_error: + radiusd_access_request_aborted(q); + } /*********************************************************************** @@ -588,15 +618,13 @@ radius_query_response_decoration(struct radius_query *q) void radiusd_access_request_answer(struct radius_query *q) { - int sz, res_id, res_code; - char buf[NI_MAXHOST + NI_MAXSERV + 30]; const char *authen_secret = q->authen->auth->module->secret; radius_set_request_packet(q->res, q->req); if (authen_secret == NULL) { /* - * The module couldn't check the authenticators + * The module diddn't check the authenticators */ if (radius_check_response_authenticator(q->res, q->client->secret) != 0) { @@ -614,31 +642,10 @@ radiusd_access_request_answer(struct radius_query *q) } } - /* Decorate the response */ - if (radius_query_response_decoration(q) != 0) - goto on_error; - - if (radiusd_access_response_fixup(q) != 0) - goto on_error; - - res_id = radius_get_id(q->res); - res_code = radius_get_code(q->res); - - /* Reset response/message authenticator */ - if (radius_has_attr(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) - radius_del_attr_all(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR); - radius_put_message_authenticator(q->res, q->client->secret); - radius_set_response_authenticator(q->res, q->client->secret); + RADIUSD_ASSERT(q->deco == NULL); + radius_query_response(q); - 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"); + return; on_error: radiusd_access_request_aborted(q); } @@ -754,23 +761,6 @@ radiusd_conf_init(struct radiusd *conf) TAILQ_INIT(&conf->authen); TAILQ_INIT(&conf->client); - /* - * TODO: load the standard modules - */ -#if 0 -static struct radiusd_module *radiusd_standard_modules[] = { - NULL -}; - - u_int i; - struct radiusd_module *module; - for (i = 0; radiusd_standard_modules[i] != NULL; i++) { - module = radiusd_create_module_class( - radiusd_standard_modules[i]); - TAILQ_INSERT_TAIL(&conf->module, module, next); - } -#endif - return; } @@ -793,6 +783,9 @@ radiusd_access_response_fixup(struct radius_query *q) const char *olds = q->client->secret; const char *news = authen_secret; + if (news == NULL) + news = olds; + /* RFC 2865 Tunnel-Password */ attrlen = sizeof(attrlen); if (radius_get_raw_attr(q->res, RADIUS_TYPE_TUNNEL_PASSWORD, @@ -1238,28 +1231,77 @@ radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg) break; } case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER: + case IMSG_RADIUSD_MODULE_REQDECO_DONE: + case IMSG_RADIUSD_MODULE_RESDECO_DONE: { static struct radiusd_module_radpkt_arg *ans; + const char *typestr = "unknown"; + + switch (imsg->hdr.type) { + case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER: + typestr = "ACCSREQ_ANSWER"; + break; + case IMSG_RADIUSD_MODULE_REQDECO_DONE: + typestr = "REQDECO_DONE"; + break; + case IMSG_RADIUSD_MODULE_RESDECO_DONE: + typestr = "RESDECO_DONE"; + break; + } + if (datalen < (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) { - log_warnx("Received ACCSREQ_ANSWER message, but " - "length is wrong"); + log_warnx("Received %s message, but length is wrong", + typestr); break; } q_id = ((struct radiusd_module_radpkt_arg *)imsg->data)->q_id; q = radiusd_find_query(module->radiusd, q_id); if (q == NULL) { - log_warnx("Received ACCSREQ_ANSWER from %s, but query " - "id=%u unknown", module->name, q_id); + log_warnx("Received %s from %s, but query id=%u " + "unknown", typestr, module->name, q_id); break; } if ((ans = radiusd_module_recv_radpkt(module, imsg, - IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER, - "ACCSREQ_ANSWER")) != NULL) { - q->res = radius_convert_packet( - module->radpkt, module->radpktoff); - radiusd_access_request_answer(q); + imsg->hdr.type, typestr)) != NULL) { + RADIUS_PACKET *radpkt = NULL; + + if (module->radpktoff > 0 && + (radpkt = radius_convert_packet( + module->radpkt, module->radpktoff)) == NULL) { + log_warn("q=%u radius_convert_packet() failed", + q->id); + radiusd_access_request_aborted(q); + break; + } module->radpktoff = 0; + switch (imsg->hdr.type) { + case IMSG_RADIUSD_MODULE_REQDECO_DONE: + if (radpkt != NULL) { + radius_delete_packet(q->req); + q->req = radpkt; + } + radius_query_request(q); + break; + case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER: + if (radpkt == NULL) { + log_warn("q=%u wrong pkt from module", + q->id); + radiusd_access_request_aborted(q); + } + q->res = radpkt; + radiusd_access_request_answer(q); + break; + case IMSG_RADIUSD_MODULE_RESDECO_DONE: + if (radpkt != NULL) { + radius_delete_packet(q->res); + radius_set_request_packet(radpkt, + q->req); + q->res = radpkt; + } + radius_query_response(q); + break; + } } break; } @@ -1276,8 +1318,8 @@ radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg) break; } default: - RADIUSD_DBG(("Unhandled imsg type=%d", - imsg->hdr.type)); + RADIUSD_DBG(("Unhandled imsg type=%d from %s", imsg->hdr.type, + module->name)); } } @@ -1306,9 +1348,11 @@ radiusd_module_recv_radpkt(struct radiusd_module *module, struct imsg *imsg, "received length is too big", type_str, module->name); goto on_fail; } - memcpy(module->radpkt + module->radpktoff, - (caddr_t)(ans + 1), chunklen); - module->radpktoff += chunklen; + if (chunklen > 0) { + memcpy(module->radpkt + module->radpktoff, + (caddr_t)(ans + 1), chunklen); + module->radpktoff += chunklen; + } if (!ans->final) return (NULL); /* again */ if (module->radpktoff != ans->pktlen) { @@ -1427,11 +1471,9 @@ radiusd_module_userpass(struct radiusd_module *module, struct radius_query *q) userpass.has_pass = true; else userpass.has_pass = false; - - if (strlcpy(userpass.user, q->username, sizeof(userpass.user)) - >= sizeof(userpass.user)) { - log_warnx("Could request USERPASS to module `%s': " - "User-Name too long", module->name); + if (radius_get_string_attr(q->req, RADIUS_TYPE_USER_NAME, + userpass.user, sizeof(userpass.user)) != 0) { + log_warnx("q=%u no User-Name attribute", q->id); goto on_error; } imsg_compose(&module->ibuf, IMSG_RADIUSD_MODULE_USERPASS, 0, 0, -1, @@ -1446,16 +1488,14 @@ static void radiusd_module_access_request(struct radiusd_module *module, struct radius_query *q) { - struct radiusd_module_radpkt_arg accsreq; - struct iovec iov[2]; - int off = 0, len, siz; - const u_char *pkt; RADIUS_PACKET *radpkt; char pass[256]; if ((radpkt = radius_convert_packet(radius_get_data(q->req), radius_get_length(q->req))) == NULL) { - log_warn("Could not send ACCSREQ for `%s'", module->name); + log_warn("q=%u Could not send ACCSREQ to `%s'", q->id, + module->name); + radiusd_access_request_aborted(q); return; } if (q->client->secret[0] != '\0' && module->secret != NULL && @@ -1465,30 +1505,85 @@ radiusd_module_access_request(struct radiusd_module *module, (void)radius_put_raw_attr(radpkt, RADIUS_TYPE_USER_PASSWORD, pass, strlen(pass)); } + if (imsg_compose_radius_packet(&module->ibuf, + IMSG_RADIUSD_MODULE_ACCSREQ, q->id, radpkt) == -1) { + log_warn("q=%u Could not send ACCSREQ to `%s'", q->id, + module->name); + radiusd_access_request_aborted(q); + } + radiusd_module_reset_ev_handler(module); + radius_delete_packet(radpkt); +} + +static void +radiusd_module_request_decoration(struct radiusd_module *module, + struct radius_query *q) +{ + if (module->fd < 0) { + log_warnx("q=%u Could not send REQDECO to `%s': module is " + "not running?", q->id, module->name); + radiusd_access_request_aborted(q); + return; + } + if (imsg_compose_radius_packet(&module->ibuf, + IMSG_RADIUSD_MODULE_REQDECO, q->id, q->req) == -1) { + log_warn("q=%u Could not send REQDECO to `%s'", q->id, + module->name); + radiusd_access_request_aborted(q); + return; + } + radiusd_module_reset_ev_handler(module); +} + +static void +radiusd_module_response_decoration(struct radiusd_module *module, + struct radius_query *q) +{ + if (module->fd < 0) { + log_warnx("q=%u Could not send RESDECO to `%s': module is " + "not running?", q->id, module->name); + radiusd_access_request_aborted(q); + return; + } + if (imsg_compose_radius_packet(&module->ibuf, + IMSG_RADIUSD_MODULE_RESDECO, q->id, q->res) == -1) { + log_warn("q=%u Could not send RESDECO to `%s'", q->id, + module->name); + radiusd_access_request_aborted(q); + return; + } + radiusd_module_reset_ev_handler(module); +} + +static int +imsg_compose_radius_packet(struct imsgbuf *ibuf, uint32_t type, u_int q_id, + RADIUS_PACKET *radpkt) +{ + struct radiusd_module_radpkt_arg arg; + int off = 0, len, siz; + struct iovec iov[2]; + const u_char *pkt; pkt = radius_get_data(radpkt); len = radius_get_length(radpkt); - memset(&accsreq, 0, sizeof(accsreq)); - accsreq.q_id = q->id; - accsreq.pktlen = len; + memset(&arg, 0, sizeof(arg)); + arg.q_id = q_id; + arg.pktlen = len; while (off < len) { - siz = MAX_IMSGSIZE - sizeof(accsreq); + siz = MAX_IMSGSIZE - sizeof(arg); if (len - off > siz) - accsreq.final = false; + arg.final = false; else { - accsreq.final = true; + arg.final = true; siz = len - off; } - iov[0].iov_base = &accsreq; - iov[0].iov_len = sizeof(accsreq); + iov[0].iov_base = &arg; + iov[0].iov_len = sizeof(arg); iov[1].iov_base = (caddr_t)pkt + off; iov[1].iov_len = siz; - imsg_composev(&module->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ, 0, 0, - -1, iov, 2); + if (imsg_composev(ibuf, type, 0, 0, -1, iov, 2) == -1) + return (-1); off += siz; } - radiusd_module_reset_ev_handler(module); - radius_delete_packet(radpkt); - - return; + return (0); } diff --git a/usr.sbin/radiusd/radiusd.conf.5 b/usr.sbin/radiusd/radiusd.conf.5 index b2bd3f9d0ce..f1271f1ca32 100644 --- a/usr.sbin/radiusd/radiusd.conf.5 +++ b/usr.sbin/radiusd/radiusd.conf.5 @@ -1,7 +1,7 @@ -.\" $OpenBSD: radiusd.conf.5,v 1.16 2023/09/04 12:28:18 yasuoka Exp $ +.\" $OpenBSD: radiusd.conf.5,v 1.17 2023/09/08 05:56:22 yasuoka Exp $ .\" .\" Copyright (c) 2014 Esdenera Networks GmbH -.\" Copyright (c) 2014 Internet Initiative Japan Inc. +.\" Copyright (c) 2014, 2023 Internet Initiative Japan Inc. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,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: September 4 2023 $ +.Dd $Mdocdate: September 8 2023 $ .Dt RADIUSD.CONF 5 .Os .Sh NAME @@ -68,6 +68,7 @@ The following modules are available: .It Sy "Path" Ta Sy "Description" .It Pa /usr/libexec/radiusd/radiusd_bsdauth Ta Do bsdauth Dc module .It Pa /usr/libexec/radiusd/radiusd_radius Ta Do radius Dc module +.It Pa /usr/libexec/radiusd/radiusd_standard Ta Do standard Dc module .El .Bl -tag -width Ds .It Do bsdauth Dc module @@ -83,6 +84,11 @@ It only supports PAP, password based authentication. The .Dq radius module provides authentication from upstream RADIUS servers. +.It Do standard Dc module +The +.Dq standard +module provides standard decorations for Access-Request messages or its +response messages. .El .It Ic module set Ar module key value ... Configure the module specific configurations by @@ -146,6 +152,34 @@ and .Ar max-failover will not be used. .El +.Pp +The +.Dq standard +module supports the following configuration key and value: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Ic strip-atmark-realm Ar true | false +Remove the realm part which starts with @ +.Pq atmark +from the User-Name attribute of the Access-Request. +.Pp +.It Ic strip-nt-domain Ar true | false +Remove NT domain which ends with \\ +.Pq backslash +from the User-Name attribute of the Access-Request. +.Pp +.It Cm remove-request-attribute Oo Ar vendor Oc Ar type +.It Cm remove-response-attribute Oo Ar vendor Oc Ar type +Remove all the specified attributes from request or response +messages of Access-Request. +Specify +.Ar type +of the attribute in a decimal number. +To specify a vendor attribute, +specify the Vendor-Id +in a decimal number for +.Ar vendor . +.El .It Ic authenticate Ar username-pattern ... Brq ... Specify an authentication configuration for the users specified by .Ar username-pattern . @@ -162,6 +196,8 @@ It is followed by a block of options enclosed in curly brackets: .Bl -tag -width Ds .It Ic authenticate-by Ar module Specify the module name. +.It Ic decorate-by Ar module +Specify the module name. .El .El .Sh FILES @@ -182,28 +218,32 @@ module executable. .Sh EXAMPLES .Bd -literal -offset indent listen on 0.0.0.0 -#listen on :: +listen on :: client 127.0.0.1/32 { secret "secret" + msgauth-required no } client 192.168.0.0/24 { secret "secret" - msgauth-required yes } module load bsdauth "/usr/libexec/radiusd/radiusd_bsdauth" -module set bsdauth restrict-group "operator" +module set bsdauth restrict-group operator module load radius "/usr/libexec/radiusd/radiusd_radius" module set radius secret "testing123" module set radius server "127.0.0.1" -authenticate *@example.com { - authenticate-by radius +module load strip-realm "/usr/libexec/radiusd/radiusd_standard" +module set strip-realm strip-atmark-realm true + +authenticate *@local { + authenticate-by bsdauth + decorate-by strip-realm } authenticate * { - authenticate-by bsdauth + authenticate-by radius } .Ed .Sh SEE ALSO diff --git a/usr.sbin/radiusd/radiusd.h b/usr.sbin/radiusd/radiusd.h index 3d2676e8a54..87ea9dd79c8 100644 --- a/usr.sbin/radiusd/radiusd.h +++ b/usr.sbin/radiusd/radiusd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: radiusd.h,v 1.4 2019/04/03 11:54:56 yasuoka Exp $ */ +/* $OpenBSD: radiusd.h,v 1.5 2023/09/08 05:56:22 yasuoka Exp $ */ #ifndef RADIUSD_H #define RADIUSD_H 1 @@ -17,6 +17,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include #include #define RADIUSD_MODULE_NAME_LEN 32 @@ -38,6 +40,10 @@ enum imsg_type { /* Check the response's authenticator if the module doesn't */ IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED, + IMSG_RADIUSD_MODULE_REQDECO, + IMSG_RADIUSD_MODULE_REQDECO_DONE, + IMSG_RADIUSD_MODULE_RESDECO, + IMSG_RADIUSD_MODULE_RESDECO_DONE, IMSG_RADIUSD_MODULE_STOP }; @@ -46,6 +52,8 @@ 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 }; struct radiusd_module_object { diff --git a/usr.sbin/radiusd/radiusd_local.h b/usr.sbin/radiusd/radiusd_local.h index 4c9f1d2fdd3..2704ecc27fd 100644 --- a/usr.sbin/radiusd/radiusd_local.h +++ b/usr.sbin/radiusd/radiusd_local.h @@ -1,4 +1,4 @@ -/* $OpenBSD: radiusd_local.h,v 1.5 2019/04/01 11:05:41 yasuoka Exp $ */ +/* $OpenBSD: radiusd_local.h,v 1.6 2023/09/08 05:56:22 yasuoka Exp $ */ /* * Copyright (c) 2013 Internet Initiative Japan Inc. @@ -122,6 +122,7 @@ struct radius_query { int req_modified; char username[256]; /* original username */ TAILQ_ENTRY(radius_query) next; + struct radiusd_module_ref *deco; }; #ifndef nitems #define nitems(_x) (sizeof((_x)) / sizeof((_x)[0])) @@ -149,6 +150,12 @@ struct radius_query { #define MODULE_DO_ACCSREQ(_m) \ ((_m)->fd >= 0 && \ ((_m)->capabilities & RADIUSD_MODULE_CAP_ACCSREQ) != 0) +#define MODULE_DO_REQDECO(_m) \ + ((_m)->fd >= 0 && \ + ((_m)->capabilities & RADIUSD_MODULE_CAP_REQDECO) != 0) +#define MODULE_DO_RESDECO(_m) \ + ((_m)->fd >= 0 && \ + ((_m)->capabilities & RADIUSD_MODULE_CAP_RESDECO) != 0) extern struct radiusd_module mod_standard; extern struct radiusd_module mod_radius; diff --git a/usr.sbin/radiusd/radiusd_module.c b/usr.sbin/radiusd/radiusd_module.c index f38170af91b..02d9b524225 100644 --- a/usr.sbin/radiusd/radiusd_module.c +++ b/usr.sbin/radiusd/radiusd_module.c @@ -1,4 +1,4 @@ -/* $OpenBSD: radiusd_module.c,v 1.13 2019/06/28 13:32:49 deraadt Exp $ */ +/* $OpenBSD: radiusd_module.c,v 1.14 2023/09/08 05:56:22 yasuoka Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko @@ -46,6 +46,10 @@ static void (*module_userpass) (void *, u_int, const char *, const char *) = NULL; static void (*module_access_request) (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 *, + size_t) = NULL; struct module_base { void *ctx; @@ -73,7 +77,6 @@ static int module_imsg_handler(struct module_base *, struct imsg *); #ifdef USE_LIBEVENT static void module_on_event(int, short, void *); #endif -static void module_stop(struct module_base *); static void module_reset_event(struct module_base *); struct module_base * @@ -90,6 +93,8 @@ module_create(int sock, void *ctx, struct module_handlers *handler) module_userpass = handler->userpass; module_access_request = handler->access_request; module_config_set = handler->config_set; + module_request_decoration = handler->request_decoration; + module_response_decoration = handler->response_decoration; module_start_module = handler->start; module_stop_module = handler->stop; @@ -140,6 +145,10 @@ module_load(struct module_base *base) load.cap |= RADIUSD_MODULE_CAP_USERPASS; if (module_access_request != NULL) load.cap |= RADIUSD_MODULE_CAP_ACCSREQ; + if (module_request_decoration != NULL) + load.cap |= RADIUSD_MODULE_CAP_REQDECO; + if (module_response_decoration != NULL) + load.cap |= RADIUSD_MODULE_CAP_RESDECO; imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load, sizeof(load)); imsg_flush(&base->ibuf); @@ -260,6 +269,22 @@ module_accsreq_aborted(struct module_base *base, u_int q_id) return (ret); } +int +module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, + size_t pktlen) +{ + return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE, + q_id, pkt, pktlen)); +} + +int +module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, + size_t pktlen) +{ + return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE, + q_id, pkt, pktlen)); +} + static int module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id, const u_char *pkt, size_t pktlen) @@ -271,19 +296,22 @@ module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id, len = pktlen; ans.q_id = q_id; ans.pktlen = pktlen; - while (off < len) { + ans.final = false; + + while (!ans.final) { siz = MAX_IMSGSIZE - sizeof(ans); - if (len - off > siz) - ans.final = false; - else { + if (len - off <= siz) { ans.final = true; siz = len - off; } iov[0].iov_base = &ans; iov[0].iov_len = sizeof(ans); - iov[1].iov_base = (u_char *)pkt + off; - iov[1].iov_len = siz; - ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov, 2); + if (siz > 0) { + iov[1].iov_base = (u_char *)pkt + off; + iov[1].iov_len = siz; + } + ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov, + (siz > 0)? 2 : 1); if (ret == -1) break; off += siz; @@ -305,7 +333,6 @@ module_recv_imsg(struct module_base *base) module_stop(base); return (-1); } - for (;;) { if ((n = imsg_get(&base->ibuf, &imsg)) == -1) { syslog(LOG_ERR, "%s: imsg_get(): %m", __func__); @@ -410,19 +437,40 @@ module_imsg_handler(struct module_base *base, struct imsg *imsg) break; } case IMSG_RADIUSD_MODULE_ACCSREQ: + case IMSG_RADIUSD_MODULE_REQDECO: + case IMSG_RADIUSD_MODULE_RESDECO: { struct radiusd_module_radpkt_arg *accessreq; int chunklen; + const char *typestr; - if (module_access_request == NULL) { - syslog(LOG_ERR, "Received ACCSREQ message, but " - "module doesn't support"); - break; + if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) { + if (module_access_request == NULL) { + syslog(LOG_ERR, "Received ACCSREQ message, but " + "module doesn't support"); + break; + } + typestr = "ACCSREQ"; + } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) { + if (module_request_decoration == NULL) { + syslog(LOG_ERR, "Received REQDECO message, but " + "module doesn't support"); + break; + } + typestr = "REQDECO"; + } else { + if (module_response_decoration == NULL) { + syslog(LOG_ERR, "Received RESDECO message, but " + "module doesn't support"); + break; + } + typestr = "RESDECO"; } + if (datalen < (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) { - syslog(LOG_ERR, "Received ACCSREQ message, but " - "length is wrong"); + syslog(LOG_ERR, "Received %s message, but " + "length is wrong", typestr); break; } accessreq = (struct radiusd_module_radpkt_arg *)imsg->data; @@ -431,7 +479,7 @@ module_imsg_handler(struct module_base *base, struct imsg *imsg) if ((nradpkt = realloc(base->radpkt, accessreq->pktlen)) == NULL) { syslog(LOG_ERR, "Could not handle received " - "ACCSREQ message: %m"); + "%s message: %m", typestr); base->radpktoff = 0; goto accsreq_out; } @@ -441,8 +489,8 @@ module_imsg_handler(struct module_base *base, struct imsg *imsg) chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg); if (chunklen > base->radpktsiz - base->radpktoff){ syslog(LOG_ERR, - "Could not handle received ACCSREQ message: " - "received length is too big"); + "Could not handle received %s message: " + "received length is too big", typestr); base->radpktoff = 0; goto accsreq_out; } @@ -453,13 +501,20 @@ module_imsg_handler(struct module_base *base, struct imsg *imsg) goto accsreq_out; if (base->radpktoff != accessreq->pktlen) { syslog(LOG_ERR, - "Could not handle received ACCSREQ " - "message: length is mismatch"); + "Could not handle received %s " + "message: length is mismatch", typestr); base->radpktoff = 0; goto accsreq_out; } - module_access_request(base->ctx, accessreq->q_id, - base->radpkt, base->radpktoff); + 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_REQDECO) + module_request_decoration(base->ctx, accessreq->q_id, + base->radpkt, base->radpktoff); + else + module_response_decoration(base->ctx, accessreq->q_id, + base->radpkt, base->radpktoff); base->radpktoff = 0; accsreq_out: break; @@ -469,7 +524,7 @@ accsreq_out: return (0); } -static void +void module_stop(struct module_base *base) { if (module_stop_module != NULL) diff --git a/usr.sbin/radiusd/radiusd_module.h b/usr.sbin/radiusd/radiusd_module.h index 647cfcf7e6a..42380eac246 100644 --- a/usr.sbin/radiusd/radiusd_module.h +++ b/usr.sbin/radiusd/radiusd_module.h @@ -35,8 +35,13 @@ struct module_handlers { void (*access_request)(void *ctx, u_int query_id, const u_char *pkt, size_t pktlen); - /* User-Password Attribute is encrypted if the module has the secret */ + + void (*request_decoration)(void *ctx, u_int query_id, const u_char *pkt, + size_t pktlen); + + void (*response_decoration)(void *ctx, u_int query_id, + const u_char *pkt, size_t pktlen); }; #define SYNTAX_ASSERT(_cond, _msg) \ @@ -51,6 +56,7 @@ __BEGIN_DECLS struct module_base *module_create(int, void *, struct module_handlers *); void module_start(struct module_base *); +void module_stop(struct module_base *); int module_run(struct module_base *); void module_destroy(struct module_base *); void module_load(struct module_base *); @@ -67,6 +73,10 @@ int module_userpass_fail(struct module_base *, u_int, int module_accsreq_answer(struct module_base *, u_int, const u_char *, size_t); int module_accsreq_aborted(struct module_base *, u_int); +int module_reqdeco_done(struct module_base *, u_int, + const u_char *, size_t); +int module_resdeco_done(struct module_base *, u_int, + const u_char *, size_t); __END_DECLS diff --git a/usr.sbin/radiusd/radiusd_standard.c b/usr.sbin/radiusd/radiusd_standard.c new file mode 100644 index 00000000000..04052dfc50c --- /dev/null +++ b/usr.sbin/radiusd/radiusd_standard.c @@ -0,0 +1,298 @@ +/* $OpenBSD: radiusd_standard.c,v 1.1 2023/09/08 05:56:22 yasuoka Exp $ */ + +/* + * Copyright (c) 2013, 2023 Internet Initiative Japan Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "radiusd.h" +#include "radiusd_module.h" + +TAILQ_HEAD(attrs,attr); + +struct attr { + uint8_t type; + uint32_t vendor; + uint32_t vtype; + TAILQ_ENTRY(attr) next; +}; + +struct module_standard { + struct module_base *base; + bool strip_atmark_realm; + bool strip_nt_domain; + struct attrs remove_reqattrs; + struct attrs remove_resattrs; +}; + +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); + +int +main(int argc, char *argv[]) +{ + struct module_standard module_standard; + struct module_handlers handlers = { + .config_set = module_standard_config_set, + .request_decoration = module_standard_reqdeco, + .response_decoration = module_standard_resdeco + }; + struct attr *attr; + + memset(&module_standard, 0, sizeof(module_standard)); + TAILQ_INIT(&module_standard.remove_reqattrs); + TAILQ_INIT(&module_standard.remove_resattrs); + + if ((module_standard.base = module_create( + STDIN_FILENO, &module_standard, &handlers)) == NULL) + err(1, "Could not create a module instance"); + + module_drop_privilege(module_standard.base); + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + + module_load(module_standard.base); + + openlog(NULL, LOG_PID, LOG_DAEMON); + + while (module_run(module_standard.base) == 0) + ; + + module_destroy(module_standard.base); + while ((attr = TAILQ_FIRST(&module_standard.remove_reqattrs)) != NULL) { + TAILQ_REMOVE(&module_standard.remove_reqattrs, attr, next); + freezero(attr, sizeof(struct attr)); + } + while ((attr = TAILQ_FIRST(&module_standard.remove_resattrs)) != NULL) { + TAILQ_REMOVE(&module_standard.remove_resattrs, attr, next); + freezero(attr, sizeof(struct attr)); + } + + exit(EXIT_SUCCESS); +} + +static void +module_standard_config_set(void *ctx, const char *name, int argc, + char * const * argv) +{ + struct module_standard *module = ctx; + struct attr *attr; + const char *errmsg = "none"; + const char *errstr; + + if (strcmp(name, "strip-atmark-realm") == 0) { + SYNTAX_ASSERT(argc == 1, + "`strip-atmark-realm' must have only one argment"); + if (strcmp(argv[0], "true") == 0) + module->strip_atmark_realm = true; + else if (strcmp(argv[0], "false") == 0) + module->strip_atmark_realm = false; + else + SYNTAX_ASSERT(0, + "`strip-atmark-realm' must `true' or `false'"); + } else if (strcmp(name, "strip-nt-domain") == 0) { + SYNTAX_ASSERT(argc == 1, + "`strip-nt-domain' must have only one argment"); + if (strcmp(argv[0], "true") == 0) + module->strip_nt_domain = true; + else if (strcmp(argv[0], "false") == 0) + module->strip_nt_domain = false; + else + SYNTAX_ASSERT(0, + "`strip-nt-domain' must `true' or `false'"); + } else if (strcmp(name, "remove-request-attribute") == 0 || + strcmp(name, "remove-response-attribute") == 0) { + struct attrs *attrs; + + if (strcmp(name, "remove-request-attribute") == 0) { + SYNTAX_ASSERT(argc == 1 || argc == 2, + "`remove-request-attribute' must have one or two " + "argment"); + attrs = &module->remove_reqattrs; + } else { + SYNTAX_ASSERT(argc == 1 || argc == 2, + "`remove-response-attribute' must have one or two " + "argment"); + attrs = &module->remove_resattrs; + } + if ((attr = calloc(1, sizeof(struct attr))) == NULL) { + module_send_message(module->base, IMSG_NG, + "Out of memory: %s", strerror(errno)); + } + if (argc == 1) { + attr->type = strtonum(argv[0], 0, 255, &errstr); + if (errstr == NULL && + attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) { + TAILQ_INSERT_TAIL(attrs, attr, next); + attr = NULL; + } + } else { + attr->type = RADIUS_TYPE_VENDOR_SPECIFIC; + attr->vendor = strtonum(argv[0], 0, UINT32_MAX, + &errstr); + if (errstr == NULL) + attr->vtype = strtonum(argv[1], 0, 255, + &errstr); + if (errstr == NULL) { + TAILQ_INSERT_TAIL(attrs, attr, next); + attr = NULL; + } + } + freezero(attr, sizeof(struct attr)); + if (strcmp(name, "remove-request-attribute") == 0) + SYNTAX_ASSERT(attr == NULL, + "wrong number for `remove-request-attribute`"); + else + SYNTAX_ASSERT(attr == NULL, + "wrong number for `remove-response-attribute`"); + } else if (strncmp(name, "_", 1) == 0) + /* nothing */; /* ignore all internal messages */ + else { + module_send_message(module->base, IMSG_NG, + "Unknown config parameter name `%s'", name); + return; + } + module_send_message(module->base, IMSG_OK, NULL); + return; + + syntax_error: + module_send_message(module->base, IMSG_NG, "%s", errmsg); +} + +/* request message decoration */ +static void +module_standard_reqdeco(void *ctx, u_int q_id, const u_char *pkt, size_t pktlen) +{ + struct module_standard *module = ctx; + RADIUS_PACKET *radpkt = NULL; + int changed = 0; + char *ch, *username, buf[256]; + struct attr *attr; + + if (module->strip_atmark_realm || module->strip_nt_domain) { + if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { + syslog(LOG_ERR, + "%s: radius_convert_packet() failed: %m", __func__); + module_stop(module->base); + return; + } + + username = buf; + if (radius_get_string_attr(radpkt, RADIUS_TYPE_USER_NAME, + username, sizeof(buf)) != 0) { + syslog(LOG_WARNING, + "standard: q=%u could not get User-Name attribute", + q_id); + goto skip; + } + + if (module->strip_atmark_realm && + (ch = strrchr(username, '@')) != NULL) { + *ch = '\0'; + changed++; + } + if (module->strip_nt_domain && + (ch = strchr(username, '\\')) != NULL) { + username = ch + 1; + changed++; + } + if (changed > 0) { + radius_del_attr_all(radpkt, RADIUS_TYPE_USER_NAME); + radius_put_string_attr(radpkt, + RADIUS_TYPE_USER_NAME, username); + } + } + skip: + TAILQ_FOREACH(attr, &module->remove_reqattrs, next) { + if (radpkt == NULL && + (radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { + syslog(LOG_ERR, + "%s: radius_convert_packet() failed: %m", __func__); + module_stop(module->base); + return; + } + if (attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) + radius_del_attr_all(radpkt, attr->type); + else + radius_del_vs_attr_all(radpkt, attr->vendor, + attr->vtype); + } + if (radpkt == NULL) { + pkt = NULL; + pktlen = 0; + } else { + pkt = radius_get_data(radpkt); + pktlen = radius_get_length(radpkt); + } + if (module_reqdeco_done(module->base, q_id, pkt, pktlen) == -1) { + syslog(LOG_ERR, "%s: module_reqdeco_done() failed: %m", + __func__); + module_stop(module->base); + } + if (radpkt != NULL) + radius_delete_packet(radpkt); +} + +/* response message decoration */ +static void +module_standard_resdeco(void *ctx, u_int q_id, const u_char *pkt, size_t pktlen) +{ + struct module_standard *module = ctx; + RADIUS_PACKET *radpkt = NULL; + struct attr *attr; + + TAILQ_FOREACH(attr, &module->remove_reqattrs, next) { + if (radpkt == NULL && + (radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { + syslog(LOG_ERR, + "%s: radius_convert_packet() failed: %m", __func__); + module_stop(module->base); + return; + } + if (attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) + radius_del_attr_all(radpkt, attr->type); + else + radius_del_vs_attr_all(radpkt, attr->vendor, + attr->vtype); + } + if (radpkt == NULL) { + pkt = NULL; + pktlen = 0; + } else { + pkt = radius_get_data(radpkt); + pktlen = radius_get_length(radpkt); + } + if (module_resdeco_done(module->base, q_id, pkt, pktlen) == -1) { + syslog(LOG_ERR, "%s: module_resdeco_done() failed: %m", + __func__); + module_stop(module->base); + } + if (radpkt != NULL) + radius_delete_packet(radpkt); +} diff --git a/usr.sbin/radiusd/radiusd_standard/Makefile b/usr.sbin/radiusd/radiusd_standard/Makefile new file mode 100644 index 00000000000..5dbea5e193b --- /dev/null +++ b/usr.sbin/radiusd/radiusd_standard/Makefile @@ -0,0 +1,8 @@ +# $OpenBSD: Makefile,v 1.1 2023/09/08 05:56:22 yasuoka Exp $ +PROG= radiusd_standard +BINDIR= /usr/libexec/radiusd +SRCS= radiusd_standard.c radiusd_module.c +LDADD= -lutil -lradius -lcrypto +NOMAN= # + +.include