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