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, (char *)check->strvalue, len);
189 if (ret == 0 && rest)
190 strcpy(rest, name + len);
193 namelen = strlen(name);
196 ret = strcmp(name + namelen - len,
197 (char *)check->strvalue);
198 if (ret == 0 && rest) {
199 strncpy(rest, name, namelen - len);
200 rest[namelen - len] = 0;
209 * Match a username with a wildcard expression.
210 * Is very limited for now.
212 static int matches(char *name, PAIR_LIST *pl, char *matchpart)
216 char *wild = pl->name;
220 * We now support both:
222 * DEFAULT Prefix = "P"
227 if ((tmp = pairfind(pl->check, PW_PREFIX)) != NULL ||
228 (tmp = pairfind(pl->check, PW_SUFFIX)) != NULL) {
230 if (strncmp(pl->name, "DEFAULT", 7) == 0 ||
231 strcmp(pl->name, name) == 0)
232 return !presufcmp(tmp, name, matchpart);
236 * Shortcut if there's no '*' in pl->name.
238 if (strchr(pl->name, '*') == NULL &&
239 (strncmp(pl->name, "DEFAULT", 7) == 0 ||
240 strcmp(pl->name, name) == 0)) {
241 strcpy(matchpart, name);
246 * Normally, we should return 0 here, but we
247 * support the old * stuff.
252 if (len == 0 || wlen == 0) return 0;
254 if (wild[0] == '*') {
257 if (wlen <= len && strcmp(name + (len - wlen), wild) == 0) {
258 strcpy(matchpart, name);
259 matchpart[len - wlen] = 0;
262 } else if (wild[wlen - 1] == '*') {
263 if (wlen <= len && strncmp(name, wild, wlen - 1) == 0) {
264 strcpy(matchpart, name + wlen - 1);
274 * Add hints to the info sent by the terminal server
275 * based on the pattern of the username.
277 static int hints_setup(REQUEST *request)
279 char newname[MAX_STRING_LEN];
286 VALUE_PAIR *request_pairs;
288 request_pairs = request->packet->vps;
290 if (hints == NULL || request_pairs == NULL)
291 return RLM_MODULE_NOOP;
294 * Check for valid input, zero length names not permitted
296 if ((tmp = pairfind(request_pairs, PW_USER_NAME)) == NULL)
299 name = (char *)tmp->strvalue;
301 if (name == NULL || name[0] == 0)
303 * No name, nothing to do.
305 return RLM_MODULE_NOOP;
307 for (i = hints; i; i = i->next) {
308 if (matches(name, i, newname)) {
309 DEBUG2(" hints: Matched %s at %d",
315 if (i == NULL) return RLM_MODULE_NOOP;
317 add = paircopy(i->reply);
320 printf("In hints_setup, newname is %s\n", newname);
324 * See if we need to adjust the name.
327 if ((tmp = pairfind(i->reply, PW_STRIP_USER_NAME)) != NULL
330 if ((tmp = pairfind(i->check, PW_STRIP_USER_NAME)) != NULL
335 tmp = pairfind(request_pairs, PW_STRIPPED_USER_NAME);
337 strcpy((char *)tmp->strvalue, newname);
338 tmp->length = strlen((char *)tmp->strvalue);
341 * No Stripped-User-Name exists: add one.
343 tmp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
345 radlog(L_ERR|L_CONS, "no memory");
348 strcpy((char *)tmp->strvalue, newname);
349 tmp->length = strlen((char *)tmp->strvalue);
350 pairadd(&request_pairs, tmp);
352 request->username = tmp;
356 * Now add all attributes to the request list,
357 * except the PW_STRIP_USER_NAME one.
359 pairdelete(&add, PW_STRIP_USER_NAME);
360 for(last = request_pairs; last && last->next; last = last->next)
362 if (last) last->next = add;
364 return RLM_MODULE_UPDATED;
368 * See if the huntgroup matches. This function is
369 * tied to the "Huntgroup" keyword.
371 static int huntgroup_cmp(VALUE_PAIR *request, VALUE_PAIR *check,
372 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
377 check_pairs = check_pairs; /* shut the compiler up */
378 reply_pairs = reply_pairs;
380 huntgroup = (char *)check->strvalue;
382 for (i = huntgroups; i; i = i->next) {
383 if (strcmp(i->name, huntgroup) != 0)
385 if (paircmp(request, i->check, NULL) == 0) {
386 DEBUG2(" huntgroups: Matched %s at %d",
393 * paircmp() expects to see zero on match, so let's
404 * See if we have access to the huntgroup.
406 static int huntgroup_access(VALUE_PAIR *request_pairs)
409 int r = RLM_MODULE_OK;
412 * We're not controlling access by huntgroups:
415 if (huntgroups == NULL)
416 return RLM_MODULE_OK;
418 for(i = huntgroups; i; i = i->next) {
420 * See if this entry matches.
422 if (paircmp(request_pairs, i->check, NULL) != 0)
426 * Now check for access.
428 r = RLM_MODULE_REJECT;
429 if (hunt_paircmp(request_pairs, i->reply) == 0) {
439 * If the NAS wasn't smart enought to add a NAS-IP-Address
440 * to the request, then add it ourselves.
442 static void add_nas_attr(REQUEST *request)
446 nas = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS);
448 nas = paircreate(PW_NAS_IP_ADDRESS, PW_TYPE_IPADDR);
450 radlog(L_ERR, "No memory");
453 nas->lvalue = request->packet->src_ipaddr;
454 pairadd(&request->packet->vps, nas);
458 * Add in a Request-Src-IP-Address, to tell the user
459 * the source IP of the request. That is, the client,
460 * but Client-IP-Address is too close to the old
461 * Client-FOO names, which I KNOW would confuse a lot
464 * Note that this MAY BE different from the NAS-IP-Address,
465 * especially if the request is being proxied.
467 * Note also that this is a server configuration item,
468 * and will NOT make it to any packets being sent from
471 nas = paircreate(PW_REQUEST_SRC_IP_ADDRESS, PW_TYPE_IPADDR);
473 radlog(L_ERR, "No memory");
476 nas->lvalue = request->packet->src_ipaddr;
477 pairadd(&request->packet->vps, nas);
484 static int preprocess_init(void)
489 pairlist_free(&huntgroups);
490 pairlist_free(&hints);
492 sprintf(buffer, "%s/%s", radius_dir, RADIUS_HUNTGROUPS);
493 rcode = pairlist_read(buffer, &huntgroups, 0);
498 sprintf(buffer, "%s/%s", radius_dir, RADIUS_HINTS);
499 rcode = pairlist_read(buffer, &hints, 0);
504 paircompare_register(PW_HUNTGROUP_NAME, 0, huntgroup_cmp);
510 * Preprocess a request.
512 static int preprocess_authorize(void *instance, REQUEST *request)
519 * Mangle the username, to get rid of stupid implementation
524 #ifdef WITH_ASCEND_HACK
526 * If we're using Ascend systems, hack the NAS-Port-Id
527 * in place, to go from Ascend's weird values to something
528 * approaching rationality.
530 ascend_nasport_hack(pairfind(request->packet->vps, PW_NAS_PORT_ID));
533 hints_setup(request);
536 * Note that we add the Request-Src-IP-Address to the request
537 * structure BEFORE checking huntgroup access. This allows
538 * the Request-Src-IP-Address to be used for huntgroup
541 add_nas_attr(request);
543 if (huntgroup_access(request->packet->vps) != RLM_MODULE_OK) {
544 radlog(L_AUTH, "No huntgroup access: [%s] (%s)",
545 request->username->strvalue,
546 auth_name(buf, sizeof(buf), request, 1));
547 return RLM_MODULE_REJECT;
550 return RLM_MODULE_OK; /* Meaning: try next authorization module */
554 * Preprocess a request before accounting
556 static int preprocess_preaccounting(void *instance, REQUEST *request)
562 * Ensure that we have the SAME user name for both
563 * authentication && accounting.
566 r = hints_setup(request);
569 * Ensure that we log the NAS IP Address in the packet.
571 add_nas_attr(request);
579 static int preprocess_destroy(void)
581 paircompare_unregister(PW_HUNTGROUP_NAME, huntgroup_cmp);
582 pairlist_free(&huntgroups);
583 pairlist_free(&hints);
588 /* globally exported name */
589 module_t rlm_preprocess = {
591 0, /* type: reserved */
592 preprocess_init, /* initialization */
593 NULL, /* instantiation */
594 preprocess_authorize, /* authorization */
595 NULL, /* authentication */
596 preprocess_preaccounting, /* pre-accounting */
597 NULL, /* accounting */
598 NULL, /* checksimul */
600 preprocess_destroy, /* destroy */