1 /* $OpenBSD: radiusd_standard.c,v 1.1 2023/09/08 05:56:22 yasuoka 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 57 int 58 main(int argc, char *argv[]) 59 { 60 struct module_standard module_standard; 61 struct module_handlers handlers = { 62 .config_set = module_standard_config_set, 63 .request_decoration = module_standard_reqdeco, 64 .response_decoration = module_standard_resdeco 65 }; 66 struct attr *attr; 67 68 memset(&module_standard, 0, sizeof(module_standard)); 69 TAILQ_INIT(&module_standard.remove_reqattrs); 70 TAILQ_INIT(&module_standard.remove_resattrs); 71 72 if ((module_standard.base = module_create( 73 STDIN_FILENO, &module_standard, &handlers)) == NULL) 74 err(1, "Could not create a module instance"); 75 76 module_drop_privilege(module_standard.base); 77 if (pledge("stdio", NULL) == -1) 78 err(1, "pledge"); 79 80 module_load(module_standard.base); 81 82 openlog(NULL, LOG_PID, LOG_DAEMON); 83 84 while (module_run(module_standard.base) == 0) 85 ; 86 87 module_destroy(module_standard.base); 88 while ((attr = TAILQ_FIRST(&module_standard.remove_reqattrs)) != NULL) { 89 TAILQ_REMOVE(&module_standard.remove_reqattrs, attr, next); 90 freezero(attr, sizeof(struct attr)); 91 } 92 while ((attr = TAILQ_FIRST(&module_standard.remove_resattrs)) != NULL) { 93 TAILQ_REMOVE(&module_standard.remove_resattrs, attr, next); 94 freezero(attr, sizeof(struct attr)); 95 } 96 97 exit(EXIT_SUCCESS); 98 } 99 100 static void 101 module_standard_config_set(void *ctx, const char *name, int argc, 102 char * const * argv) 103 { 104 struct module_standard *module = ctx; 105 struct attr *attr; 106 const char *errmsg = "none"; 107 const char *errstr; 108 109 if (strcmp(name, "strip-atmark-realm") == 0) { 110 SYNTAX_ASSERT(argc == 1, 111 "`strip-atmark-realm' must have only one argment"); 112 if (strcmp(argv[0], "true") == 0) 113 module->strip_atmark_realm = true; 114 else if (strcmp(argv[0], "false") == 0) 115 module->strip_atmark_realm = false; 116 else 117 SYNTAX_ASSERT(0, 118 "`strip-atmark-realm' must `true' or `false'"); 119 } else if (strcmp(name, "strip-nt-domain") == 0) { 120 SYNTAX_ASSERT(argc == 1, 121 "`strip-nt-domain' must have only one argment"); 122 if (strcmp(argv[0], "true") == 0) 123 module->strip_nt_domain = true; 124 else if (strcmp(argv[0], "false") == 0) 125 module->strip_nt_domain = false; 126 else 127 SYNTAX_ASSERT(0, 128 "`strip-nt-domain' must `true' or `false'"); 129 } else if (strcmp(name, "remove-request-attribute") == 0 || 130 strcmp(name, "remove-response-attribute") == 0) { 131 struct attrs *attrs; 132 133 if (strcmp(name, "remove-request-attribute") == 0) { 134 SYNTAX_ASSERT(argc == 1 || argc == 2, 135 "`remove-request-attribute' must have one or two " 136 "argment"); 137 attrs = &module->remove_reqattrs; 138 } else { 139 SYNTAX_ASSERT(argc == 1 || argc == 2, 140 "`remove-response-attribute' must have one or two " 141 "argment"); 142 attrs = &module->remove_resattrs; 143 } 144 if ((attr = calloc(1, sizeof(struct attr))) == NULL) { 145 module_send_message(module->base, IMSG_NG, 146 "Out of memory: %s", strerror(errno)); 147 } 148 if (argc == 1) { 149 attr->type = strtonum(argv[0], 0, 255, &errstr); 150 if (errstr == NULL && 151 attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) { 152 TAILQ_INSERT_TAIL(attrs, attr, next); 153 attr = NULL; 154 } 155 } else { 156 attr->type = RADIUS_TYPE_VENDOR_SPECIFIC; 157 attr->vendor = strtonum(argv[0], 0, UINT32_MAX, 158 &errstr); 159 if (errstr == NULL) 160 attr->vtype = strtonum(argv[1], 0, 255, 161 &errstr); 162 if (errstr == NULL) { 163 TAILQ_INSERT_TAIL(attrs, attr, next); 164 attr = NULL; 165 } 166 } 167 freezero(attr, sizeof(struct attr)); 168 if (strcmp(name, "remove-request-attribute") == 0) 169 SYNTAX_ASSERT(attr == NULL, 170 "wrong number for `remove-request-attribute`"); 171 else 172 SYNTAX_ASSERT(attr == NULL, 173 "wrong number for `remove-response-attribute`"); 174 } else if (strncmp(name, "_", 1) == 0) 175 /* nothing */; /* ignore all internal messages */ 176 else { 177 module_send_message(module->base, IMSG_NG, 178 "Unknown config parameter name `%s'", name); 179 return; 180 } 181 module_send_message(module->base, IMSG_OK, NULL); 182 return; 183 184 syntax_error: 185 module_send_message(module->base, IMSG_NG, "%s", errmsg); 186 } 187 188 /* request message decoration */ 189 static void 190 module_standard_reqdeco(void *ctx, u_int q_id, const u_char *pkt, size_t pktlen) 191 { 192 struct module_standard *module = ctx; 193 RADIUS_PACKET *radpkt = NULL; 194 int changed = 0; 195 char *ch, *username, buf[256]; 196 struct attr *attr; 197 198 if (module->strip_atmark_realm || module->strip_nt_domain) { 199 if ((radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { 200 syslog(LOG_ERR, 201 "%s: radius_convert_packet() failed: %m", __func__); 202 module_stop(module->base); 203 return; 204 } 205 206 username = buf; 207 if (radius_get_string_attr(radpkt, RADIUS_TYPE_USER_NAME, 208 username, sizeof(buf)) != 0) { 209 syslog(LOG_WARNING, 210 "standard: q=%u could not get User-Name attribute", 211 q_id); 212 goto skip; 213 } 214 215 if (module->strip_atmark_realm && 216 (ch = strrchr(username, '@')) != NULL) { 217 *ch = '\0'; 218 changed++; 219 } 220 if (module->strip_nt_domain && 221 (ch = strchr(username, '\\')) != NULL) { 222 username = ch + 1; 223 changed++; 224 } 225 if (changed > 0) { 226 radius_del_attr_all(radpkt, RADIUS_TYPE_USER_NAME); 227 radius_put_string_attr(radpkt, 228 RADIUS_TYPE_USER_NAME, username); 229 } 230 } 231 skip: 232 TAILQ_FOREACH(attr, &module->remove_reqattrs, next) { 233 if (radpkt == NULL && 234 (radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { 235 syslog(LOG_ERR, 236 "%s: radius_convert_packet() failed: %m", __func__); 237 module_stop(module->base); 238 return; 239 } 240 if (attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) 241 radius_del_attr_all(radpkt, attr->type); 242 else 243 radius_del_vs_attr_all(radpkt, attr->vendor, 244 attr->vtype); 245 } 246 if (radpkt == NULL) { 247 pkt = NULL; 248 pktlen = 0; 249 } else { 250 pkt = radius_get_data(radpkt); 251 pktlen = radius_get_length(radpkt); 252 } 253 if (module_reqdeco_done(module->base, q_id, pkt, pktlen) == -1) { 254 syslog(LOG_ERR, "%s: module_reqdeco_done() failed: %m", 255 __func__); 256 module_stop(module->base); 257 } 258 if (radpkt != NULL) 259 radius_delete_packet(radpkt); 260 } 261 262 /* response message decoration */ 263 static void 264 module_standard_resdeco(void *ctx, u_int q_id, const u_char *pkt, size_t pktlen) 265 { 266 struct module_standard *module = ctx; 267 RADIUS_PACKET *radpkt = NULL; 268 struct attr *attr; 269 270 TAILQ_FOREACH(attr, &module->remove_reqattrs, next) { 271 if (radpkt == NULL && 272 (radpkt = radius_convert_packet(pkt, pktlen)) == NULL) { 273 syslog(LOG_ERR, 274 "%s: radius_convert_packet() failed: %m", __func__); 275 module_stop(module->base); 276 return; 277 } 278 if (attr->type != RADIUS_TYPE_VENDOR_SPECIFIC) 279 radius_del_attr_all(radpkt, attr->type); 280 else 281 radius_del_vs_attr_all(radpkt, attr->vendor, 282 attr->vtype); 283 } 284 if (radpkt == NULL) { 285 pkt = NULL; 286 pktlen = 0; 287 } else { 288 pkt = radius_get_data(radpkt); 289 pktlen = radius_get_length(radpkt); 290 } 291 if (module_resdeco_done(module->base, q_id, pkt, pktlen) == -1) { 292 syslog(LOG_ERR, "%s: module_resdeco_done() failed: %m", 293 __func__); 294 module_stop(module->base); 295 } 296 if (radpkt != NULL) 297 radius_delete_packet(radpkt); 298 } 299