3 * Contains the functions for the "huntgroups" and "hints"
10 static const char rcsid[] = "$Id$";
14 #include <sys/types.h>
32 /* FIXME: should this stuff be instance data? */
33 static PAIR_LIST *huntgroups;
34 static PAIR_LIST *hints;
36 #ifdef WITH_ASCEND_HACK
39 * This hack changes Ascend's wierd port numberings
40 * to standard 0-??? port numbers so that the "+" works
41 * for IP address assignments.
43 static void ascend_nasport_hack(VALUE_PAIR *nas_port)
53 if (nas_port->lvalue > 9999) {
54 service = nas_port->lvalue/10000; /* 1=digital 2=analog */
55 line = (nas_port->lvalue - (10000 * service)) / 100;
56 channel = nas_port->lvalue-((10000 * service)+(100 * line));
58 (channel - 1) + (line - 1) * ASCEND_CHANNELS_PER_LINE;
64 * Mangle username if needed, IN PLACE.
66 static void rad_mangle(REQUEST *request)
69 VALUE_PAIR *request_pairs;
71 #ifdef WITH_NTDOMAIN_HACK
72 char newname[MAX_STRING_LEN];
74 #if defined(WITH_NTDOMAIN_HACK) || defined(WITH_SPECIALIX_JETSTREAM_HACK)
79 * Get the username from the request
80 * If it isn't there, then we can't mangle the request.
82 request_pairs = request->packet->vps;
83 namepair = pairfind(request_pairs, PW_USER_NAME);
84 if ((namepair == NULL) ||
85 (namepair->length <= 0)) {
89 #ifdef WITH_NTDOMAIN_HACK
91 * Windows NT machines often authenticate themselves as
92 * NT_DOMAIN\username. Try to be smart about this.
94 * FIXME: should we handle this as a REALM ?
96 if ((ptr = strchr(namepair->strvalue, '\\')) != NULL) {
97 strNcpy(newname, ptr + 1, sizeof(newname));
99 strcpy(namepair->strvalue, newname);
100 namepair->length = strlen(newname);
102 #endif /* WITH_NTDOMAIN_HACK */
104 #ifdef WITH_SPECIALIX_JETSTREAM_HACK
106 * Specialix Jetstream 8500 24 port access server.
107 * If the user name is 10 characters or longer, a "/"
108 * and the excess characters after the 10th are
109 * appended to the user name.
111 * Reported by Lucas Heise <root@laonet.net>
113 if (strlen(namepair->strvalue) > 10 && namepair->strvalue[10] == '/') {
114 for (ptr = namepair->strvalue + 11; *ptr; ptr++)
117 namepair->length = strlen(namepair->strvalue);
122 * Small check: if Framed-Protocol present but Service-Type
123 * is missing, add Service-Type = Framed-User.
125 if (pairfind(request_pairs, PW_FRAMED_PROTOCOL) != NULL &&
126 pairfind(request_pairs, PW_SERVICE_TYPE) == NULL) {
127 tmp = paircreate(PW_SERVICE_TYPE, PW_TYPE_INTEGER);
129 tmp->lvalue = PW_FRAMED_USER;
130 pairmove(&request_pairs, &tmp);
136 * FIXME: find some substitute for this, or
137 * drop the log_auth_detail option all together.
140 rad_accounting_orig(request, -1, "detail.auth");
145 * Compare the request with the "reply" part in the
146 * huntgroup, which normally only contains username or group.
147 * At least one of the "reply" items has to match.
149 static int hunt_paircmp(VALUE_PAIR *request, VALUE_PAIR *check)
151 VALUE_PAIR *check_item = check;
155 if (check == NULL) return 0;
157 while (result != 0 && check_item != NULL) {
159 tmp = check_item->next;
160 check_item->next = NULL;
162 result = paircmp(request, check_item, NULL);
164 check_item->next = tmp;
165 check_item = check_item->next;
173 * Compare prefix/suffix
175 static int presufcmp(VALUE_PAIR *check, char *name, char *rest)
181 printf("Comparing %s and %s, check->attr is %d\n",
182 name, check->strvalue, check->attribute);
185 len = strlen((char *)check->strvalue);
186 switch (check->attribute) {
188 ret = strncmp(name, check->strvalue, len);
189 if (ret == 0 && rest)
190 strcpy(rest, name + len);
193 namelen = strlen(name);
196 ret = strcmp(name + namelen - len, check->strvalue);
197 if (ret == 0 && rest) {
198 strncpy(rest, name, namelen - len);
199 rest[namelen - len] = 0;
208 * Match a username with a wildcard expression.
209 * Is very limited for now.
211 static int matches(char *name, PAIR_LIST *pl, char *matchpart)
215 char *wild = pl->name;
219 * We now support both:
221 * DEFAULT Prefix = "P"
226 if ((tmp = pairfind(pl->check, PW_PREFIX)) != NULL ||
227 (tmp = pairfind(pl->check, PW_SUFFIX)) != NULL) {
229 if (strncmp(pl->name, "DEFAULT", 7) == 0 ||
230 strcmp(pl->name, name) == 0)
231 return !presufcmp(tmp, name, matchpart);
235 * Shortcut if there's no '*' in pl->name.
237 if (strchr(pl->name, '*') == NULL &&
238 (strncmp(pl->name, "DEFAULT", 7) == 0 ||
239 strcmp(pl->name, name) == 0)) {
240 strcpy(matchpart, name);
245 * Normally, we should return 0 here, but we
246 * support the old * stuff.
251 if (len == 0 || wlen == 0) return 0;
253 if (wild[0] == '*') {
256 if (wlen <= len && strcmp(name + (len - wlen), wild) == 0) {
257 strcpy(matchpart, name);
258 matchpart[len - wlen] = 0;
261 } else if (wild[wlen - 1] == '*') {
262 if (wlen <= len && strncmp(name, wild, wlen - 1) == 0) {
263 strcpy(matchpart, name + wlen - 1);
273 * Add hints to the info sent by the terminal server
274 * based on the pattern of the username.
276 static int hints_setup(REQUEST *request)
278 char newname[MAX_STRING_LEN];
285 VALUE_PAIR *request_pairs;
287 request_pairs = request->packet->vps;
289 if (hints == NULL || request_pairs == NULL)
290 return RLM_MODULE_NOOP;
293 * Check for valid input, zero length names not permitted
295 if ((tmp = pairfind(request_pairs, PW_USER_NAME)) == NULL)
298 name = (char *)tmp->strvalue;
300 if (name == NULL || name[0] == 0)
302 * No name, nothing to do.
304 return RLM_MODULE_NOOP;
306 for (i = hints; i; i = i->next) {
307 if (matches(name, i, newname)) {
308 DEBUG2(" hints: Matched %s at %d",
314 if (i == NULL) return RLM_MODULE_NOOP;
316 add = paircopy(i->reply);
319 printf("In hints_setup, newname is %s\n", newname);
323 * See if we need to adjust the name.
326 if ((tmp = pairfind(i->reply, PW_STRIP_USER_NAME)) != NULL
329 if ((tmp = pairfind(i->check, PW_STRIP_USER_NAME)) != NULL
334 tmp = pairfind(request_pairs, PW_STRIPPED_USER_NAME);
336 strcpy(tmp->strvalue, newname);
337 tmp->length = strlen((char *)tmp->strvalue);
340 * No Stripped-User-Name exists: add one.
342 tmp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
344 radlog(L_ERR|L_CONS, "no memory");
347 strcpy(tmp->strvalue, newname);
348 tmp->length = strlen((char *)tmp->strvalue);
349 pairadd(&request_pairs, tmp);
351 request->username = tmp;
355 * Now add all attributes to the request list,
356 * except the PW_STRIP_USER_NAME one.
358 pairdelete(&add, PW_STRIP_USER_NAME);
359 for(last = request_pairs; last && last->next; last = last->next)
361 if (last) last->next = add;
363 return RLM_MODULE_UPDATED;
367 * See if the huntgroup matches. This function is
368 * tied to the "Huntgroup" keyword.
370 static int huntgroup_cmp(VALUE_PAIR *request, VALUE_PAIR *check,
371 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
376 check_pairs = check_pairs; /* shut the compiler up */
377 reply_pairs = reply_pairs;
379 huntgroup = (char *)check->strvalue;
381 for (i = huntgroups; i; i = i->next) {
382 if (strcmp(i->name, huntgroup) != 0)
384 if (paircmp(request, i->check, NULL) == 0) {
385 DEBUG2(" huntgroups: Matched %s at %d",
392 * paircmp() expects to see zero on match, so let's
403 * See if we have access to the huntgroup.
405 static int huntgroup_access(VALUE_PAIR *request_pairs)
408 int r = RLM_MODULE_OK;
411 * We're not controlling access by huntgroups:
414 if (huntgroups == NULL)
415 return RLM_MODULE_OK;
417 for(i = huntgroups; i; i = i->next) {
419 * See if this entry matches.
421 if (paircmp(request_pairs, i->check, NULL) != 0)
425 * Now check for access.
427 r = RLM_MODULE_REJECT;
428 if (hunt_paircmp(request_pairs, i->reply) == 0) {
438 * If the NAS wasn't smart enought to add a NAS-IP-Address
439 * to the request, then add it ourselves.
441 static void add_nas_attr(REQUEST *request)
445 nas = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS);
447 nas = paircreate(PW_NAS_IP_ADDRESS, PW_TYPE_IPADDR);
449 radlog(L_ERR, "No memory");
452 nas->lvalue = request->packet->src_ipaddr;
453 pairadd(&request->packet->vps, nas);
457 * Add in a Request-Src-IP-Address, to tell the user
458 * the source IP of the request. That is, the client,
459 * but Client-IP-Address is too close to the old
460 * Client-FOO names, which I KNOW would confuse a lot
463 * Note that this MAY BE different from the NAS-IP-Address,
464 * especially if the request is being proxied.
466 * Note also that this is a server configuration item,
467 * and will NOT make it to any packets being sent from
470 nas = paircreate(PW_REQUEST_SRC_IP_ADDRESS, PW_TYPE_IPADDR);
472 radlog(L_ERR, "No memory");
475 nas->lvalue = request->packet->src_ipaddr;
476 pairadd(&request->packet->vps, nas);
483 static int preprocess_init(void)
488 pairlist_free(&huntgroups);
489 pairlist_free(&hints);
491 sprintf(buffer, "%s/%s", radius_dir, RADIUS_HUNTGROUPS);
492 rcode = pairlist_read(buffer, &huntgroups, 0);
497 sprintf(buffer, "%s/%s", radius_dir, RADIUS_HINTS);
498 rcode = pairlist_read(buffer, &hints, 0);
503 paircompare_register(PW_HUNTGROUP_NAME, 0, huntgroup_cmp);
509 * Preprocess a request.
511 static int preprocess_authorize(void *instance, REQUEST *request)
518 * Mangle the username, to get rid of stupid implementation
523 #ifdef WITH_ASCEND_HACK
525 * If we're using Ascend systems, hack the NAS-Port-Id
526 * in place, to go from Ascend's weird values to something
527 * approaching rationality.
529 ascend_nasport_hack(pairfind(request->packet->vps, PW_NAS_PORT_ID));
532 hints_setup(request);
535 * Note that we add the Request-Src-IP-Address to the request
536 * structure BEFORE checking huntgroup access. This allows
537 * the Request-Src-IP-Address to be used for huntgroup
540 add_nas_attr(request);
542 if (huntgroup_access(request->packet->vps) != RLM_MODULE_OK) {
543 radlog(L_AUTH, "No huntgroup access: [%s] (%s)",
544 request->username->strvalue,
545 auth_name(buf, sizeof(buf), request, 1));
546 return RLM_MODULE_REJECT;
549 return RLM_MODULE_OK; /* Meaning: try next authorization module */
553 * Preprocess a request before accounting
555 static int preprocess_preaccounting(void *instance, REQUEST *request)
561 * Ensure that we have the SAME user name for both
562 * authentication && accounting.
565 r = hints_setup(request);
568 * Ensure that we log the NAS IP Address in the packet.
570 add_nas_attr(request);
578 static int preprocess_destroy(void)
580 paircompare_unregister(PW_HUNTGROUP_NAME, huntgroup_cmp);
581 pairlist_free(&huntgroups);
582 pairlist_free(&hints);
587 /* globally exported name */
588 module_t rlm_preprocess = {
590 0, /* type: reserved */
591 preprocess_init, /* initialization */
592 NULL, /* instantiation */
593 preprocess_authorize, /* authorization */
594 NULL, /* authentication */
595 preprocess_preaccounting, /* pre-accounting */
596 NULL, /* accounting */
598 preprocess_destroy, /* destroy */