Add internal API for rlm_cache module
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Jeff Carneal <jeff@apex.net>
23  */
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/state.h>
29 #include <freeradius-devel/rad_assert.h>
30
31 #include <ctype.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, bool do_cli)
38 {
39         VALUE_PAIR      *cli;
40         VALUE_PAIR      *pair;
41         uint16_t        port = 0;
42         char const      *tls = "";
43
44         if ((cli = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) == NULL) {
45                 do_cli = false;
46         }
47
48         if ((pair = pairfind(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY)) != NULL) {
49                 port = pair->vp_integer;
50         }
51
52         if (request->packet->dst_port == 0) {
53                 if (pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY)) {
54                         tls = " via TLS tunnel";
55                 } else {
56                         tls = " via proxy to virtual server";
57                 }
58         }
59
60         snprintf(buf, buflen, "from client %.128s port %u%s%.128s%s",
61                         request->client->shortname, port,
62                  (do_cli ? " cli " : ""), (do_cli ? cli->vp_strvalue : ""),
63                  tls);
64
65         return buf;
66 }
67
68
69
70 /*
71  * Make sure user/pass are clean
72  * and then log them
73  */
74 static int rad_authlog(char const *msg, REQUEST *request, int goodpass)
75 {
76         int logit;
77         char const *extra_msg = NULL;
78         char clean_password[1024];
79         char clean_username[1024];
80         char buf[1024];
81         char extra[1024];
82         char *p;
83         VALUE_PAIR *username = NULL;
84
85         if (!request->root->log_auth) {
86                 return 0;
87         }
88
89         /*
90          * Get the correct username based on the configured value
91          */
92         if (!log_stripped_names) {
93                 username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
94         } else {
95                 username = request->username;
96         }
97
98         /*
99          *      Clean up the username
100          */
101         if (username == NULL) {
102                 strcpy(clean_username, "<no User-Name attribute>");
103         } else {
104                 fr_print_string(username->vp_strvalue,
105                                 username->length,
106                                 clean_username, sizeof(clean_username), '\0');
107         }
108
109         /*
110          *      Clean up the password
111          */
112         if (request->root->log_auth_badpass || request->root->log_auth_goodpass) {
113                 if (!request->password) {
114                         VALUE_PAIR *auth_type;
115
116                         auth_type = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
117                         if (auth_type) {
118                                 snprintf(clean_password, sizeof(clean_password),
119                                          "<via Auth-Type = %s>",
120                                          dict_valnamebyattr(PW_AUTH_TYPE, 0,
121                                                             auth_type->vp_integer));
122                         } else {
123                                 strcpy(clean_password, "<no User-Password attribute>");
124                         }
125                 } else if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
126                         strcpy(clean_password, "<CHAP-Password>");
127                 } else {
128                         fr_print_string(request->password->vp_strvalue,
129                                          request->password->length,
130                                         clean_password, sizeof(clean_password), '\0');
131                 }
132         }
133
134         if (goodpass) {
135                 logit = request->root->log_auth_goodpass;
136                 extra_msg = request->root->auth_goodpass_msg;
137         } else {
138                 logit = request->root->log_auth_badpass;
139                 extra_msg = request->root->auth_badpass_msg;
140         }
141
142         if (extra_msg) {
143                 extra[0] = ' ';
144                 p = extra + 1;
145                 if (radius_xlat(p, sizeof(extra) - 1, request, extra_msg, NULL, NULL) < 0) {
146                         return -1;
147                 }
148         } else {
149                 *extra = '\0';
150         }
151
152         RAUTH("%s: [%s%s%s] (%s)%s",
153                        msg,
154                        clean_username,
155                        logit ? "/" : "",
156                        logit ? clean_password : "",
157                        auth_name(buf, sizeof(buf), request, 1),
158                        extra);
159
160         return 0;
161 }
162
163 /*
164  *      Check password.
165  *
166  *      Returns:        0  OK
167  *                      -1 Password fail
168  *                      -2 Rejected (Auth-Type = Reject, send Port-Message back)
169  *                      1  End check & return, don't reply
170  *
171  *      NOTE: NOT the same as the RLM_ values !
172  */
173 static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
174 {
175         vp_cursor_t cursor;
176         VALUE_PAIR *auth_type_pair;
177         int auth_type = -1;
178         int result;
179         int auth_type_count = 0;
180
181         /*
182          *      Look for matching check items. We skip the whole lot
183          *      if the authentication type is PW_AUTHTYPE_ACCEPT or
184          *      PW_AUTHTYPE_REJECT.
185          */
186         fr_cursor_init(&cursor, &request->config_items);
187         while ((auth_type_pair = fr_cursor_next_by_num(&cursor, PW_AUTH_TYPE, 0, TAG_ANY))) {
188                 auth_type = auth_type_pair->vp_integer;
189                 auth_type_count++;
190
191                 RDEBUG2("Found Auth-Type = %s", dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type));
192                 if (auth_type == PW_AUTHTYPE_REJECT) {
193                         RDEBUG2("Auth-Type = Reject, rejecting user");
194
195                         return -2;
196                 }
197         }
198
199         /*
200          *      Warn if more than one Auth-Type was found, because only the last
201          *      one found will actually be used.
202          */
203         if ((auth_type_count > 1) && (debug_flag)) {
204                 RERROR("Warning:  Found %d auth-types on request for user '%s'",
205                         auth_type_count, request->username->vp_strvalue);
206         }
207
208         /*
209          *      This means we have a proxy reply or an accept and it wasn't
210          *      rejected in the above loop. So that means it is accepted and we
211          *      do no further authentication.
212          */
213         if ((auth_type == PW_AUTHTYPE_ACCEPT)
214 #ifdef WITH_PROXY
215             || (request->proxy)
216 #endif
217             ) {
218                 RDEBUG2("Auth-Type = Accept, accepting the user");
219                 return 0;
220         }
221
222         /*
223          *      Check that Auth-Type has been set, and reject if not.
224          *
225          *      Do quick checks to see if Cleartext-Password or Crypt-Password have
226          *      been set, and complain if so.
227          */
228         if (auth_type < 0) {
229                 if (pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) {
230                         RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Crypt'");
231                         RWDEBUG2("Use the PAP module instead");
232                 }
233                 else if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) {
234                         RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Local'");
235                         RWDEBUG2("Use the PAP or CHAP modules instead");
236                 }
237
238                 /*
239                  *      The admin hasn't told us how to
240                  *      authenticate the user, so we reject them!
241                  *
242                  *      This is fail-safe.
243                  */
244
245                 REDEBUG2("No Auth-Type found: rejecting the user via Post-Auth-Type = Reject");
246                 return -2;
247         }
248
249         /*
250          *      See if there is a module that handles
251          *      this Auth-Type, and turn the RLM_ return
252          *      status into the values as defined at
253          *      the top of this function.
254          */
255         result = process_authenticate(auth_type, request);
256         switch (result) {
257         /*
258          *      An authentication module FAIL
259          *      return code, or any return code that
260          *      is not expected from authentication,
261          *      is the same as an explicit REJECT!
262          */
263         case RLM_MODULE_FAIL:
264         case RLM_MODULE_INVALID:
265         case RLM_MODULE_NOOP:
266         case RLM_MODULE_NOTFOUND:
267         case RLM_MODULE_REJECT:
268         case RLM_MODULE_UPDATED:
269         case RLM_MODULE_USERLOCK:
270         default:
271                 result = -1;
272                 break;
273
274         case RLM_MODULE_OK:
275                 result = 0;
276                 break;
277
278         case RLM_MODULE_HANDLED:
279                 result = 1;
280                 break;
281         }
282
283         return result;
284 }
285
286 /*
287  *      Post-authentication step processes the response before it is
288  *      sent to the NAS. It can receive both Access-Accept and Access-Reject
289  *      replies.
290  */
291 int rad_postauth(REQUEST *request)
292 {
293         int     result;
294         int     postauth_type = 0;
295         VALUE_PAIR *vp;
296
297         /*
298          *      Do post-authentication calls. ignoring the return code.
299          */
300         vp = pairfind(request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
301         if (vp) {
302                 postauth_type = vp->vp_integer;
303                 RDEBUG2("Using Post-Auth-Type %s",
304                         dict_valnamebyattr(PW_POST_AUTH_TYPE, 0, postauth_type));
305         }
306         result = process_post_auth(postauth_type, request);
307         switch (result) {
308         /*
309          *      The module failed, or said to reject the user: Do so.
310          */
311         case RLM_MODULE_FAIL:
312         case RLM_MODULE_INVALID:
313         case RLM_MODULE_REJECT:
314         case RLM_MODULE_USERLOCK:
315         default:
316                 request->reply->code = PW_CODE_ACCESS_REJECT;
317                 fr_state_discard(request, request->packet);
318                 result = RLM_MODULE_REJECT;
319                 break;
320         /*
321          *      The module handled the request, cancel the reply.
322          */
323         case RLM_MODULE_HANDLED:
324                 /* FIXME */
325                 break;
326         /*
327          *      The module had a number of OK return codes.
328          */
329         case RLM_MODULE_NOOP:
330         case RLM_MODULE_NOTFOUND:
331         case RLM_MODULE_OK:
332         case RLM_MODULE_UPDATED:
333                 result = RLM_MODULE_OK;
334
335                 if (request->reply->code == PW_CODE_ACCESS_CHALLENGE) {
336                         fr_state_put_vps(request, request->packet, request->reply);
337
338                 } else {
339                         fr_state_discard(request, request->packet);
340                 }
341                 break;
342         }
343         return result;
344 }
345
346 /*
347  *      Process and reply to an authentication request
348  *
349  *      The return value of this function isn't actually used right now, so
350  *      it's not entirely clear if it is returning the right things. --Pac.
351  */
352 int rad_authenticate(REQUEST *request)
353 {
354 #ifdef WITH_SESSION_MGMT
355         VALUE_PAIR      *check_item;
356 #endif
357         VALUE_PAIR      *module_msg;
358         VALUE_PAIR      *tmp = NULL;
359         int             result;
360         char            autz_retry = 0;
361         int             autz_type = 0;
362
363 #ifdef WITH_PROXY
364         /*
365          *      If this request got proxied to another server, we need
366          *      to check whether it authenticated the request or not.
367          *
368          *      request->proxy gets set only AFTER authorization, so
369          *      it's safe to check it here.  If it exists, it means
370          *      we're doing a second pass through rad_authenticate().
371          */
372         if (request->proxy) {
373                 int code = 0;
374
375                 if (request->proxy_reply) code = request->proxy_reply->code;
376
377                 switch (code) {
378                 /*
379                  *      Reply of ACCEPT means accept, thus set Auth-Type
380                  *      accordingly.
381                  */
382                 case PW_CODE_ACCESS_ACCEPT:
383                         tmp = radius_paircreate(request,
384                                                 &request->config_items,
385                                                 PW_AUTH_TYPE, 0);
386                         if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT;
387                         goto authenticate;
388
389                 /*
390                  *      Challenges are punted back to the NAS without any
391                  *      further processing.
392                  */
393                 case PW_CODE_ACCESS_CHALLENGE:
394                         request->reply->code = PW_CODE_ACCESS_CHALLENGE;
395                         return RLM_MODULE_OK;
396
397                 /*
398                  *      ALL other replies mean reject. (this is fail-safe)
399                  *
400                  *      Do NOT do any authorization or authentication. They
401                  *      are being rejected, so we minimize the amount of work
402                  *      done by the server, by rejecting them here.
403                  */
404                 case PW_CODE_ACCESS_REJECT:
405                         rad_authlog("Login incorrect (Home Server says so)",
406                                     request, 0);
407                         request->reply->code = PW_CODE_ACCESS_REJECT;
408                         return RLM_MODULE_REJECT;
409
410                 default:
411                         rad_authlog("Login incorrect (Home Server failed to respond)",
412                                     request, 0);
413                         return RLM_MODULE_REJECT;
414                 }
415         }
416 #endif
417         /*
418          *      Look for, and cache, passwords.
419          */
420         if (!request->password) {
421                 request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
422         }
423         if (!request->password) {
424                 request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
425         }
426
427         /*
428          *      Grab the VPS associated with the State attribute.
429          */
430         fr_state_get_vps(request, request->packet);
431
432         /*
433          *      Get the user's authorization information from the database
434          */
435 autz_redo:
436         result = process_authorize(autz_type, request);
437         switch (result) {
438         case RLM_MODULE_NOOP:
439         case RLM_MODULE_NOTFOUND:
440         case RLM_MODULE_OK:
441         case RLM_MODULE_UPDATED:
442                 break;
443         case RLM_MODULE_HANDLED:
444                 return result;
445         case RLM_MODULE_FAIL:
446         case RLM_MODULE_INVALID:
447         case RLM_MODULE_REJECT:
448         case RLM_MODULE_USERLOCK:
449         default:
450                 if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) {
451                         char msg[MAX_STRING_LEN + 16];
452                         snprintf(msg, sizeof(msg), "Invalid user (%s)",
453                                  module_msg->vp_strvalue);
454                         rad_authlog(msg,request,0);
455                 } else {
456                         rad_authlog("Invalid user", request, 0);
457                 }
458                 request->reply->code = PW_CODE_ACCESS_REJECT;
459                 return result;
460         }
461         if (!autz_retry) {
462                 tmp = pairfind(request->config_items, PW_AUTZ_TYPE, 0, TAG_ANY);
463                 if (tmp) {
464                         autz_type = tmp->vp_integer;
465                         RDEBUG2("Using Autz-Type %s",
466                                 dict_valnamebyattr(PW_AUTZ_TYPE, 0, autz_type));
467                         autz_retry = 1;
468                         goto autz_redo;
469                 }
470         }
471
472         /*
473          *      If we haven't already proxied the packet, then check
474          *      to see if we should.  Maybe one of the authorize
475          *      modules has decided that a proxy should be used. If
476          *      so, get out of here and send the packet.
477          */
478         if (
479 #ifdef WITH_PROXY
480             (request->proxy == NULL) &&
481 #endif
482             ((tmp = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY)) != NULL)) {
483                 REALM *realm;
484
485                 realm = realm_find2(tmp->vp_strvalue);
486
487                 /*
488                  *      Don't authenticate, as the request is going to
489                  *      be proxied.
490                  */
491                 if (realm && realm->auth_pool) {
492                         return RLM_MODULE_OK;
493                 }
494
495                 /*
496                  *      Catch users who set Proxy-To-Realm to a LOCAL
497                  *      realm (sigh).  But don't complain if it is
498                  *      *the* LOCAL realm.
499                  */
500                 if (realm &&(strcmp(realm->name, "LOCAL") != 0)) {
501                         RWDEBUG2("You set Proxy-To-Realm = %s, but it is a LOCAL realm!  Cancelling proxy request.", realm->name);
502                 }
503
504                 if (!realm) {
505                         RWDEBUG2("You set Proxy-To-Realm = %s, but the realm does not exist!  Cancelling invalid proxy request.", tmp->vp_strvalue);
506                 }
507         }
508
509 #ifdef WITH_PROXY
510  authenticate:
511 #endif
512
513         /*
514          *      Validate the user
515          */
516         do {
517                 result = rad_check_password(request);
518                 if (result > 0) {
519                         /*
520                          *      We presume that the reply has been set by someone.
521                          */
522                         if (request->reply->code == PW_CODE_ACCESS_CHALLENGE) {
523                                 fr_state_put_vps(request, request->packet, request->reply);
524
525                         } else {
526                                 fr_state_discard(request, request->packet);
527                         }
528                         return RLM_MODULE_HANDLED;
529                 }
530
531         } while(0);
532
533         /*
534          *      Failed to validate the user.
535          *
536          *      We PRESUME that the code which failed will clean up
537          *      request->reply->vps, to be ONLY the reply items it
538          *      wants to send back.
539          */
540         if (result < 0) {
541                 RDEBUG2("Failed to authenticate the user");
542                 request->reply->code = PW_CODE_ACCESS_REJECT;
543
544                 if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL){
545                         char msg[MAX_STRING_LEN+19];
546
547                         snprintf(msg, sizeof(msg), "Login incorrect (%s)",
548                                  module_msg->vp_strvalue);
549                         rad_authlog(msg, request, 0);
550                 } else {
551                         rad_authlog("Login incorrect", request, 0);
552                 }
553
554                 if (request->password) {
555                         VERIFY_VP(request->password);
556                         /* double check: maybe the secret is wrong? */
557                         if ((debug_flag > 1) && (request->password->da->attr == PW_USER_PASSWORD)) {
558                                 uint8_t const *p;
559
560                                 p = (uint8_t const *) request->password->vp_strvalue;
561                                 while (*p) {
562                                         int size;
563
564                                         size = fr_utf8_char(p);
565                                         if (!size) {
566                                                 RWDEBUG("Unprintable characters in the password.  Double-check the "
567                                                         "shared secret on the server and the NAS!");
568                                                 break;
569                                         }
570                                         p += size;
571                                 }
572                         }
573                 }
574         }
575
576 #ifdef WITH_SESSION_MGMT
577         if (result >= 0 &&
578             (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) {
579                 int r, session_type = 0;
580                 char            logstr[1024];
581                 char            umsg[MAX_STRING_LEN + 1];
582
583                 tmp = pairfind(request->config_items, PW_SESSION_TYPE, 0, TAG_ANY);
584                 if (tmp) {
585                         session_type = tmp->vp_integer;
586                         RDEBUG2("Using Session-Type %s",
587                                 dict_valnamebyattr(PW_SESSION_TYPE, 0, session_type));
588                 }
589
590                 /*
591                  *      User authenticated O.K. Now we have to check
592                  *      for the Simultaneous-Use parameter.
593                  */
594                 if (request->username &&
595                     (r = process_checksimul(session_type, request, check_item->vp_integer)) != 0) {
596                         char mpp_ok = 0;
597
598                         if (r == 2){
599                                 /* Multilink attempt. Check if port-limit > simultaneous-use */
600                                 VALUE_PAIR *port_limit;
601
602                                 if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT, 0, TAG_ANY)) != NULL &&
603                                         port_limit->vp_integer > check_item->vp_integer){
604                                         RDEBUG2("MPP is OK");
605                                         mpp_ok = 1;
606                                 }
607                         }
608                         if (!mpp_ok){
609                                 if (check_item->vp_integer > 1) {
610                                         snprintf(umsg, sizeof(umsg),
611                                                  "\r\n%s (%d)\r\n\n",
612                                                  main_config.denied_msg,
613                                                  (int)check_item->vp_integer);
614                                 } else {
615                                         snprintf(umsg, sizeof(umsg),
616                                                  "\r\n%s\r\n\n",
617                                                  main_config.denied_msg);
618                                 }
619
620                                 request->reply->code = PW_CODE_ACCESS_REJECT;
621
622                                 /*
623                                  *      They're trying to log in too many times.
624                                  *      Remove ALL reply attributes.
625                                  */
626                                 pairfree(&request->reply->vps);
627                                 pairmake_reply("Reply-Message",
628                                                umsg, T_OP_SET);
629
630                                 snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
631                                         check_item->vp_integer,
632                                         r == 2 ? "[MPP attempt]" : "");
633                                 rad_authlog(logstr, request, 1);
634
635                                 result = -1;
636                         }
637                 }
638         }
639 #endif
640
641         /*
642          *      Result should be >= 0 here - if not, it means the user
643          *      is rejected, so we just process post-auth and return.
644          */
645         if (result < 0) {
646                 return RLM_MODULE_REJECT;
647         }
648
649         /*
650          *      Set the reply to Access-Accept, if it hasn't already
651          *      been set to something.  (i.e. Access-Challenge)
652          */
653         if (request->reply->code == 0)
654           request->reply->code = PW_CODE_ACCESS_ACCEPT;
655
656         if ((module_msg = pairfind(request->packet->vps, PW_MODULE_SUCCESS_MESSAGE, 0, TAG_ANY)) != NULL){
657                 char msg[MAX_STRING_LEN+12];
658
659                 snprintf(msg, sizeof(msg), "Login OK (%s)",
660                          module_msg->vp_strvalue);
661                 rad_authlog(msg, request, 1);
662         } else {
663                 rad_authlog("Login OK", request, 1);
664         }
665
666         return result;
667 }
668
669 /*
670  *      Run a virtual server auth and postauth
671  *
672  */
673 int rad_virtual_server(REQUEST *request)
674 {
675         VALUE_PAIR *vp;
676         int result;
677
678         RDEBUG("Virtual server received request");
679         rdebug_pair_list(L_DBG_LVL_1, request, request->packet->vps, NULL);
680
681         RDEBUG("server %s {", request->server);
682         RINDENT();
683
684         /*
685          *      We currently only handle AUTH packets here.
686          *      This could be expanded to handle other packets as well if required.
687          */
688         rad_assert(request->packet->code == PW_CODE_ACCESS_REQUEST);
689
690         result = rad_authenticate(request);
691
692         if (request->reply->code == PW_CODE_ACCESS_REJECT) {
693                 pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
694                 vp = pairmake_config("Post-Auth-Type", "Reject", T_OP_SET);
695                 if (vp) rad_postauth(request);
696         }
697
698         if (request->reply->code == PW_CODE_ACCESS_ACCEPT) {
699                 rad_postauth(request);
700         }
701
702         REXDENT();
703         RDEBUG("} # server %s", request->server);
704
705         RDEBUG("Virtual server sending reply");
706         rdebug_pair_list(L_DBG_LVL_1, request, request->reply->vps, NULL);
707
708         return result;
709 }