2 * auth.c User authentication.
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.
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.
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
20 * Copyright 2000 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2000 Jeff Carneal <jeff@apex.net>
25 static const char rcsid[] = "$Id$";
28 #include "libradius.h"
35 # include <netinet/in.h>
42 * Return a short string showing the terminal server, port
43 * and calling station ID.
45 char *auth_name(char *buf, size_t buflen, REQUEST *request, int do_cli) {
50 if ((cli = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) == NULL)
52 if ((pair = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL)
55 snprintf(buf, buflen, "from client %.128s port %u%s%.128s",
56 client_name(request->packet->src_ipaddr), port,
57 (do_cli ? " cli " : ""), (do_cli ? (char *)cli->strvalue : ""));
64 * Check if account has expired, and if user may login now.
66 static int check_expiration(REQUEST *request)
69 VALUE_PAIR *check_item = request->config_items;
72 while (result == 0 && check_item != NULL) {
75 * Check expiration date if we are doing password aging.
77 if (check_item->attribute == PW_EXPIRATION) {
79 * Has this user's password expired?
81 * If so, remove ALL reply attributes,
82 * and add our own Reply-Message, saying
83 * why they're being rejected.
85 if (check_item->lvalue < (unsigned) time(NULL)) {
89 vp = pairmake("Reply-Message",
90 "Password Has Expired\r\n",
92 pairfree(&request->reply->vps);
93 request->reply->vps = vp;
97 check_item = check_item->next;
104 * Make sure user/pass are clean
107 static int rad_authlog(const char *msg, REQUEST *request, int goodpass) {
109 char clean_password[1024];
110 char clean_username[1024];
112 VALUE_PAIR *username = NULL;
114 if (!mainconfig.log_auth) {
119 * Get the correct username based on the configured value
121 if (log_stripped_names == 0) {
122 username = pairfind(request->packet->vps, PW_USER_NAME);
124 username = request->username;
128 * Clean up the username
130 if (username == NULL) {
131 strcpy(clean_username, "<no User-Name attribute>");
133 librad_safeprint((char *)username->strvalue,
135 clean_username, sizeof(clean_username));
139 * Clean up the password
141 if (mainconfig.log_auth_badpass || mainconfig.log_auth_goodpass) {
142 if (!request->password) {
143 strcpy(clean_password, "<no User-Password attribute>");
144 } else if (request->password->attribute == PW_CHAP_PASSWORD) {
145 strcpy(clean_password, "<CHAP-Password>");
147 librad_safeprint((char *)request->password->strvalue,
148 request->password->length,
149 clean_password, sizeof(clean_password));
154 radlog(L_AUTH, "%s: [%s%s%s] (%s)",
157 mainconfig.log_auth_goodpass ? "/" : "",
158 mainconfig.log_auth_goodpass ? clean_password : "",
159 auth_name(buf, sizeof(buf), request, 1));
161 radlog(L_AUTH, "%s: [%s%s%s] (%s)",
164 mainconfig.log_auth_badpass ? "/" : "",
165 mainconfig.log_auth_badpass ? clean_password : "",
166 auth_name(buf, sizeof(buf), request, 1));
177 * -2 Rejected (Auth-Type = Reject, send Port-Message back)
178 * 1 End check & return, don't reply
180 * NOTE: NOT the same as the RLM_ values !
182 int rad_check_password(REQUEST *request)
184 VALUE_PAIR *auth_type_pair;
185 VALUE_PAIR *cur_config_item;
186 VALUE_PAIR *password_pair;
187 VALUE_PAIR *auth_item;
188 char string[MAX_STRING_LEN];
191 int auth_type_count = 0;
195 * Look for matching check items. We skip the whole lot
196 * if the authentication type is PW_AUTHTYPE_ACCEPT or
197 * PW_AUTHTYPE_REJECT.
199 cur_config_item = request->config_items;
200 while(((auth_type_pair = pairfind(cur_config_item, PW_AUTH_TYPE))) != NULL) {
201 auth_type = auth_type_pair->lvalue;
204 DEBUG2(" rad_check_password: Found Auth-Type %s",
205 auth_type_pair->strvalue);
206 cur_config_item = auth_type_pair->next;
208 if (auth_type == PW_AUTHTYPE_REJECT) {
209 DEBUG2(" rad_check_password: Auth-Type = Reject, rejecting user");
214 if (( auth_type_count > 1) && (debug_flag)) {
215 radlog(L_ERR, "Warning: Found %d auth-types on request for user '%s'",
216 auth_type_count, request->username->strvalue);
220 * This means we have a proxy reply or an accept
221 * and it wasn't rejected in the above loop. So
222 * that means it is accepted and we do no further
225 if ((auth_type == PW_AUTHTYPE_ACCEPT) || (request->proxy)) {
226 DEBUG2(" rad_check_password: Auth-Type = Accept, accepting the user");
231 * Find the password from the users file.
233 if ((password_pair = pairfind(request->config_items, PW_CRYPT_PASSWORD)) != NULL)
234 auth_type = PW_AUTHTYPE_CRYPT;
236 password_pair = pairfind(request->config_items, PW_PASSWORD);
240 auth_type = PW_AUTHTYPE_LOCAL;
243 * The admin hasn't told us how to
244 * authenticate the user, so we reject them!
248 DEBUG2("auth: No authenticate method (Auth-Type) configuration found for the request: Rejecting the user");
254 case PW_AUTHTYPE_CRYPT:
256 * Find the password sent by the user. It
257 * SHOULD be there, if it's not
258 * authentication fails.
260 auth_item = request->password;
261 if (auth_item == NULL) {
262 DEBUG2("auth: No User-Password or CHAP-Password attribute in the request");
266 DEBUG2("auth: type Crypt");
267 if (password_pair == NULL) {
268 DEBUG2("No Crypt-Password configured for the user");
269 rad_authlog("Login incorrect "
270 "(No Crypt-Password configured for the user)", request, 0);
274 switch (lrad_crypt_check((char *)auth_item->strvalue,
275 (char *)password_pair->strvalue)) {
277 rad_authlog("Login incorrect "
278 "(system failed to supply an encrypted password for comparison)", request, 0);
283 case PW_AUTHTYPE_LOCAL:
284 DEBUG2("auth: type Local");
287 * Find the password sent by the user. It
288 * SHOULD be there, if it's not
289 * authentication fails.
291 auth_item = request->password;
292 if (auth_item == NULL) {
293 DEBUG2("auth: No User-Password or CHAP-Password attribute in the request");
298 * Plain text password.
300 if (password_pair == NULL) {
301 DEBUG2("auth: No password configured for the user");
302 rad_authlog("Login incorrect "
303 "(No password configured for the user)", request, 0);
308 * Local password is just plain text.
310 if (auth_item->attribute == PW_PASSWORD) {
311 if (strcmp((char *)password_pair->strvalue,
312 (char *)auth_item->strvalue) != 0) {
313 DEBUG2("auth: user supplied User-Password does NOT match local User-Password");
316 DEBUG2("auth: user supplied User-Password matches local User-Password");
319 } else if (auth_item->attribute != PW_CHAP_PASSWORD) {
320 DEBUG2("The user did not supply a User-Password or a CHAP-Password attribute");
321 rad_authlog("Login incorrect "
322 "(no User-Password or CHAP-Password attribute)", request, 0);
326 rad_chap_encode(request->packet, string,
327 auth_item->strvalue[0], password_pair);
332 if (memcmp(string + 1, auth_item->strvalue + 1,
333 CHAP_VALUE_LENGTH) != 0) {
334 DEBUG2("auth: user supplied CHAP-Password does NOT match local User-Password");
337 DEBUG2("auth: user supplied CHAP-Password matches local User-Password");
340 DEBUG2("auth: type \"%s\"",
341 dict_valbyattr(PW_AUTH_TYPE, auth_type)->name);
343 * See if there is a module that handles
344 * this type, and turn the RLM_ return
345 * status into the values as defined at
346 * the top of this function.
348 result = module_authenticate(auth_type, request);
351 * An authentication module FAIL
352 * return code, or any return code that
353 * is not expected from authentication,
354 * is the same as an explicit REJECT!
356 case RLM_MODULE_FAIL:
357 case RLM_MODULE_REJECT:
358 case RLM_MODULE_USERLOCK:
359 case RLM_MODULE_INVALID:
360 case RLM_MODULE_NOTFOUND:
361 case RLM_MODULE_NOOP:
362 case RLM_MODULE_UPDATED:
368 case RLM_MODULE_HANDLED:
379 * Process and reply to an authentication request
381 * The return value of this function isn't actually used right now, so
382 * it's not entirely clear if it is returning the right things. --Pac.
384 int rad_authenticate(REQUEST *request)
386 VALUE_PAIR *namepair;
387 VALUE_PAIR *check_item;
388 VALUE_PAIR *reply_item;
389 VALUE_PAIR *auth_item;
390 VALUE_PAIR *module_msg;
391 VALUE_PAIR *tmp = NULL;
392 VALUE_PAIR *autz_type_item = NULL;
393 VALUE_PAIR *postauth_type_item = NULL;
395 char umsg[MAX_STRING_LEN + 1];
396 const char *user_msg = NULL;
397 const char *password;
400 int seen_callback_id;
401 char buf[1024], logstr[1024];
404 int postauth_type = 0;
409 * If this request got proxied to another server,
410 * AND it was an authentication request, then we need
411 * to add an initial Auth-Type: Auth-Accept for success,
412 * Auth-Reject for fail. We also need to add the reply
413 * pairs from the server to the initial reply.
415 if ((request->proxy_reply) &&
416 (request->packet->code == PW_AUTHENTICATION_REQUEST)) {
417 tmp = paircreate(PW_AUTH_TYPE, PW_TYPE_INTEGER);
419 radlog(L_ERR|L_CONS, "no memory");
424 * Challenges are punted back to the NAS
425 * without any further processing.
427 if (request->proxy_reply->code == PW_ACCESS_CHALLENGE) {
428 request->reply->code = PW_ACCESS_CHALLENGE;
429 return RLM_MODULE_HANDLED;
433 * Reply of ACCEPT means accept, ALL other
434 * replies mean reject. This is fail-safe.
436 if (request->proxy_reply->code == PW_AUTHENTICATION_ACK)
437 tmp->lvalue = PW_AUTHTYPE_ACCEPT;
439 tmp->lvalue = PW_AUTHTYPE_REJECT;
440 pairadd(&request->config_items, tmp);
443 * If it's an Access-Reject, then do NOT do any
444 * authorization or authentication. They're being
445 * rejected, so we minimize the amount of work
446 * done by the server, by rejecting them here.
448 if ((request->proxy_reply->code != PW_AUTHENTICATION_ACK) &&
449 (request->proxy_reply->code != PW_ACCESS_CHALLENGE)) {
450 rad_authlog("Login incorrect (Home Server says so)", request, 0);
451 request->reply->code = PW_AUTHENTICATION_REJECT;
452 return RLM_MODULE_REJECT;
457 * Get the username from the request.
459 * Note that namepair MAY be NULL, in which case there
460 * is no User-Name attribute in the request.
462 namepair = request->username;
465 * Look for, and cache, passwords.
467 if (!request->password) {
468 request->password = pairfind(request->packet->vps,
473 * Discover which password we want to use.
475 auth_item = request->password;
477 password = (const char *)auth_item->strvalue;
481 * Maybe there's a CHAP-Password?
483 if ((auth_item = pairfind(request->packet->vps,
484 PW_CHAP_PASSWORD)) != NULL) {
485 password = "<CHAP-PASSWORD>";
489 * No password we recognize.
491 password = "<NO-PASSWORD>";
494 request->password = auth_item;
497 * Get the user's authorization information from the database
500 r = module_authorize(autz_type, request);
501 if (r != RLM_MODULE_NOTFOUND &&
502 r != RLM_MODULE_NOOP &&
503 r != RLM_MODULE_OK &&
504 r != RLM_MODULE_UPDATED) {
505 if (r != RLM_MODULE_FAIL && r != RLM_MODULE_HANDLED) {
506 if ((module_msg = pairfind(request->packet->vps,
507 PW_MODULE_FAILURE_MESSAGE)) != NULL){
508 char msg[MAX_STRING_LEN+16];
509 snprintf(msg, sizeof(msg), "Invalid user (%s)",
510 module_msg->strvalue);
511 rad_authlog(msg,request,0);
513 rad_authlog("Invalid user", request, 0);
515 request->reply->code = PW_AUTHENTICATION_REJECT;
519 * Hope that the module returning REJECT is smart
520 * enough to do pairfre(&request->reply->vps)...
525 autz_type_item = pairfind(request->config_items, PW_AUTZ_TYPE);
527 autz_type = autz_type_item->lvalue;
535 * If we haven't already proxied the packet, then check
536 * to see if we should. Maybe one of the authorize
537 * modules has decided that a proxy should be used. If
538 * so, get out of here and send the packet.
540 if ((request->proxy == NULL) &&
541 (pairfind(request->config_items, PW_PROXY_TO_REALM) != NULL)) {
542 return RLM_MODULE_OK;
546 * Perhaps there is a Stripped-User-Name now.
548 namepair = request->username;
554 if ((result = check_expiration(request)) < 0)
556 result = rad_check_password(request);
559 return RLM_MODULE_HANDLED;
564 * Failed to validate the user.
566 * We PRESUME that the code which failed will clean up
567 * request->reply->vps, to be ONLY the reply items it
568 * wants to send back.
571 DEBUG2("auth: Failed to validate the user.");
572 request->reply->code = PW_AUTHENTICATION_REJECT;
574 if ((module_msg = pairfind(request->packet->vps,PW_MODULE_FAILURE_MESSAGE)) != NULL){
575 char msg[MAX_STRING_LEN+19];
577 snprintf(msg, sizeof(msg), "Login incorrect (%s)",
578 module_msg->strvalue);
579 rad_authlog(msg, request, 0);
581 rad_authlog("Login incorrect", request, 0);
584 /* double check: maybe the secret is wrong? */
585 if ((debug_flag > 1) && (auth_item != NULL) &&
586 (auth_item->attribute == PW_PASSWORD)) {
589 p = auth_item->strvalue;
591 if (!isprint((int) *p)) {
592 log_debug(" WARNING: Unprintable characters in the password.\n\t Double-check the shared secret on the server and the NAS!");
601 (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE)) != NULL) {
602 VALUE_PAIR *session_type;
605 session_type = pairfind(request->config_items, PW_SESSION_TYPE);
607 sess_type = session_type->lvalue;
610 * User authenticated O.K. Now we have to check
611 * for the Simultaneous-Use parameter.
614 (r = module_checksimul(sess_type,request, check_item->lvalue)) != 0) {
618 /* Multilink attempt. Check if port-limit > simultaneous-use */
619 VALUE_PAIR *port_limit;
621 if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT)) != NULL &&
622 port_limit->lvalue > check_item->lvalue){
623 DEBUG2("main auth: MPP is OK");
628 if (check_item->lvalue > 1) {
629 snprintf(umsg, sizeof(umsg),
630 "\r\nYou are already logged in %d times - access denied\r\n\n",
631 (int)check_item->lvalue);
634 user_msg = "\r\nYou are already logged in - access denied\r\n\n";
637 request->reply->code = PW_AUTHENTICATION_REJECT;
640 * They're trying to log in too many times.
641 * Remove ALL reply attributes.
643 pairfree(&request->reply->vps);
644 tmp = pairmake("Reply-Message", user_msg, T_OP_SET);
645 request->reply->vps = tmp;
647 snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
649 r == 2 ? "[MPP attempt]" : "");
650 rad_authlog(logstr, request, 1);
658 (check_item = pairfind(request->config_items, PW_LOGIN_TIME)) != NULL) {
661 * Authentication is OK. Now see if this
662 * user may login at this time of the day.
664 r = timestr_match((char *)check_item->strvalue,
667 * Session-Timeout needs to be at least
668 * 60 seconds, some terminal servers
669 * ignore smaller values.
673 * User called outside allowed time interval.
676 user_msg = "You are calling outside your allowed timespan\r\n";
678 request->reply->code = PW_AUTHENTICATION_REJECT;
679 pairfree(&request->reply->vps);
681 tmp = pairmake("Reply-Message", user_msg, T_OP_SET);
682 request->reply->vps = tmp;
684 snprintf(logstr, sizeof(logstr), "Outside allowed timespan (time allowed %s)",
685 check_item->strvalue);
686 rad_authlog(logstr, request, 1);
691 * User is allowed, but set Session-Timeout.
693 if ((reply_item = pairfind(request->reply->vps,
694 PW_SESSION_TIMEOUT)) != NULL) {
695 if (reply_item->lvalue > (unsigned) r)
696 reply_item->lvalue = r;
698 if ((reply_item = paircreate(
700 PW_TYPE_INTEGER)) == NULL) {
701 radlog(L_ERR|L_CONS, "no memory");
704 reply_item->lvalue = r;
705 pairadd(&request->reply->vps, reply_item);
711 * Result should be >= 0 here - if not, we return.
714 return RLM_MODULE_OK;
718 * We might need this later. The 'password' string
719 * is NOT used anywhere below here, except for logging,
720 * so it should be safe...
722 if ((auth_item != NULL) && (auth_item->attribute == PW_CHAP_PASSWORD)) {
723 password = "CHAP-Password";
727 * Add the port number to the Framed-IP-Address if
728 * vp->addport is set.
730 if (((tmp = pairfind(request->reply->vps,
731 PW_FRAMED_IP_ADDRESS)) != NULL) &&
732 (tmp->flags.addport != 0)) {
733 VALUE_PAIR *vpPortId;
736 * Find the NAS port ID.
738 if ((vpPortId = pairfind(request->packet->vps,
739 PW_NAS_PORT)) != NULL) {
740 tmp->lvalue = htonl(ntohl(tmp->lvalue) + vpPortId->lvalue);
741 tmp->flags.addport = 0;
742 ip_ntoa(tmp->strvalue, tmp->lvalue);
744 DEBUG2("WARNING: No NAS-Port attribute in request. CANNOT return a Framed-IP-Address + NAS-Port.\n");
745 pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
750 * See if we need to execute a program.
751 * FIXME: somehow cache this info, and only execute the
752 * program when we receive an Accounting-START packet.
753 * Only at that time we know dynamic IP etc.
757 if ((auth_item = pairfind(request->reply->vps, PW_EXEC_PROGRAM)) != NULL) {
759 exec_program = strdup((char *)auth_item->strvalue);
760 pairdelete(&request->reply->vps, PW_EXEC_PROGRAM);
762 if ((auth_item = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT)) != NULL) {
764 exec_program = strdup((char *)auth_item->strvalue);
765 pairdelete(&request->reply->vps, PW_EXEC_PROGRAM_WAIT);
769 * Hack - allow % expansion in certain value strings.
770 * This is nice for certain Exec-Program programs.
772 seen_callback_id = 0;
773 if ((auth_item = pairfind(request->reply->vps, PW_CALLBACK_ID)) != NULL) {
774 seen_callback_id = 1;
775 radius_xlat(buf, sizeof(auth_item->strvalue),
776 (char *)auth_item->strvalue, request, NULL);
777 strNcpy((char *)auth_item->strvalue, buf,
778 sizeof(auth_item->strvalue));
779 auth_item->length = strlen((char *)auth_item->strvalue);
784 * If we want to exec a program, but wait for it,
785 * do it first before sending the reply.
787 if (exec_program && exec_wait) {
788 r = radius_exec_program(exec_program, request,
791 request->packet->vps, &tmp);
796 * Always add the value-pairs to the reply.
798 pairmove(&request->reply->vps, &tmp);
803 * Error. radius_exec_program() returns -1 on
804 * fork/exec errors, or >0 if the exec'ed program
805 * had a non-zero exit status.
807 if (umsg[0] == '\0') {
808 user_msg = "\r\nAccess denied (external check failed).";
813 request->reply->code = PW_AUTHENTICATION_REJECT;
814 tmp = pairmake("Reply-Message", user_msg, T_OP_SET);
816 pairadd(&request->reply->vps, tmp);
817 rad_authlog("Login incorrect (external check failed)",
820 return RLM_MODULE_REJECT;
825 * Delete "normal" A/V pairs when using callback.
827 * FIXME: This is stupid. The portmaster should accept
828 * these settings instead of insisting on using a
831 * FIXME2: Move this into the above exec thingy?
832 * (if you knew how I use the exec_wait, you'd understand).
834 if (seen_callback_id) {
835 pairdelete(&request->reply->vps, PW_FRAMED_PROTOCOL);
836 pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
837 pairdelete(&request->reply->vps, PW_FRAMED_IP_NETMASK);
838 pairdelete(&request->reply->vps, PW_FRAMED_ROUTE);
839 pairdelete(&request->reply->vps, PW_FRAMED_MTU);
840 pairdelete(&request->reply->vps, PW_FRAMED_COMPRESSION);
841 pairdelete(&request->reply->vps, PW_FILTER_ID);
842 pairdelete(&request->reply->vps, PW_PORT_LIMIT);
843 pairdelete(&request->reply->vps, PW_CALLBACK_NUMBER);
847 * Filter (possibly multiple) Reply-Message attributes
848 * through radius_xlat, modifying them in place.
850 if (user_msg == NULL) {
851 reply_item = pairfind(request->reply->vps, PW_REPLY_MESSAGE);
853 radius_xlat(buf, sizeof(reply_item->strvalue),
854 (char *)reply_item->strvalue, request, NULL);
855 strNcpy((char *)reply_item->strvalue, buf,
856 sizeof(reply_item->strvalue));
857 reply_item->length = strlen((char *)reply_item->strvalue);
859 reply_item = pairfind(reply_item->next, PW_REPLY_MESSAGE);
864 * Set the reply to Access-Accept, if it hasn't already
865 * been set to something. (i.e. Access-Challenge)
867 if (request->reply->code == 0)
868 request->reply->code = PW_AUTHENTICATION_ACK;
870 if ((module_msg = pairfind(request->packet->vps,PW_MODULE_SUCCESS_MESSAGE)) != NULL){
871 char msg[MAX_STRING_LEN+12];
873 snprintf(msg, sizeof(msg), "Login OK (%s)",
874 module_msg->strvalue);
875 rad_authlog(msg, request, 1);
877 rad_authlog("Login OK", request, 1);
880 if (exec_program && !exec_wait) {
882 * No need to check the exit status here.
884 radius_exec_program(exec_program, request, exec_wait,
885 NULL, 0, request->packet->vps, NULL);
892 * Do post-authentication calls. ignoring the return code.
893 * If the post-authentication
895 postauth_type_item = pairfind(request->config_items, PW_POST_AUTH_TYPE);
896 if (postauth_type_item)
897 postauth_type = postauth_type_item->lvalue;
898 result = module_post_auth(postauth_type, request);
904 * The module failed, or said to reject the user: Do so.
906 case RLM_MODULE_FAIL:
907 case RLM_MODULE_REJECT:
908 case RLM_MODULE_USERLOCK:
909 case RLM_MODULE_INVALID:
910 request->reply->code = PW_AUTHENTICATION_REJECT;
911 result = RLM_MODULE_REJECT;
915 * The module had a number of OK return codes.
917 case RLM_MODULE_NOTFOUND:
918 case RLM_MODULE_NOOP:
919 case RLM_MODULE_UPDATED:
921 case RLM_MODULE_HANDLED:
922 result = RLM_MODULE_OK;