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