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