/* Copyright (c) 1995-1998 by Cisco systems, Inc. Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear on all copies of the software and supporting documentation, the name of Cisco Systems, Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission, and notice be given in supporting documentation that modification, copying and distribution is by permission of Cisco Systems, Inc. Cisco Systems, Inc. makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "tac_plus.h" #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #ifdef SHADOW_PASSWORDS #ifdef HAVE_SHADOW_H #include #endif #endif #include "pwlib.h" #include "expire.h" #include "time_limit.h" #include "report.h" #include "utils.h" #include "cfgfile.h" #include "pw.h" #include "choose_authen.h" /* for "struct authen_data" */ #include "packet.h" #include "main.h" #include "parse.h" #ifdef USE_PAM #include "tac_pam.h" #endif #ifdef DB #include "db.h" /* For database verification */ #endif #ifdef USE_LDAP #include "ldap_author.h" /* For LDAP verification */ #endif static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename)); /* Generic password verification routines for des, file and cleartext passwords */ /* Adjust data->status depending on whether a user has expired or not */ void set_expiration_status TAC_ARGS((const char *exp_date, struct authen_data *data)); void set_expiration_status(exp_date, data) const char *exp_date; struct authen_data *data; { int expired; /* if the status is anything except pass, there's no point proceeding */ if (data->status != TAC_PLUS_AUTHEN_STATUS_PASS) { return; } /* Check the expiration date, if any. If NULL, this check will return * PW_OK */ expired = check_expiration(exp_date); switch (expired) { case PW_OK: if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "Password has not expired %s", exp_date ? exp_date : ""); data->status = TAC_PLUS_AUTHEN_STATUS_PASS; return; case PW_EXPIRING: if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "Password will expire soon %s", exp_date ? exp_date : ""); if (data->server_msg) free(data->server_msg); data->server_msg = tac_strdup("Password will expire soon"); data->status = TAC_PLUS_AUTHEN_STATUS_PASS; return; case PW_EXPIRED: if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "Password has expired %s", exp_date ? exp_date : ""); if (data->server_msg) free(data->server_msg); data->server_msg = tac_strdup("Password has expired"); data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return; default: report(LOG_ERR, "%s: Bogus return value %d from check_expiration", session.peer, expired); data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; return; } } /* Verify that this user/password is valid. Works only for cleartext, file and des passwords. Return 1 if password is valid */ int verify TAC_ARGS((const char *name, const char *passwd, struct authen_data *data, int recurse)); int verify(name, passwd, data, recurse) const char *name; const char *passwd; struct authen_data *data; int recurse; { const char *exp_date, *cfg_passwd, *p, *timestamp; timestamp = cfg_get_timestamp(name, recurse); if ( timestamp != NULL ) { if( time_limit_process(timestamp) == 0 ) { if ( debug & DEBUG_AUTHEN_FLAG ) report(LOG_DEBUG,"Timestamp check failed"); data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } } if (data->type == TAC_PLUS_AUTHEN_TYPE_PAP) { cfg_passwd = cfg_get_pap_secret(name, recurse); } else { cfg_passwd = cfg_get_login_secret(name, recurse); } /* If there is no login or pap password for this user, see if there is a global password for her that we can use */ if (!cfg_passwd) { cfg_passwd = cfg_get_global_secret(name, recurse); } /* If we still have no password for this user (or no user for that matter) but the default authentication = file statement has been issued, attempt to use this password file */ if (!cfg_passwd) { const char *file = cfg_get_authen_default(); switch (cfg_get_authen_default_method()) { case (S_file): if (file) return (passwd_file_verify(name, passwd, data, file)); break; #ifdef DB case (S_db): /* ugly check for database connect string */ if( strstr(file, "://") ) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG,"%s %s: DB access to %s for user %s",session.peer, session.port, file, name); if (!db_verify(name, passwd, file)) { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; } exp_date = NULL; set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); } break; #endif #ifdef USE_LDAP case (S_ldap): if (ldap_verify(name, passwd, file)==1) { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; } exp_date = NULL; set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); break; #endif /* USE_LDAP */ #ifdef USE_PAM case (S_pam): if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "PAM verify daemon [PAM] == NAS %s", passwd); if (tac_pam_auth(name, passwd, data, file)) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "PAM default authentication fail"); data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return(0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, " PAM default authentication pass"); } exp_date = cfg_get_expires(name, recurse); set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); break; #endif default: /* otherwise, we fail */ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } } /* We have a configured password. Deal with it depending on its type */ p = tac_find_substring("cleartext ", cfg_passwd); if (p) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "verify daemon %s == NAS %s", p, passwd); if (strcmp(passwd, p)) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "Password is incorrect"); data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return(0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "Password is correct"); } exp_date = cfg_get_expires(name, recurse); set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); } #ifdef USE_PAM p = tac_find_substring("pam ", cfg_passwd); if (p) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "PAM verify daemon %s == NAS %s", p,passwd); if (tac_pam_auth(name, passwd, data,p)) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "PAM Password is incorrect"); data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return(0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "PAM Password is correct"); } exp_date = cfg_get_expires(name, recurse); set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); } #endif /* USE_PAM */ p = tac_find_substring("des ", cfg_passwd); if (p) { /* try to verify this des password */ if (!des_verify(passwd, p)) { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; } exp_date = cfg_get_expires(name, recurse); set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); } #ifdef DB p = tac_find_substring("db ", cfg_passwd); if (p) { /* try to verify this password from database */ if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "DB verify daemon %s == NAS %s", p, passwd); if (!db_verify(name, passwd, p)) { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "DB Password is incorrect"); return (0); } else { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "DB Password is correct"); data->status = TAC_PLUS_AUTHEN_STATUS_PASS; } exp_date = cfg_get_expires(name, recurse); set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); } #endif /* DB */ p = tac_find_substring("file ", cfg_passwd); if (p) { return (passwd_file_verify(name, passwd, data, p)); } /* Oops. No idea what kind of password this is. This should never happen as the parser should never create such passwords. */ report(LOG_ERR, "%s: Error cannot identify password type %s for %s", session.peer, cfg_passwd && cfg_passwd[0] ? cfg_passwd : "", name ? name : ""); data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } static int etc_passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data)); /* verify that this user/password is valid per /etc/passwd. * Return 0 if invalid. */ static int etc_passwd_file_verify(user, supplied_passwd, data) const char *user; const char *supplied_passwd; struct authen_data *data; { struct passwd *pw; char *exp_date; char *cfg_passwd; #ifdef SHADOW_PASSWORDS char buf[12]; #endif /* SHADOW_PASSWORDS */ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; setpwent(); pw = getpwnam(user); endpwent(); if (pw == NULL) { /* no entry exists */ return (0); } if (*pw->pw_passwd == '\0' || supplied_passwd == NULL || *supplied_passwd == '\0') { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } cfg_passwd = pw->pw_passwd; exp_date = pw->pw_shell; #ifdef SHADOW_PASSWORDS if (STREQ(pw->pw_passwd, "x")) { struct spwd *spwd = getspnam(user); if (!spwd) { if (debug & DEBUG_PASSWD_FLAG) { report(LOG_DEBUG, "No entry for %s in shadow file", user); } data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } if (debug & DEBUG_PASSWD_FLAG) { report(LOG_DEBUG, "Found entry for %s in shadow file", user); } cfg_passwd = spwd->sp_pwdp; /* * Sigh. The Solaris shadow password file contains its own * expiry date as the number of days after the epoch * (January 1, 1970) when the password expires. * Convert this to ascii so that the traditional tacacs * password expiration routines work correctly. */ if (spwd->sp_expire > 0) { long secs = spwd->sp_expire * 24 * 60 * 60; char *p = ctime(&secs); bcopy(p+4, buf, 7); bcopy(p+20, buf+7, 4); buf[11] = '\0'; exp_date = buf; } } #endif /* SHADOW_PASSWORDS */ /* try to verify the password */ if (!des_verify(supplied_passwd, cfg_passwd)) { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; } /* password ok. Check expiry field */ set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); } /* verify that this user/password is valid per a passwd(5) style database. Return 0 if invalid. */ static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename)); static int passwd_file_verify(user, supplied_passwd, data, filename) const char *user; const char *supplied_passwd; struct authen_data *data; const char *filename; { struct passwd *pw; char *exp_date; char *cfg_passwd; data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; if (filename && (STREQ(filename, "/etc/passwd")|| STREQ(filename,"/etc/shadow") )) { return(etc_passwd_file_verify(user, supplied_passwd, data)); } /* an alternate filename */ if (!(access(filename, R_OK) == 0)) { report(LOG_ERR, "%s %s: Cannot access %s for user %s -- %s", session.peer, session.port, filename, user, sys_errlist[errno]); return (0); } pw = tac_passwd_lookup(user, filename); if (pw == NULL) /* no entry exists */ return (0); if (*pw->pw_passwd == '\0' || supplied_passwd == NULL || *supplied_passwd == '\0') { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } cfg_passwd = pw->pw_passwd; exp_date = pw->pw_shell; /* try to verify the password */ if (!des_verify(supplied_passwd, cfg_passwd)) { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; return (0); } else { data->status = TAC_PLUS_AUTHEN_STATUS_PASS; } /* password ok. Check expiry field */ set_expiration_status(exp_date, data); return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS); } /* * verify a provided password against a des encrypted one * return 1 if verified, 0 otherwise. */ int des_verify TAC_ARGS((const char *users_passwd, const char *encrypted_passwd)); int des_verify(users_passwd, encrypted_passwd) const char *users_passwd; const char *encrypted_passwd; { char *ep; if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "verify %s %s", users_passwd, encrypted_passwd); if (users_passwd == NULL || *users_passwd == '\0' || encrypted_passwd == NULL || *encrypted_passwd == '\0') { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "verify returns 0"); return (0); } ep = (char *) crypt(users_passwd, encrypted_passwd); if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "%s encrypts to %s", users_passwd, ep); if (strcmp(ep, encrypted_passwd) == 0) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "Password is correct"); return (1); } if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "Password is incorrect"); return (0); }