#define _XOPEN_SOURCE /* #include */ #include #include #include #include #include #include #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 256 #endif #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #define PAM_PASSWORD #ifndef __linux__ #include #endif #include #include #include /* * User authentication */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc , const char **argv ) { #ifndef __linux__ login_cap_t *lc; #endif struct spwd *pwd; const char *pass, *crypt_pass, *user; int pam_err; /* identify user */ if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { PAM_LOG("Authenticating as self."); pwd = getspnam(getlogin()); } else { if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) { PAM_ERROR("Authenticating with uname [%s] failed.", user); return (pam_err); } pwd = getspnam(user); } PAM_LOG("Authenticating user: [%s]", user); /* get password */ if (pwd != NULL) { PAM_LOG("Doing real authentication"); pass = pwd->sp_pwdp; if (pass[0] == '\0') { if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && openpam_get_option(pamh, PAM_OPT_NULLOK)){ PAM_ERROR("Authentication failed. Empty passwd not allowed."); return (PAM_SUCCESS); } pass = "*"; } #ifndef __linux__ lc = login_getpwclass(pwd); #endif } else { PAM_LOG("Doing dummy authentication."); pass = "*"; #ifndef __linux__ lc = login_getpwclass(NULL); #endif } #ifndef __linux__ prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL); pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt); login_close(lc); #else pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, (const char **) &pass, NULL); #endif PAM_LOG("Got password for user [%s]", user); if (pam_err == PAM_CONV_ERR) return (pam_err); if (pam_err != PAM_SUCCESS) return (PAM_AUTH_ERR); /* check shadow */ crypt_pass = crypt(pass, pwd->sp_pwdp); if ( strcmp(crypt_pass, pwd->sp_pwdp) != 0 ) { PAM_ERROR("Wrong password. Authentication failed."); pam_err = PAM_AUTH_ERR; } else { PAM_LOG("Authentication completed succesfully."); pam_err = PAM_SUCCESS; } return (pam_err); } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh , int flags , int argc , const char *argv[] ) { /* * This functions takes care of renewing/initializing * user credentials as well as gid/uids. Someday, it * will be completed. For now, it's not very urgent. */ return (PAM_SUCCESS); } /* * Account Management */ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags , int argc , const char *argv[] ) { #ifndef __linux__ login_cap_t *lc; #endif struct spwd *pwd; int pam_err; const char *user; time_t curtime; #ifndef __linux__ const void *rhost, *tty; char rhostip[MAXHOSTNAMELEN] = ""; #endif /* Sanity checks for uname,pwd,tty,host etc */ pam_err = pam_get_user(pamh, &user, NULL); if (pam_err != PAM_SUCCESS) return (pam_err); if (user == NULL || (pwd = getspnam(user)) == NULL) return (PAM_SERVICE_ERR); #ifndef __linux__ /* * tty/host info are provided by login classes * and cannot be used out of the box under Linux * for sanity checking (BSD only). May need to * be ported/rewritten to work on Linux as well. * Time will tell... */ pam_err = pam_get_item(pamh, PAM_RHOST, &rhost); if (pam_err != PAM_SUCCESS) return (pam_err); pam_err = pam_get_item(pamh, PAM_TTY, &tty); if (pam_err != PAM_SUCCESS) return (pam_err); #endif if (*pwd->sp_pwdp == '\0' && (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) return (PAM_NEW_AUTHTOK_REQD); #ifndef __linux__ lc = login_getpwclass(pwd); if (lc == NULL) { return (PAM_SERVICE_ERR); } #endif /* Check if pw_lstchg or pw_expire is set */ if (pwd->sp_lstchg || pwd->sp_expire) curtime = time(NULL) / (60 * 60 * 24); if (pwd->sp_expire) { if ( (curtime > pwd->sp_expire ) && ( pwd->sp_expire != -1 ) ) { #ifndef __linux__ login_close(lc); #endif return (PAM_ACCT_EXPIRED); } else if ( ( pwd->sp_expire - curtime < pwd->sp_warn) ) { // pam_error(pamh, "Warning: your account expires on %s", // ctime(&pwd->pw_expire)); } } if (pwd->sp_lstchg == 0 ) { return (PAM_NEW_AUTHTOK_REQD); } /* check all other possibilities (mostly stolen from pam_tcb) */ if ((curtime > (pwd->sp_lstchg + pwd->sp_max + pwd->sp_inact)) && (pwd->sp_max != -1) && (pwd->sp_inact != -1) && (pwd->sp_lstchg != 0)) return (PAM_ACCT_EXPIRED); if (((pwd->sp_lstchg + pwd->sp_max) < curtime) && (pwd->sp_max != -1)) return (PAM_ACCT_EXPIRED); if ((curtime - pwd->sp_lstchg > pwd->sp_max) && (curtime - pwd->sp_lstchg > pwd->sp_inact) && (curtime - pwd->sp_lstchg > pwd->sp_max + pwd->sp_inact) && (pwd->sp_max != -1) && (pwd->sp_inact != -1)) return (PAM_ACCT_EXPIRED); pam_err = (PAM_SUCCESS); #ifndef __linux__ /* validate tty/host/time */ if (!auth_hostok(lc, rhost, rhostip) || !auth_ttyok(lc, tty) || !auth_timeok(lc, time(NULL))) pam_err = PAM_AUTH_ERR; login_close(lc); #endif return (pam_err); } /* * Password Management */ PAM_EXTERN int pam_sm_chautok(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { /* * NIS support will be left for future implementation. * This is standard unix passwd changing function. */ struct spwd *new_pwd, *old_pwd; char oldprefix[HASH_PREFIX_SIZE]; const char *user, *old_pass, *new_pass; char *hashedpwd; int pam_err; /* identify user */ if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { PAM_LOG("Authenticating as self."); old_pwd = getspnam(getlogin()); } else { if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) { PAM_ERROR("Authenticating with uname [%s] failed.", user); return (pam_err); } old_pwd = getspnam(user); } PAM_LOG("Got user: [%s]", user); if (pwd == NULL) { PAM_ERROR("User [%s] either has a corrupted passwd entry or \ is not in the selected database"); return (PAM_AUTHTOK_RECOVERY_ERR); } /* * When looking through the LinuxPAM code, I came across this : * * ` Various libraries at various times have had bugs related to * '+' or '-' as the first character of a user name. Don't * allow them. ` * * I don't know if the problem is still around but just in case... */ if (user == NULL || user[0] == '-' || user[0] == '+' ) { PAM_ERROR("Bad username [%s]", user); return (PAM_USER_UNKNOWN); } if (flags & PAM_PRELIM_CHECK) { PAM_LOG("PRELIM round"); if (getuid() == 0 ) { /* root doesn't need old passwd */ return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); } if ( (pwd->pw_passwd[0] == '\0' ) && ( openpam_get_option(pamh, PAM_OPT_NULLOK) ) && ( openpam_get_option(pamh,PAM_DISALLOW_NULL_AUTHTOK)) ) { /* * Something funny could happen here since we don't * ask for a password. */ old_pass = ""; } return (PAM_SUCCESS); } PAM_MODULE_ENTRY("pam_unix")