c19b80006336546b78c2586f8f44a4625c32d350
[freeradius.git] / src / modules / rlm_preprocess / rlm_preprocess.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_preprocess.c
19  * @brief Fixes up requests, and processes huntgroups/hints files.
20  *
21  * @copyright 2000,2006  The FreeRADIUS server project
22  * @copyright 2000  Alan DeKok <aland@ox.org>
23  */
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/radiusd.h>
27 #include        <freeradius-devel/modules.h>
28 #include        <freeradius-devel/rad_assert.h>
29
30 #include        <ctype.h>
31
32 typedef struct rlm_preprocess_t {
33         char const      *huntgroup_file;
34         char const      *hints_file;
35         PAIR_LIST       *huntgroups;
36         PAIR_LIST       *hints;
37         bool            with_ascend_hack;
38         uint32_t        ascend_channels_per_line;
39         bool            with_ntdomain_hack;
40         bool            with_specialix_jetstream_hack;
41         bool            with_cisco_vsa_hack;
42         bool            with_alvarion_vsa_hack;
43         bool            with_cablelabs_vsa_hack;
44 } rlm_preprocess_t;
45
46 static const CONF_PARSER module_config[] = {
47         { "huntgroups", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_preprocess_t, huntgroup_file), NULL },
48         { "hints", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_preprocess_t, hints_file), NULL },
49         { "with_ascend_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_ascend_hack), "no" },
50         { "ascend_channels_per_line", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_preprocess_t, ascend_channels_per_line), "23" },
51
52         { "with_ntdomain_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_ntdomain_hack), "no" },
53         { "with_specialix_jetstream_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_specialix_jetstream_hack), "no"  },
54         { "with_cisco_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_cisco_vsa_hack), "no" },
55         { "with_alvarion_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_alvarion_vsa_hack), "no" },
56 #if 0
57         { "with_cablelabs_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_cablelabs_vsa_hack), NULL },
58 #endif
59         { NULL, -1, 0, NULL, NULL }
60 };
61
62 /*
63  *     See if a VALUE_PAIR list contains Fall-Through = Yes
64  */
65 static int fallthrough(VALUE_PAIR *vp)
66 {
67         VALUE_PAIR *tmp;
68         tmp = pairfind(vp, PW_FALL_THROUGH, 0, TAG_ANY);
69
70         return tmp ? tmp->vp_integer : 0;
71 }
72
73 /*
74  *      This hack changes Ascend's wierd port numberings
75  *      to standard 0-??? port numbers so that the "+" works
76  *      for IP address assignments.
77  */
78 static void ascend_nasport_hack(VALUE_PAIR *nas_port, int channels_per_line)
79 {
80         int service;
81         int line;
82         int channel;
83
84         if (!nas_port) {
85                 return;
86         }
87
88         if (nas_port->vp_integer > 9999) {
89                 service = nas_port->vp_integer/10000; /* 1=digital 2=analog */
90                 line = (nas_port->vp_integer - (10000 * service)) / 100;
91                 channel = nas_port->vp_integer - ((10000 * service) + (100 * line));
92                 nas_port->vp_integer = (channel - 1) + ((line - 1) * channels_per_line);
93         }
94 }
95
96 /*
97  *      This hack strips out Cisco's VSA duplicities in lines
98  *      (Cisco not implemented VSA's in standard way.
99  *
100  *      Cisco sends it's VSA attributes with the attribute name *again*
101  *      in the string, like:  H323-Attribute = "h323-attribute=value".
102  *      This sort of behaviour is nonsense.
103  */
104 static void cisco_vsa_hack(REQUEST *request)
105 {
106         int             vendorcode;
107         char            *ptr;
108         char            newattr[MAX_STRING_LEN];
109         VALUE_PAIR      *vp;
110         vp_cursor_t     cursor;
111         for (vp = fr_cursor_init(&cursor, &request->packet->vps);
112              vp;
113              vp = fr_cursor_next(&cursor)) {
114                 vendorcode = vp->da->vendor;
115                 if (!((vendorcode == 9) || (vendorcode == 6618))) {
116                         continue; /* not a Cisco or Quintum VSA, continue */
117                 }
118
119                 if (vp->da->type != PW_TYPE_STRING) {
120                         continue;
121                 }
122
123                 /*
124                  *  No weird packing.  Ignore it.
125                  */
126                 ptr = strchr(vp->vp_strvalue, '='); /* find an '=' */
127                 if (!ptr) {
128                         continue;
129                 }
130
131                 /*
132                  *      Cisco-AVPair's get packed as:
133                  *
134                  *      Cisco-AVPair = "h323-foo-bar = baz"
135                  *      Cisco-AVPair = "h323-foo-bar=baz"
136                  *
137                  *      which makes sense only if you're a lunatic.
138                  *      This code looks for the attribute named inside
139                  *      of the string, and if it exists, adds it as a new
140                  *      attribute.
141                  */
142                 if (vp->da->attr == 1) {
143                         char const *p;
144
145                         p = vp->vp_strvalue;
146                         gettoken(&p, newattr, sizeof(newattr), false);
147
148                         if (dict_attrbyname(newattr) != NULL) {
149                                 pairmake_packet(newattr, ptr + 1, T_OP_EQ);
150                         }
151                 } else {        /* h322-foo-bar = "h323-foo-bar = baz" */
152                         /*
153                          *      We strip out the duplicity from the
154                          *      value field, we use only the value on
155                          *      the right side of the '=' character.
156                          */
157                         pairstrcpy(vp, ptr + 1);
158                 }
159         }
160 }
161
162
163 /*
164  *      Don't even ask what this is doing...
165  */
166 static void alvarion_vsa_hack(VALUE_PAIR *vp)
167 {
168         int number = 1;
169         vp_cursor_t cursor;
170
171         for (vp = fr_cursor_init(&cursor, &vp);
172              vp;
173              vp = fr_cursor_next(&cursor)) {
174                 DICT_ATTR const *da;
175
176                 if (vp->da->vendor != 12394) {
177                         continue;
178                 }
179
180                 if (vp->da->type != PW_TYPE_STRING) {
181                         continue;
182                 }
183
184                 da = dict_attrbyvalue(number, 12394);
185                 if (!da) {
186                         continue;
187                 }
188
189                 vp->da = da;
190
191                 number++;
192         }
193 }
194
195 /*
196  *      Cablelabs magic, taken from:
197  *
198  * http://www.cablelabs.com/packetcable/downloads/specs/PKT-SP-EM-I12-05812.pdf
199  *
200  *      Sample data is:
201  *
202  *      0x0001d2d2026d30310000000000003030
203  *        3130303030000e812333000100033031
204  *        00000000000030303130303030000000
205  *        00063230313230313331303630323231
206  *        2e3633390000000081000500
207  */
208
209 typedef struct cl_timezone_t {
210         uint8_t         dst;
211         uint8_t         sign;
212         uint8_t         hh[2];
213         uint8_t         mm[2];
214         uint8_t         ss[2];
215 } cl_timezone_t;
216
217 typedef struct cl_bcid_t {
218         uint32_t        timestamp;
219         uint8_t         element_id[8];
220         cl_timezone_t   timezone;
221         uint32_t        event_counter;
222 } cl_bcid_t;
223
224 typedef struct cl_em_hdr_t {
225         uint16_t        version;
226         cl_bcid_t       bcid;
227         uint16_t        message_type;
228         uint16_t        element_type;
229         uint8_t         element_id[8];
230         cl_timezone_t   time_zone;
231         uint32_t        sequence_number;
232         uint8_t         event_time[18];
233         uint8_t         status[4];
234         uint8_t         priority;
235         uint16_t        attr_count; /* of normal Cablelabs VSAs */
236         uint8_t         event_object;
237 } cl_em_hdr_t;
238
239
240 static void cablelabs_vsa_hack(VALUE_PAIR **list)
241 {
242         VALUE_PAIR *ev;
243
244         ev = pairfind(*list, 1, 4491, TAG_ANY); /* Cablelabs-Event-Message */
245         if (!ev) {
246                 return;
247         }
248
249         /*
250          *      FIXME: write 100's of lines of code to decode
251          *      each data structure above.
252          */
253 }
254
255 /*
256  *      Mangle username if needed, IN PLACE.
257  */
258 static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
259 {
260         int             num_proxy_state;
261         VALUE_PAIR      *namepair;
262         VALUE_PAIR      *request_pairs;
263         VALUE_PAIR      *tmp;
264         vp_cursor_t     cursor;
265
266         /*
267          *      Get the username from the request
268          *      If it isn't there, then we can't mangle the request.
269          */
270         request_pairs = request->packet->vps;
271         namepair = pairfind(request_pairs, PW_USER_NAME, 0, TAG_ANY);
272         if (!namepair || (namepair->length == 0)) {
273                 return;
274         }
275
276         if (inst->with_ntdomain_hack) {
277                 char *ptr;
278                 char newname[MAX_STRING_LEN];
279
280                 /*
281                  *      Windows NT machines often authenticate themselves as
282                  *      NT_DOMAIN\username. Try to be smart about this.
283                  *
284                  *      FIXME: should we handle this as a REALM ?
285                  */
286                 if ((ptr = strchr(namepair->vp_strvalue, '\\')) != NULL) {
287                         strlcpy(newname, ptr + 1, sizeof(newname));
288                         /* Same size */
289                         pairstrcpy(namepair, newname);
290                 }
291         }
292
293         if (inst->with_specialix_jetstream_hack) {
294                 /*
295                  *      Specialix Jetstream 8500 24 port access server.
296                  *      If the user name is 10 characters or longer, a "/"
297                  *      and the excess characters after the 10th are
298                  *      appended to the user name.
299                  *
300                  *      Reported by Lucas Heise <root@laonet.net>
301                  */
302                 if ((strlen(namepair->vp_strvalue) > 10) &&
303                     (namepair->vp_strvalue[10] == '/')) {
304                         pairstrcpy(namepair, namepair->vp_strvalue + 11);
305                 }
306         }
307
308         /*
309          *      Small check: if Framed-Protocol present but Service-Type
310          *      is missing, add Service-Type = Framed-User.
311          */
312         if (pairfind(request_pairs, PW_FRAMED_PROTOCOL, 0, TAG_ANY) != NULL &&
313             pairfind(request_pairs, PW_SERVICE_TYPE, 0, TAG_ANY) == NULL) {
314                 tmp = radius_paircreate(request->packet, &request->packet->vps, PW_SERVICE_TYPE, 0);
315                 tmp->vp_integer = PW_FRAMED_USER;
316         }
317
318         num_proxy_state = 0;
319         for (tmp = fr_cursor_init(&cursor, &request->packet->vps);
320              tmp;
321              tmp = fr_cursor_next(&cursor)) {
322                 if (tmp->da->vendor != 0) {
323                         continue;
324                 }
325
326                 if (tmp->da->attr != PW_PROXY_STATE) {
327                         continue;
328                 }
329
330                 num_proxy_state++;
331         }
332
333         if (num_proxy_state > 10) {
334                 RWDEBUG("There are more than 10 Proxy-State attributes in the request");
335                 RWDEBUG("You have likely configured an infinite proxy loop");
336         }
337 }
338
339 /*
340  *      Compare the request with the "reply" part in the
341  *      huntgroup, which normally only contains username or group.
342  *      At least one of the "reply" items has to match.
343  */
344 static int hunt_paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check)
345 {
346         vp_cursor_t     cursor;
347         VALUE_PAIR      *check_item;
348         VALUE_PAIR      *tmp;
349         int             result = -1;
350
351         if (!check) return 0;
352
353         for (check_item = fr_cursor_init(&cursor, &check);
354              check_item && (result != 0);
355              check_item = fr_cursor_next(&cursor)) {
356                 /* FIXME: paircopy should be removed once VALUE_PAIRs are no longer in linked lists */
357                 tmp = paircopyvp(request, check_item);
358                 tmp->op = check_item->op;
359                 result = paircompare(req, request, check_item, NULL);
360                 pairfree(&tmp);
361         }
362
363         return result;
364 }
365
366
367 /*
368  *      Add hints to the info sent by the terminal server
369  *      based on the pattern of the username, and other attributes.
370  */
371 static int hints_setup(PAIR_LIST *hints, REQUEST *request)
372 {
373         char const      *name;
374         VALUE_PAIR      *add;
375         VALUE_PAIR      *tmp;
376         PAIR_LIST       *i;
377         VALUE_PAIR      *request_pairs;
378         int             updated = 0, ft;
379
380         request_pairs = request->packet->vps;
381
382         if (!hints || !request_pairs)
383                 return RLM_MODULE_NOOP;
384
385         /*
386          *      Check for valid input, zero length names not permitted
387          */
388         name = (tmp = pairfind(request_pairs, PW_USER_NAME, 0, TAG_ANY)) ?
389                 tmp->vp_strvalue : NULL;
390         if (!name || name[0] == 0) {
391                 /*
392                  *      No name, nothing to do.
393                  */
394                 return RLM_MODULE_NOOP;
395         }
396
397         for (i = hints; i; i = i->next) {
398                 /*
399                  *      Use "paircompare", which is a little more general...
400                  */
401                 if (((strcmp(i->name, "DEFAULT") == 0) || (strcmp(i->name, name) == 0)) &&
402                     (paircompare(request, request_pairs, i->check, NULL) == 0)) {
403                         RDEBUG2("hints: Matched %s at %d", i->name, i->lineno);
404                         /*
405                          *      Now add all attributes to the request list,
406                          *      except PW_STRIP_USER_NAME and PW_FALL_THROUGH
407                          *      and xlat them.
408                          */
409                         add = paircopy(request->packet, i->reply);
410                         ft = fallthrough(add);
411
412                         pairdelete(&add, PW_STRIP_USER_NAME, 0, TAG_ANY);
413                         pairdelete(&add, PW_FALL_THROUGH, 0, TAG_ANY);
414                         radius_pairmove(request, &request->packet->vps, add, true);
415
416                         updated = 1;
417                         if (!ft) {
418                                 break;
419                         }
420                 }
421         }
422
423         if (updated == 0) {
424                 return RLM_MODULE_NOOP;
425         }
426
427         return RLM_MODULE_UPDATED;
428 }
429
430 /*
431  *      See if we have access to the huntgroup.
432  */
433 static int huntgroup_access(REQUEST *request, PAIR_LIST *huntgroups)
434 {
435         PAIR_LIST       *i;
436         int             r = RLM_MODULE_OK;
437         VALUE_PAIR      *request_pairs = request->packet->vps;
438
439         /*
440          *      We're not controlling access by huntgroups:
441          *      Allow them in.
442          */
443         if (!huntgroups) {
444                 return RLM_MODULE_OK;
445         }
446
447         for (i = huntgroups; i; i = i->next) {
448                 /*
449                  *      See if this entry matches.
450                  */
451                 if (paircompare(request, request_pairs, i->check, NULL) != 0) {
452                         continue;
453                 }
454
455                 /*
456                  *      Now check for access.
457                  */
458                 r = RLM_MODULE_REJECT;
459                 if (hunt_paircmp(request, request_pairs, i->reply) == 0) {
460                         VALUE_PAIR *vp;
461
462                         /*
463                          *  We've matched the huntgroup, so add it in
464                          *  to the list of request pairs.
465                          */
466                         vp = pairfind(request_pairs, PW_HUNTGROUP_NAME, 0, TAG_ANY);
467                         if (!vp) {
468                                 vp = radius_paircreate(request->packet, &request->packet->vps, PW_HUNTGROUP_NAME, 0);
469                                 pairstrcpy(vp, i->name);
470                         }
471                         r = RLM_MODULE_OK;
472                 }
473                 break;
474         }
475
476         return r;
477 }
478
479 /*
480  *      If the NAS wasn't smart enought to add a NAS-IP-Address
481  *      to the request, then add it ourselves.
482  */
483 static int add_nas_attr(REQUEST *request)
484 {
485         VALUE_PAIR *nas;
486
487         switch (request->packet->src_ipaddr.af) {
488         case AF_INET:
489                 nas = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
490                 if (!nas) {
491                         nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IP_ADDRESS, 0);
492                         nas->vp_ipaddr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
493                 }
494                 break;
495
496         case AF_INET6:
497                 nas = pairfind(request->packet->vps, PW_NAS_IPV6_ADDRESS, 0, TAG_ANY);
498                 if (!nas) {
499                         nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IPV6_ADDRESS, 0);
500                         memcpy(&nas->vp_ipv6addr, &request->packet->src_ipaddr.ipaddr,
501                                sizeof(request->packet->src_ipaddr.ipaddr));
502                 }
503                 break;
504
505         default:
506                 ERROR("Unknown address family for packet");
507                 return -1;
508         }
509
510         return 0;
511 }
512
513
514 /*
515  *      Initialize.
516  */
517 static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
518 {
519         int ret;
520         rlm_preprocess_t *inst = instance;
521
522         /*
523          *      Read the huntgroups file.
524          */
525         if (inst->huntgroup_file) {
526                 ret = pairlist_read(inst, inst->huntgroup_file, &(inst->huntgroups), 0);
527                 if (ret < 0) {
528                         ERROR("rlm_preprocess: Error reading %s", inst->huntgroup_file);
529
530                         return -1;
531                 }
532         }
533
534         /*
535          *      Read the hints file.
536          */
537         if (inst->hints_file) {
538                 ret = pairlist_read(inst, inst->hints_file, &(inst->hints), 0);
539                 if (ret < 0) {
540                         ERROR("rlm_preprocess: Error reading %s", inst->hints_file);
541
542                         return -1;
543                 }
544         }
545
546         return 0;
547 }
548
549 /*
550  *      Preprocess a request.
551  */
552 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
553 {
554         int r;
555         rlm_preprocess_t *inst = instance;
556
557         /*
558          *      Mangle the username, to get rid of stupid implementation
559          *      bugs.
560          */
561         rad_mangle(inst, request);
562
563         if (inst->with_ascend_hack) {
564                 /*
565                  *      If we're using Ascend systems, hack the NAS-Port-Id
566                  *      in place, to go from Ascend's weird values to something
567                  *      approaching rationality.
568                  */
569                 ascend_nasport_hack(pairfind(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY),
570                                     inst->ascend_channels_per_line);
571         }
572
573         if (inst->with_cisco_vsa_hack) {
574                 /*
575                  *      We need to run this hack because the h323-conf-id
576                  *      attribute should be used.
577                  */
578                 cisco_vsa_hack(request);
579         }
580
581         if (inst->with_alvarion_vsa_hack) {
582                 /*
583                  *      We need to run this hack because the Alvarion
584                  *      people are crazy.
585                  */
586                 alvarion_vsa_hack(request->packet->vps);
587         }
588
589         if (inst->with_cablelabs_vsa_hack) {
590                 /*
591                  *      We need to run this hack because the Cablelabs
592                  *      people are crazy.
593                  */
594                 cablelabs_vsa_hack(&request->packet->vps);
595         }
596
597         /*
598          *      Note that we add the Request-Src-IP-Address to the request
599          *      structure BEFORE checking huntgroup access.  This allows
600          *      the Request-Src-IP-Address to be used for huntgroup
601          *      comparisons.
602          */
603         if (add_nas_attr(request) < 0) {
604                 return RLM_MODULE_FAIL;
605         }
606
607         hints_setup(inst->hints, request);
608
609         /*
610          *      If there is a PW_CHAP_PASSWORD attribute but there
611          *      is PW_CHAP_CHALLENGE we need to add it so that other
612          *      modules can use it as a normal attribute.
613          */
614         if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
615             pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
616                 VALUE_PAIR *vp;
617
618                 vp = radius_paircreate(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
619                 pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
620         }
621
622         if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
623                 char buf[1024];
624                 RIDEBUG("No huntgroup access: [%s] (%s)",
625                         request->username ? request->username->vp_strvalue : "<NO User-Name>",
626                         auth_name(buf, sizeof(buf), request, 1));
627
628                 return r;
629         }
630
631         return RLM_MODULE_OK; /* Meaning: try next authorization module */
632 }
633
634 /*
635  *      Preprocess a request before accounting
636  */
637 static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
638 {
639         int r;
640         VALUE_PAIR *vp;
641         rlm_preprocess_t *inst = instance;
642
643         /*
644          *  Ensure that we have the SAME user name for both
645          *  authentication && accounting.
646          */
647         rad_mangle(inst, request);
648
649         if (inst->with_cisco_vsa_hack) {
650                 /*
651                  *      We need to run this hack because the h323-conf-id
652                  *      attribute should be used.
653                  */
654                 cisco_vsa_hack(request);
655         }
656
657         if (inst->with_alvarion_vsa_hack) {
658                 /*
659                  *      We need to run this hack because the Alvarion
660                  *      people are crazy.
661                  */
662                 alvarion_vsa_hack(request->packet->vps);
663         }
664
665         if (inst->with_cablelabs_vsa_hack) {
666                 /*
667                  *      We need to run this hack because the Cablelabs
668                  *      people are crazy.
669                  */
670                 cablelabs_vsa_hack(&request->packet->vps);
671         }
672
673         /*
674          *  Ensure that we log the NAS IP Address in the packet.
675          */
676         if (add_nas_attr(request) < 0) {
677                 return RLM_MODULE_FAIL;
678         }
679
680         hints_setup(inst->hints, request);
681
682         /*
683          *      Add an event timestamp.  This means that the rest of
684          *      the server can use it, rather than various error-prone
685          *      manual calculations.
686          */
687         vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
688         if (!vp) {
689                 VALUE_PAIR *delay;
690
691                 vp = radius_paircreate(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
692                 vp->vp_date = request->packet->timestamp.tv_sec;
693
694                 delay = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
695                 if (delay) {
696                         vp->vp_date -= delay->vp_integer;
697                 }
698         }
699
700         if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
701                 char buf[1024];
702                 RIDEBUG("No huntgroup access: [%s] (%s)",
703                         request->username ? request->username->vp_strvalue : "<NO User-Name>",
704                         auth_name(buf, sizeof(buf), request, 1));
705                 return r;
706         }
707
708         return r;
709 }
710
711 /* globally exported name */
712 module_t rlm_preprocess = {
713         RLM_MODULE_INIT,
714         "preprocess",
715         0,              /* type */
716         sizeof(rlm_preprocess_t),
717         module_config,
718         mod_instantiate,                        /* instantiation */
719         NULL,                                   /* detach */
720         {
721                 NULL,                           /* authentication */
722                 mod_authorize,                  /* authorization */
723                 mod_preaccounting,              /* pre-accounting */
724                 NULL,                           /* accounting */
725                 NULL,                           /* checksimul */
726                 NULL,                           /* pre-proxy */
727                 NULL,                           /* post-proxy */
728                 NULL                            /* post-auth */
729         },
730 };
731