src/usr.sbin/radiusd/radiusd_standard.c

301 lines
8.5 KiB
C

/* $OpenBSD: radiusd_standard.c,v 1.5 2024/04/23 13:34:51 jsg 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 <sys/types.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <radius.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#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,
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, 0);
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 *req, size_t reqlen,
const u_char *res, size_t reslen)
{
struct module_standard *module = ctx;
RADIUS_PACKET *radres = NULL;
struct attr *attr;
TAILQ_FOREACH(attr, &module->remove_resattrs, next) {
if (radres == NULL &&
(radres = radius_convert_packet(res, reslen)) == 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(radres, attr->type);
else
radius_del_vs_attr_all(radres, attr->vendor,
attr->vtype);
}
if (radres == NULL) {
res = NULL;
reslen = 0;
} else {
res = radius_get_data(radres);
reslen = radius_get_length(radres);
}
if (module_resdeco_done(module->base, q_id, res, reslen) == -1) {
syslog(LOG_ERR, "%s: module_resdeco_done() failed: %m",
__func__);
module_stop(module->base);
}
if (radres != NULL)
radius_delete_packet(radres);
}