1 /* $OpenBSD: radiusd_standard.c,v 1.5 2024/04/23 13:34:51 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2013, 2023 Internet Initiative Japan Inc. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include <sys/types.h> 19 #include <sys/queue.h> 20 21 #include <err.h> 22 #include <errno.h> 23 #include <radius.h> 24 #include <stdbool.h> 25 #include <stdint.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <syslog.h> 30 #include <unistd.h> 31 32 #include "radiusd.h" 33 #include "radiusd_module.h" 34 35 TAILQ_HEAD(attrs,attr); 36 37 struct attr { 38 uint8_t type; 39 uint32_t vendor; 40 uint32_t vtype; 41 TAILQ_ENTRY(attr) next; 42 }; 43 44 struct module_standard { 45 struct module_base *base; 46 bool strip_atmark_realm; 47 bool strip_nt_domain; 48 struct attrs remove_reqattrs; 49 struct attrs remove_resattrs; 50 }; 51 52 static void module_standard_config_set(void *, const char *, int, 53 char * const *); 54 static void module_standard_reqdeco(void *, u_int, const u_char *, size_t); 55 static void module_standard_resdeco(void *, u_int, const u_char *, size_t, 56 const u_char *, size_t); 57 58 int 59 main(int argc, char *argv[]) 60 { 61 struct module_standard module_standard; 62 struct module_handlers handlers = { 63 .config_set = module_standard_config_set, 64 .request_decoration = module_standard_reqdeco, 65 .response_decoration = module_standard_resdeco 66 }; 67 struct attr *attr; 68 69 memset(&module_standard, 0, sizeof(module_standard)); 70 TAILQ_INIT(&module_standard.remove_reqattrs); 71 TAILQ_INIT(&module_standard.remove_resattrs); 72 73 if ((module_standard.base = module_create( 74 STDIN_FILENO, &module_standard, &handlers)) == NULL) 75 err(1, "Could not create a module instance"); 76 77 module_drop_privilege(module_standard.base, 0); 78 if (pledge("stdio", NULL) == -1) 79 err(1, "pledge"); 80 81 module_load(module_standard.base); 82 83 openlog(NULL, LOG_PID, LOG_DAEMON); 84 85 while (module_run(module_standard.base) == 0) 86 ; 87 88 module_destroy(module_standard.base); 89 while ((attr = TAILQ_FIRST(&module_standard.remove_reqattrs)) != NULL) { 90 TAILQ_REMOVE(&module_standard.remove_reqattrs, attr, next); 91 freezero(attr, sizeof(struct attr)); 92 } 93 while ((attr = TAILQ_FIRST(&module_standard.remove_resattrs)) != NULL) { 94 TAILQ_REMOVE(&module_standard.remove_resattrs, attr, next); 95 freezero(attr, sizeof(struct attr)); 96 } 97 98 exit(EXIT_SUCCESS); 99 } 100 101 static void 102 module_standard_config_set(void *ctx, const char *name, int argc, 103 char * const * argv) 104 { 105 struct module_standard *module = ctx; 106 struct attr *attr; 107 const char *errmsg = "none"; 108 const char *errstr; 109 110 if (strcmp(name, "strip-atmark-realm") == 0) { 111 SYNTAX_ASSERT(argc == 1, 112 "`strip-atmark-realm' must have only one argment"); 113 if (strcmp(argv[0], "true") == 0) 114 module->strip_atmark_realm = true; 115 else if (strcmp(argv[0], "false") == 0) 116 module->strip_atmark_realm = false; 117 else 118 SYNTAX_ASSERT(0, 119 "`strip-atmark-realm' must `true' or `false'"); 120 } else if (strcmp(name, "strip-nt-domain") == 0) { 121 SYNTAX_ASSERT(argc == 1, 122 "`strip-nt-domain' must have only one argment"); 123 if (strcmp(argv[0], "true") == 0) 124 module->strip_nt_domain = true; 125 else if (strcmp(argv[0], "false") == 0) 126 module->strip_nt_domain = false; 127 else 128 SYNTAX_ASSERT(0, 129 "`strip-nt-domain' must `true' or `false'"); 130 } else if (strcmp(name, "remove-request-attribute") == 0 || 131 strcmp(name, "remove-response-attribute") == 0) { 132 struct attrs *attrs; 133 134 if (strcmp(name, "remove-request-attribute") == 0) { 135 SYNTAX_ASSERT(argc == 1 || argc == 2, 136 "`remove-request-attribute' must have one or two " 137 "argment"); 138 attrs = &module->remove_reqattrs; 139 } else { 140 SYNTAX_ASSERT(argc == 1 || argc == 2, 141 "`remove-response-attribute' must have one or two " 142 "argment"); 143 attrs = &module->remove_resattrs; 144 } 145 if ((attr = calloc(1, sizeof(struct attr))) == NULL) { 146 module_send_message(module->base, IMSG_NG, 147 "Out of memory: %s", strerror(errno)); 148 } 149 if (argc == 1) { 150 attr->type = strtonum(argv[0], 0, 255, &errstr); 151 if (errstr == NULL && 152 attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) { 153 TAILQ_INSERT_TAIL(attrs, attr, next); 154 attr = NULL; 155 } 156 } else { 157 attr->type = RADIUS_TYPE_VENDOR_SPECIFIC; 158 attr->vendor = strtonum(argv[0], 0, UINT32_MAX, 159 &errstr); 160 if (errstr == NULL) 161 attr->vtype = strtonum(argv[1], 0, 255, 162 &errstr); 163 if (errstr == NULL) { 164 TAILQ_INSERT_TAIL(attrs, attr, next); 165 attr = NULL; 166 } 167 } 168 freezero(attr, sizeof(struct attr)); 169 if (strcmp(name, "remove-request-attribute") == 0) 170 SYNTAX_ASSERT(attr == NULL, 171 "wrong number for `remove-request-attribute`"); 172 else 173 SYNTAX_ASSERT(attr == NULL, 174 "wrong number for `remove-response-attribute`"); 175 } else if (strncmp(name, "_", 1) == 0) 176 /* nothing */; /* ignore all internal messages */ 177 else { 178 module_send_message(module->base, IMSG_NG, 179 "Unknown config parameter name `%s'", name); 180 return; 181 } 182 module_send_message(module->base, IMSG_OK, NULL); 183 return; 184 185 syntax_error: 186 module_send_message(module->base, IMSG_NG, "%s", errmsg); 187 } 188 189 /* request message decoration */ 190 static void 191 module_standard_reqdeco(void *ctx, u_int q_id, const u_char *pkt, size_t pktlen) 192 { 193 struct module_standard *module = ctx; 194 RADIUS_PACKET *radpkt = NULL; 195 int changed = 0; 196 char *ch, *username, buf[256]; 197 struct attr *attr; 198 199 if (module->strip_atmark_realm || module->strip_nt_domain) { 200 if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { 201 syslog(LOG_ERR, 202 "%s: radius_convert_packet() failed: %m", __func__); 203 module_stop(module->base); 204 return; 205 } 206 207 username = buf; 208 if (radius_get_string_attr(radpkt, RADIUS_TYPE_USER_NAME, 209 username, sizeof(buf)) != 0) { 210 syslog(LOG_WARNING, 211 "standard: q=%u could not get User-Name attribute", 212 q_id); 213 goto skip; 214 } 215 216 if (module->strip_atmark_realm && 217 (ch = strrchr(username, '@')) != NULL) { 218 *ch = '\0'; 219 changed++; 220 } 221 if (module->strip_nt_domain && 222 (ch = strchr(username, '\\')) != NULL) { 223 username = ch + 1; 224 changed++; 225 } 226 if (changed > 0) { 227 radius_del_attr_all(radpkt, RADIUS_TYPE_USER_NAME); 228 radius_put_string_attr(radpkt, 229 RADIUS_TYPE_USER_NAME, username); 230 } 231 } 232 skip: 233 TAILQ_FOREACH(attr, &module->remove_reqattrs, next) { 234 if (radpkt == NULL && 235 (radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { 236 syslog(LOG_ERR, 237 "%s: radius_convert_packet() failed: %m", __func__); 238 module_stop(module->base); 239 return; 240 } 241 if (attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) 242 radius_del_attr_all(radpkt, attr->type); 243 else 244 radius_del_vs_attr_all(radpkt, attr->vendor, 245 attr->vtype); 246 } 247 if (radpkt == NULL) { 248 pkt = NULL; 249 pktlen = 0; 250 } else { 251 pkt = radius_get_data(radpkt); 252 pktlen = radius_get_length(radpkt); 253 } 254 if (module_reqdeco_done(module->base, q_id, pkt, pktlen) == -1) { 255 syslog(LOG_ERR, "%s: module_reqdeco_done() failed: %m", 256 __func__); 257 module_stop(module->base); 258 } 259 if (radpkt != NULL) 260 radius_delete_packet(radpkt); 261 } 262 263 /* response message decoration */ 264 static void 265 module_standard_resdeco(void *ctx, u_int q_id, const u_char *req, size_t reqlen, 266 const u_char *res, size_t reslen) 267 { 268 struct module_standard *module = ctx; 269 RADIUS_PACKET *radres = NULL; 270 struct attr *attr; 271 272 TAILQ_FOREACH(attr, &module->remove_resattrs, next) { 273 if (radres == NULL && 274 (radres = radius_convert_packet(res, reslen)) == NULL) { 275 syslog(LOG_ERR, 276 "%s: radius_convert_packet() failed: %m", __func__); 277 module_stop(module->base); 278 return; 279 } 280 if (attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) 281 radius_del_attr_all(radres, attr->type); 282 else 283 radius_del_vs_attr_all(radres, attr->vendor, 284 attr->vtype); 285 } 286 if (radres == NULL) { 287 res = NULL; 288 reslen = 0; 289 } else { 290 res = radius_get_data(radres); 291 reslen = radius_get_length(radres); 292 } 293 if (module_resdeco_done(module->base, q_id, res, reslen) == -1) { 294 syslog(LOG_ERR, "%s: module_resdeco_done() failed: %m", 295 __func__); 296 module_stop(module->base); 297 } 298 if (radres != NULL) 299 radius_delete_packet(radres); 300 } 301