Crypt thread patch from Oliver Graf <ograf@rz-online.net> (KEVAG Telekom GmbH /...
[freeradius.git] / src / main / auth.c
1 /*
2  * auth.c       User authentication.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Jeff Carneal <jeff@apex.net>
23  */
24
25 static const char rcsid[] = "$Id$";
26
27 #include "autoconf.h"
28 #include "libradius.h"
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33
34 #if HAVE_NETINET_IN_H
35 #       include <netinet/in.h>
36 #endif
37
38 #include "radiusd.h"
39 #include "modules.h"
40
41 /*
42  *      Return a short string showing the terminal server, port
43  *      and calling station ID.
44  */
45 char *auth_name(char *buf, size_t buflen, REQUEST *request, int do_cli) {
46         VALUE_PAIR      *cli;
47         VALUE_PAIR      *pair;
48         int             port = 0;
49
50         if ((cli = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) == NULL)
51                 do_cli = 0;
52         if ((pair = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL)
53                 port = pair->lvalue;
54
55         snprintf(buf, buflen, "from client %.128s port %u%s%.128s",
56                         client_name(request->packet->src_ipaddr), port,
57                         (do_cli ? " cli " : ""), (do_cli ? (char *)cli->strvalue : ""));
58
59         return buf;
60 }
61
62
63 /*
64  *      Check if account has expired, and if user may login now.
65  */
66 static int check_expiration(REQUEST *request)
67 {
68         int result;
69         VALUE_PAIR *check_item = request->config_items;
70
71         result = 0;
72         while (result == 0 && check_item != NULL) {
73
74                 /*
75                  *      Check expiration date if we are doing password aging.
76                  */
77                 if (check_item->attribute == PW_EXPIRATION) {
78                         /*
79                          *      Has this user's password expired?
80                          *
81                          *      If so, remove ALL reply attributes,
82                          *      and add our own Reply-Message, saying
83                          *      why they're being rejected.
84                          */
85                         if (check_item->lvalue < (unsigned) time(NULL)) {
86                                 VALUE_PAIR *vp;
87
88                                 result = -1;
89                                 vp = pairmake("Reply-Message",
90                                                 "Password Has Expired\r\n",
91                                                 T_OP_ADD);
92                                 pairfree(&request->reply->vps);
93                                 request->reply->vps = vp;
94                                 break;
95                         }
96                 }
97                 check_item = check_item->next;
98         }
99         return result;
100 }
101
102
103 /*
104  * Make sure user/pass are clean
105  * and then log them
106  */
107 static int rad_authlog(const char *msg, REQUEST *request, int goodpass) {
108
109         char clean_password[1024];
110         char clean_username[1024];
111         char buf[1024];
112         VALUE_PAIR *username = NULL;
113
114         if (!mainconfig.log_auth) {
115                 return 0;
116         }
117
118         /*
119          * Get the correct username based on the configured value
120          */
121         if (log_stripped_names == 0) {
122                 username = pairfind(request->packet->vps, PW_USER_NAME);
123         } else {
124                 username = request->username;
125         }
126
127         /* 
128          *      Clean up the username
129          */
130         if (username == NULL) {
131                 strcpy(clean_username, "<no User-Name attribute>");
132         } else {
133                 librad_safeprint((char *)username->strvalue,
134                                 username->length,
135                                 clean_username, sizeof(clean_username));
136         }
137
138         /* 
139          *      Clean up the password
140          */
141         if (mainconfig.log_auth_badpass || mainconfig.log_auth_goodpass) {
142                 if (!request->password) {
143                         strcpy(clean_password, "<no User-Password attribute>");
144                 } else if (request->password->attribute == PW_CHAP_PASSWORD) {
145                         strcpy(clean_password, "<CHAP-Password>");
146                 } else {
147                         librad_safeprint((char *)request->password->strvalue,
148                                          request->password->length,
149                                          clean_password, sizeof(clean_password));
150                 }
151         }
152
153         if (goodpass) {
154                 radlog(L_AUTH, "%s: [%s%s%s] (%s)",
155                                 msg,    
156                                 clean_username,
157                                 mainconfig.log_auth_goodpass ? "/" : "",
158                                 mainconfig.log_auth_goodpass ? clean_password : "",
159                                 auth_name(buf, sizeof(buf), request, 1));
160         } else {
161                 radlog(L_AUTH, "%s: [%s%s%s] (%s)",
162                                 msg,    
163                                 clean_username,
164                                 mainconfig.log_auth_badpass ? "/" : "",
165                                 mainconfig.log_auth_badpass ? clean_password : "",
166                                 auth_name(buf, sizeof(buf), request, 1));
167         }
168         
169         return 0;
170 }
171
172 /*
173  *      Check password.
174  *
175  *      Returns:        0  OK
176  *                      -1 Password fail
177  *                      -2 Rejected (Auth-Type = Reject, send Port-Message back)
178  *                      1  End check & return, don't reply
179  *
180  *      NOTE: NOT the same as the RLM_ values !
181  */
182 int rad_check_password(REQUEST *request)
183 {
184         VALUE_PAIR *auth_type_pair;
185         VALUE_PAIR *cur_config_item;
186         VALUE_PAIR *password_pair;
187         VALUE_PAIR *auth_item;
188         char string[MAX_STRING_LEN];
189         int auth_type = -1;
190         int result;
191         int auth_type_count = 0;
192         result = 0;
193
194         /*
195          *      Look for matching check items. We skip the whole lot
196          *      if the authentication type is PW_AUTHTYPE_ACCEPT or
197          *      PW_AUTHTYPE_REJECT.
198          */
199         cur_config_item = request->config_items;
200         while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE))) != NULL) {
201                 auth_type = auth_type_pair->lvalue;
202                 auth_type_count++;
203
204                 DEBUG2("  rad_check_password:  Found Auth-Type %s",
205                                 auth_type_pair->strvalue);
206                 cur_config_item = auth_type_pair->next;
207
208                 if (auth_type == PW_AUTHTYPE_REJECT) {
209                         DEBUG2("  rad_check_password: Auth-Type = Reject, rejecting user");
210                         return -2;
211                 }
212         }
213
214         if (( auth_type_count > 1) && (debug_flag)) {
215                 radlog(L_ERR, "Warning:  Found %d auth-types on request for user '%s'", 
216                         auth_type_count, request->username->strvalue);
217         }
218
219         /*
220          *      This means we have a proxy reply or an accept  
221          *  and it wasn't rejected in the above loop.  So 
222          *  that means it is accepted and we do no further 
223          *  authentication
224          */
225         if ((auth_type == PW_AUTHTYPE_ACCEPT) || (request->proxy)) {
226                 DEBUG2("  rad_check_password: Auth-Type = Accept, accepting the user");
227                 return 0;
228         }
229
230         /*
231          *      Find the password from the users file.
232          */
233         if ((password_pair = pairfind(request->config_items, PW_CRYPT_PASSWORD)) != NULL)
234                 auth_type = PW_AUTHTYPE_CRYPT;
235         else
236                 password_pair = pairfind(request->config_items, PW_PASSWORD);
237
238         if (auth_type < 0) {
239                 if (password_pair) {
240                         auth_type = PW_AUTHTYPE_LOCAL;
241                 } else {
242                         /*
243                         *       The admin hasn't told us how to
244                         *       authenticate the user, so we reject them!
245                         *
246                         *       This is fail-safe.
247                         */
248                         DEBUG2("auth: No authenticate method (Auth-Type) configuration found for the request: Rejecting the user");
249                         return -2;
250                 }
251         }
252
253         switch(auth_type) {
254                 case PW_AUTHTYPE_CRYPT:
255                         /*
256                          *      Find the password sent by the user. It
257                          *      SHOULD be there, if it's not
258                          *      authentication fails.
259                          */
260                         auth_item = request->password;
261                         if (auth_item == NULL) {
262                                 DEBUG2("auth: No User-Password or CHAP-Password attribute in the request");
263                                 return -1;
264                         }
265
266                         DEBUG2("auth: type Crypt");
267                         if (password_pair == NULL) {
268                                 DEBUG2("No Crypt-Password configured for the user");
269                                 rad_authlog("Login incorrect "
270                                         "(No Crypt-Password configured for the user)", request, 0);
271                                 return -1;
272                         }
273                                         
274                         switch (lrad_crypt_check((char *)auth_item->strvalue,
275                                                                          (char *)password_pair->strvalue)) {
276                         case -1:
277                           rad_authlog("Login incorrect "
278                                                   "(system failed to supply an encrypted password for comparison)", request, 0);
279                         case 1:
280                           return -1;
281                         }
282                         break;
283                 case PW_AUTHTYPE_LOCAL:
284                         DEBUG2("auth: type Local");
285
286                         /*
287                          *      Find the password sent by the user. It
288                          *      SHOULD be there, if it's not
289                          *      authentication fails.
290                          */
291                         auth_item = request->password;
292                         if (auth_item == NULL) {
293                                 DEBUG2("auth: No User-Password or CHAP-Password attribute in the request");
294                                 return -1;
295                         }
296
297                         /*
298                          *      Plain text password.
299                          */
300                         if (password_pair == NULL) {
301                                 DEBUG2("auth: No password configured for the user");
302                                 rad_authlog("Login incorrect "
303                                         "(No password configured for the user)", request, 0);
304                                 return -1;
305                         }
306
307                         /*
308                          *      Local password is just plain text.
309                          */
310                         if (auth_item->attribute == PW_PASSWORD) {
311                                 if (strcmp((char *)password_pair->strvalue,
312                                            (char *)auth_item->strvalue) != 0) {
313                                         DEBUG2("auth: user supplied User-Password does NOT match local User-Password");
314                                         return -1;
315                                 }
316                                 DEBUG2("auth: user supplied User-Password matches local User-Password");
317                                 break;
318
319                         } else if (auth_item->attribute != PW_CHAP_PASSWORD) {
320                                 DEBUG2("The user did not supply a User-Password or a CHAP-Password attribute");
321                                 rad_authlog("Login incorrect "
322                                         "(no User-Password or CHAP-Password attribute)", request, 0);
323                                 return -1;
324                         }
325
326                         rad_chap_encode(request->packet, string,
327                                         auth_item->strvalue[0], password_pair);
328
329                         /*
330                          *      Compare them
331                          */
332                         if (memcmp(string + 1, auth_item->strvalue + 1,
333                                    CHAP_VALUE_LENGTH) != 0) {
334                                 DEBUG2("auth: user supplied CHAP-Password does NOT match local User-Password");
335                                 return -1;
336                         }
337                         DEBUG2("auth: user supplied CHAP-Password matches local User-Password");
338                         break;
339                 default:
340                         DEBUG2("auth: type \"%s\"",
341                                         dict_valbyattr(PW_AUTH_TYPE, auth_type)->name);
342                         /*
343                          *      See if there is a module that handles
344                          *      this type, and turn the RLM_ return
345                          *      status into the values as defined at
346                          *      the top of this function.
347                          */
348                         result = module_authenticate(auth_type, request);
349                         switch (result) {
350                                 /*
351                                  *      An authentication module FAIL
352                                  *      return code, or any return code that
353                                  *      is not expected from authentication,
354                                  *      is the same as an explicit REJECT!
355                                  */
356                                 case RLM_MODULE_FAIL:
357                                 case RLM_MODULE_REJECT:
358                                 case RLM_MODULE_USERLOCK:
359                                 case RLM_MODULE_INVALID:
360                                 case RLM_MODULE_NOTFOUND:
361                                 case RLM_MODULE_NOOP:
362                                 case RLM_MODULE_UPDATED:
363                                         result = -1;
364                                         break;
365                                 case RLM_MODULE_OK:
366                                         result = 0;
367                                         break;
368                                 case RLM_MODULE_HANDLED:
369                                         result = 1;
370                                         break;
371                         }
372                         break;
373         }
374
375         return result;
376 }
377
378 /*
379  *      Process and reply to an authentication request
380  *
381  *      The return value of this function isn't actually used right now, so
382  *      it's not entirely clear if it is returning the right things. --Pac.
383  */
384 int rad_authenticate(REQUEST *request)
385 {
386         VALUE_PAIR      *namepair;
387         VALUE_PAIR      *check_item;
388         VALUE_PAIR      *reply_item;
389         VALUE_PAIR      *auth_item;
390         VALUE_PAIR      *module_msg;
391         VALUE_PAIR      *tmp = NULL;
392         VALUE_PAIR      *autz_type_item = NULL;
393         VALUE_PAIR      *postauth_type_item = NULL;
394         int             result, r;
395         char            umsg[MAX_STRING_LEN + 1];
396         const char      *user_msg = NULL;
397         const char      *password;
398         char            *exec_program;
399         int             exec_wait;
400         int             seen_callback_id;
401         char            buf[1024], logstr[1024];
402         char            autz_retry = 0;
403         int             autz_type = 0;
404         int             postauth_type = 0;
405
406         password = "";
407
408         /*
409          *      If this request got proxied to another server,
410          *      AND it was an authentication request, then we need
411          *      to add an initial Auth-Type: Auth-Accept for success,
412          *      Auth-Reject for fail. We also need to add the reply
413          *      pairs from the server to the initial reply.
414          */
415         if ((request->proxy_reply) &&
416             (request->packet->code == PW_AUTHENTICATION_REQUEST)) {
417                 tmp = paircreate(PW_AUTH_TYPE, PW_TYPE_INTEGER);
418                 if (tmp == NULL) {
419                         radlog(L_ERR|L_CONS, "no memory");
420                         exit(1);
421                 }
422
423                 /*
424                  *      Challenges are punted back to the NAS
425                  *      without any further processing.
426                  */
427                 if (request->proxy_reply->code == PW_ACCESS_CHALLENGE) {
428                         request->reply->code = PW_ACCESS_CHALLENGE;
429                         return RLM_MODULE_HANDLED;
430                 }
431
432                 /*
433                  *      Reply of ACCEPT means accept, ALL other
434                  *      replies mean reject.  This is fail-safe.
435                  */
436                 if (request->proxy_reply->code == PW_AUTHENTICATION_ACK)
437                         tmp->lvalue = PW_AUTHTYPE_ACCEPT;
438                 else
439                         tmp->lvalue = PW_AUTHTYPE_REJECT;
440                 pairadd(&request->config_items, tmp);
441
442                 /*
443                  *      If it's an Access-Reject, then do NOT do any
444                  *      authorization or authentication.  They're being
445                  *      rejected, so we minimize the amount of work
446                  *      done by the server, by rejecting them here.
447                  */
448                 if ((request->proxy_reply->code != PW_AUTHENTICATION_ACK) &&
449                     (request->proxy_reply->code != PW_ACCESS_CHALLENGE)) {
450                         rad_authlog("Login incorrect (Home Server says so)", request, 0);
451                         request->reply->code = PW_AUTHENTICATION_REJECT;
452                         return RLM_MODULE_REJECT;
453                 }
454         }
455
456         /*
457          *      Get the username from the request.
458          *
459          *      Note that namepair MAY be NULL, in which case there
460          *      is no User-Name attribute in the request.
461          */
462         namepair = request->username;
463
464         /*
465          *      Look for, and cache, passwords.
466          */
467         if (!request->password) {
468                 request->password = pairfind(request->packet->vps,
469                                              PW_PASSWORD);
470         }
471           
472         /*
473          *      Discover which password we want to use.
474          */
475         auth_item = request->password;
476         if (auth_item) {
477                 password = (const char *)auth_item->strvalue;
478
479         } else {
480                 /*
481                  *      Maybe there's a CHAP-Password?
482                  */
483                 if ((auth_item = pairfind(request->packet->vps, 
484                                 PW_CHAP_PASSWORD)) != NULL) {
485                         password = "<CHAP-PASSWORD>";
486                 
487                 } else {
488                         /*
489                          *      No password we recognize.
490                          */
491                         password = "<NO-PASSWORD>";
492                 }
493         }
494         request->password = auth_item;
495         
496         /*
497          *      Get the user's authorization information from the database
498          */
499 autz_redo:
500         r = module_authorize(autz_type, request);
501         if (r != RLM_MODULE_NOTFOUND &&
502             r != RLM_MODULE_NOOP &&
503             r != RLM_MODULE_OK &&
504             r != RLM_MODULE_UPDATED) {
505                 if (r != RLM_MODULE_FAIL && r != RLM_MODULE_HANDLED) {
506                         if ((module_msg = pairfind(request->packet->vps,
507                                         PW_MODULE_FAILURE_MESSAGE)) != NULL){
508                                 char msg[MAX_STRING_LEN+16];
509                                 snprintf(msg, sizeof(msg), "Invalid user (%s)",
510                                          module_msg->strvalue);
511                                 rad_authlog(msg,request,0);
512                         } else {
513                                 rad_authlog("Invalid user", request, 0);
514                         }
515                         request->reply->code = PW_AUTHENTICATION_REJECT;
516                 }
517
518                 /*
519                  *      Hope that the module returning REJECT is smart
520                  *      enough to do pairfre(&request->reply->vps)...
521                  */
522                 return r;
523         }
524         if (!autz_retry){
525                 autz_type_item = pairfind(request->config_items, PW_AUTZ_TYPE);
526                 if (autz_type_item){
527                         autz_type = autz_type_item->lvalue;
528                         autz_retry = 1;
529                         goto autz_redo;
530                 }
531         }
532
533
534         /*
535          *      If we haven't already proxied the packet, then check
536          *      to see if we should.  Maybe one of the authorize
537          *      modules has decided that a proxy should be used. If
538          *      so, get out of here and send the packet.
539          */
540         if ((request->proxy == NULL) &&
541                         (pairfind(request->config_items, PW_PROXY_TO_REALM) != NULL)) {
542                 return RLM_MODULE_OK;
543         }
544
545         /*
546          *      Perhaps there is a Stripped-User-Name now.
547          */
548         namepair = request->username;
549
550         /*
551          *      Validate the user
552          */
553         do {
554                 if ((result = check_expiration(request)) < 0)
555                                 break;
556                 result = rad_check_password(request);
557                 if (result > 0) {
558                         /* don't reply! */
559                         return RLM_MODULE_HANDLED;
560                 }
561         } while(0);
562         
563         /*
564          *      Failed to validate the user.
565          *
566          *      We PRESUME that the code which failed will clean up
567          *      request->reply->vps, to be ONLY the reply items it
568          *      wants to send back.
569          */
570         if (result < 0) {
571                 DEBUG2("auth: Failed to validate the user.");
572                 request->reply->code = PW_AUTHENTICATION_REJECT;
573
574                 if ((module_msg = pairfind(request->packet->vps,PW_MODULE_FAILURE_MESSAGE)) != NULL){
575                         char msg[MAX_STRING_LEN+19];
576
577                         snprintf(msg, sizeof(msg), "Login incorrect (%s)",
578                                  module_msg->strvalue);
579                         rad_authlog(msg, request, 0);
580                 } else {
581                         rad_authlog("Login incorrect", request, 0);
582                 }
583
584                 /* double check: maybe the secret is wrong? */
585                 if ((debug_flag > 1) && (auth_item != NULL) &&
586                                 (auth_item->attribute == PW_PASSWORD)) {
587                         u_char *p;
588
589                         p = auth_item->strvalue;
590                         while (*p != '\0') {
591                                 if (!isprint((int) *p)) {
592                                         log_debug("  WARNING: Unprintable characters in the password.\n\t  Double-check the shared secret on the server and the NAS!");
593                                         break;
594                                 }
595                                 p++;
596                         }
597                 }
598         }
599
600         if (result >= 0 &&
601                         (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE)) != NULL) {
602                 VALUE_PAIR      *session_type;
603                 int             sess_type = 0;
604
605                 session_type = pairfind(request->config_items, PW_SESSION_TYPE);
606                 if (session_type)
607                         sess_type = session_type->lvalue;
608
609                 /*
610                  *      User authenticated O.K. Now we have to check
611                  *      for the Simultaneous-Use parameter.
612                  */
613                 if (namepair &&
614                     (r = module_checksimul(sess_type,request, check_item->lvalue)) != 0) {
615                         char mpp_ok = 0;
616
617                         if (r == 2){
618                                 /* Multilink attempt. Check if port-limit > simultaneous-use */
619                                 VALUE_PAIR *port_limit;
620
621                                 if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT)) != NULL &&
622                                         port_limit->lvalue > check_item->lvalue){
623                                         DEBUG2("main auth: MPP is OK");
624                                         mpp_ok = 1;
625                                 }
626                         }
627                         if (!mpp_ok){
628                                 if (check_item->lvalue > 1) {
629                                 snprintf(umsg, sizeof(umsg), 
630                                                         "\r\nYou are already logged in %d times  - access denied\r\n\n",
631                                                         (int)check_item->lvalue);
632                                         user_msg = umsg;
633                                 } else {
634                                         user_msg = "\r\nYou are already logged in - access denied\r\n\n";
635                                 }
636
637                                 request->reply->code = PW_AUTHENTICATION_REJECT;
638
639                                 /*
640                                  *      They're trying to log in too many times.
641                                  *      Remove ALL reply attributes.
642                                  */
643                                 pairfree(&request->reply->vps);
644                                 tmp = pairmake("Reply-Message", user_msg, T_OP_SET);
645                                 request->reply->vps = tmp;
646
647                                 snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
648                                         check_item->lvalue,
649                                         r == 2 ? "[MPP attempt]" : "");
650                                 rad_authlog(logstr, request, 1);
651
652                                 result = -1;
653                         }
654                 }
655         }
656
657         if (result >= 0 &&
658             (check_item = pairfind(request->config_items, PW_LOGIN_TIME)) != NULL) {
659
660                 /*
661                  *      Authentication is OK. Now see if this
662                  *      user may login at this time of the day.
663                  */
664                 r = timestr_match((char *)check_item->strvalue,
665                                   request->timestamp);
666                 /*
667                  *      Session-Timeout needs to be at least
668                  *      60 seconds, some terminal servers
669                  *      ignore smaller values.
670                  */
671                 if (r < 60) {
672                         /*
673                          *      User called outside allowed time interval.
674                          */
675                         result = -1;
676                         user_msg = "You are calling outside your allowed timespan\r\n";
677
678                         request->reply->code = PW_AUTHENTICATION_REJECT;
679                         pairfree(&request->reply->vps);
680
681                         tmp = pairmake("Reply-Message", user_msg, T_OP_SET);
682                         request->reply->vps = tmp;
683
684                         snprintf(logstr, sizeof(logstr), "Outside allowed timespan (time allowed %s)", 
685                                  check_item->strvalue);
686                         rad_authlog(logstr, request, 1);
687
688                 } else if (r > 0) {
689
690                         /*
691                          *      User is allowed, but set Session-Timeout.
692                          */
693                         if ((reply_item = pairfind(request->reply->vps,
694                                         PW_SESSION_TIMEOUT)) != NULL) {
695                                 if (reply_item->lvalue > (unsigned) r)
696                                         reply_item->lvalue = r;
697                         } else {
698                                 if ((reply_item = paircreate(
699                                                 PW_SESSION_TIMEOUT,
700                                                 PW_TYPE_INTEGER)) == NULL) {
701                                         radlog(L_ERR|L_CONS, "no memory");
702                                         exit(1);
703                                 }
704                                 reply_item->lvalue = r;
705                                 pairadd(&request->reply->vps, reply_item);
706                         }
707                 }
708         }
709
710         /*
711          *      Result should be >= 0 here - if not, we return.
712          */
713         if (result < 0) {
714                 return RLM_MODULE_OK;
715         }
716
717         /*
718          *      We might need this later.  The 'password' string
719          *      is NOT used anywhere below here, except for logging,
720          *      so it should be safe...
721          */
722         if ((auth_item != NULL) && (auth_item->attribute == PW_CHAP_PASSWORD)) {
723                 password = "CHAP-Password";
724         }
725
726         /*
727          *      Add the port number to the Framed-IP-Address if
728          *      vp->addport is set.
729          */
730         if (((tmp = pairfind(request->reply->vps, 
731                              PW_FRAMED_IP_ADDRESS)) != NULL) &&
732             (tmp->flags.addport != 0)) {
733                 VALUE_PAIR *vpPortId;
734                 
735                 /*
736                  *  Find the NAS port ID.
737                  */
738                 if ((vpPortId = pairfind(request->packet->vps,
739                                          PW_NAS_PORT)) != NULL) {
740                   tmp->lvalue = htonl(ntohl(tmp->lvalue) + vpPortId->lvalue);
741                   tmp->flags.addport = 0;
742                   ip_ntoa(tmp->strvalue, tmp->lvalue);
743                 } else {
744                         DEBUG2("WARNING: No NAS-Port attribute in request.  CANNOT return a Framed-IP-Address + NAS-Port.\n");
745                         pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
746                 }
747         }
748
749         /*
750          *      See if we need to execute a program.
751          *      FIXME: somehow cache this info, and only execute the
752          *      program when we receive an Accounting-START packet.
753          *      Only at that time we know dynamic IP etc.
754          */
755         exec_program = NULL;
756         exec_wait = 0;
757         if ((auth_item = pairfind(request->reply->vps, PW_EXEC_PROGRAM)) != NULL) {
758                 exec_wait = 0;
759                 exec_program = strdup((char *)auth_item->strvalue);
760                 pairdelete(&request->reply->vps, PW_EXEC_PROGRAM);
761         }
762         if ((auth_item = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT)) != NULL) {
763                 exec_wait = 1;
764                 exec_program = strdup((char *)auth_item->strvalue);
765                 pairdelete(&request->reply->vps, PW_EXEC_PROGRAM_WAIT);
766         }
767
768         /*
769          *      Hack - allow % expansion in certain value strings.
770          *      This is nice for certain Exec-Program programs.
771          */
772         seen_callback_id = 0;
773         if ((auth_item = pairfind(request->reply->vps, PW_CALLBACK_ID)) != NULL) {
774                 seen_callback_id = 1;
775                 radius_xlat(buf, sizeof(auth_item->strvalue),
776                             (char *)auth_item->strvalue, request, NULL);
777                 strNcpy((char *)auth_item->strvalue, buf,
778                         sizeof(auth_item->strvalue));
779                 auth_item->length = strlen((char *)auth_item->strvalue);
780         }
781
782
783         /*
784          *      If we want to exec a program, but wait for it,
785          *      do it first before sending the reply.
786          */
787         if (exec_program && exec_wait) {
788                 r = radius_exec_program(exec_program, request,
789                                         exec_wait,
790                                         umsg, sizeof(umsg),
791                                         request->packet->vps, &tmp);
792                 free(exec_program);
793                 exec_program = NULL;
794
795                 /*
796                  *      Always add the value-pairs to the reply.
797                  */
798                 pairmove(&request->reply->vps, &tmp);
799                 pairfree(&tmp);
800
801                 if (r != 0) {
802                         /*
803                          *      Error. radius_exec_program() returns -1 on
804                          *      fork/exec errors, or >0 if the exec'ed program
805                          *      had a non-zero exit status.
806                          */
807                         if (umsg[0] == '\0') {
808                                 user_msg = "\r\nAccess denied (external check failed).";
809                         } else {
810                                 user_msg = &umsg[0];
811                         }
812
813                         request->reply->code = PW_AUTHENTICATION_REJECT;
814                         tmp = pairmake("Reply-Message", user_msg, T_OP_SET);
815
816                         pairadd(&request->reply->vps, tmp);
817                         rad_authlog("Login incorrect (external check failed)", 
818                                         request, 0);
819
820                         return RLM_MODULE_REJECT;
821                 }
822         }
823
824         /*
825          *      Delete "normal" A/V pairs when using callback.
826          *
827          *      FIXME: This is stupid. The portmaster should accept
828          *      these settings instead of insisting on using a
829          *      dialout location.
830          *
831          *      FIXME2: Move this into the above exec thingy?
832          *      (if you knew how I use the exec_wait, you'd understand).
833          */
834         if (seen_callback_id) {
835                 pairdelete(&request->reply->vps, PW_FRAMED_PROTOCOL);
836                 pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
837                 pairdelete(&request->reply->vps, PW_FRAMED_IP_NETMASK);
838                 pairdelete(&request->reply->vps, PW_FRAMED_ROUTE);
839                 pairdelete(&request->reply->vps, PW_FRAMED_MTU);
840                 pairdelete(&request->reply->vps, PW_FRAMED_COMPRESSION);
841                 pairdelete(&request->reply->vps, PW_FILTER_ID);
842                 pairdelete(&request->reply->vps, PW_PORT_LIMIT);
843                 pairdelete(&request->reply->vps, PW_CALLBACK_NUMBER);
844         }
845
846         /*
847          *      Filter (possibly multiple) Reply-Message attributes
848          *      through radius_xlat, modifying them in place.
849          */
850         if (user_msg == NULL) {
851                 reply_item = pairfind(request->reply->vps, PW_REPLY_MESSAGE);
852                 while (reply_item) {
853                         radius_xlat(buf, sizeof(reply_item->strvalue),
854                                     (char *)reply_item->strvalue, request, NULL);
855                         strNcpy((char *)reply_item->strvalue, buf,
856                                 sizeof(reply_item->strvalue));
857                         reply_item->length = strlen((char *)reply_item->strvalue);
858                         user_msg = NULL;
859                         reply_item = pairfind(reply_item->next, PW_REPLY_MESSAGE);
860                 }
861         }
862
863         /*
864          *      Set the reply to Access-Accept, if it hasn't already
865          *      been set to something.  (i.e. Access-Challenge)
866          */
867         if (request->reply->code == 0)
868           request->reply->code = PW_AUTHENTICATION_ACK;
869
870         if ((module_msg = pairfind(request->packet->vps,PW_MODULE_SUCCESS_MESSAGE)) != NULL){
871                 char msg[MAX_STRING_LEN+12];
872
873                 snprintf(msg, sizeof(msg), "Login OK (%s)",
874                          module_msg->strvalue);
875                 rad_authlog(msg, request, 1);
876         } else {
877                 rad_authlog("Login OK", request, 1);
878         }
879
880         if (exec_program && !exec_wait) {
881                 /*
882                  *      No need to check the exit status here.
883                  */
884                 radius_exec_program(exec_program, request, exec_wait,
885                                     NULL, 0, request->packet->vps, NULL);
886         }
887
888         if (exec_program) 
889                 free(exec_program);
890
891         /*
892          *      Do post-authentication calls. ignoring the return code.
893          *      If the post-authentication
894          */
895         postauth_type_item = pairfind(request->config_items, PW_POST_AUTH_TYPE);
896         if (postauth_type_item)
897                 postauth_type = postauth_type_item->lvalue;
898         result = module_post_auth(postauth_type, request);
899         switch (result) {
900         default:
901           break;
902           
903           /*
904            *    The module failed, or said to reject the user: Do so.
905            */
906         case RLM_MODULE_FAIL:
907         case RLM_MODULE_REJECT:
908         case RLM_MODULE_USERLOCK:
909         case RLM_MODULE_INVALID:
910           request->reply->code = PW_AUTHENTICATION_REJECT;
911           result = RLM_MODULE_REJECT;
912           break;
913
914           /*
915            *    The module had a number of OK return codes.
916            */
917         case RLM_MODULE_NOTFOUND:
918         case RLM_MODULE_NOOP:
919         case RLM_MODULE_UPDATED:
920         case RLM_MODULE_OK:
921         case RLM_MODULE_HANDLED:
922           result = RLM_MODULE_OK;
923           break;
924         }
925
926         return result;
927 }