int helper_verify_password(const char *name, const char *p, int nullok) { struct passwd *pwd = NULL; char *salt = NULL; int retval; retval = get_pwd_hash(name, &pwd, &salt); if (pwd == NULL || salt == NULL) { helper_log_err(LOG_WARNING, "check pass; user unknown"); retval = PAM_USER_UNKNOWN; } else { retval = verify_pwd_hash(p, salt, nullok); } if (salt) { _pam_overwrite(salt); _pam_drop(salt); } p = NULL; /* no longer needed here */ return retval; }
static int _audit_log(int type, const char *uname, int rc) { int audit_fd; audit_fd = audit_open(); if (audit_fd < 0) { /* You get these error codes only when the kernel doesn't have * audit compiled in. */ if (errno == EINVAL || errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) return PAM_SUCCESS; helper_log_err(LOG_CRIT, "audit_open() failed: %m"); return PAM_AUTH_ERR; } rc = audit_log_acct_message(audit_fd, type, NULL, "PAM:unix_chkpwd", uname, -1, NULL, NULL, NULL, rc == PAM_SUCCESS); if (rc == -EPERM && geteuid() != 0) { rc = 0; } audit_close(audit_fd); return rc < 0 ? PAM_AUTH_ERR : PAM_SUCCESS; }
int main(int argc, char *argv[]) { char *option; /* * Catch or ignore as many signal as possible. */ setup_signals(); /* * we establish that this program is running with non-tty stdin. * this is to discourage casual use. It does *NOT* prevent an * intruder from repeatadly running this program to determine the * password of the current user (brute force attack, but one for * which the attacker must already have gained access to the user's * account). */ if (isatty(STDIN_FILENO) || argc != 5 ) { helper_log_err(LOG_NOTICE ,"inappropriate use of Unix helper binary [UID=%d]" ,getuid()); fprintf(stderr ,"This binary is not designed for running in this way\n" "-- the system administrator has been informed\n"); sleep(10); /* this should discourage/annoy the user */ return PAM_SYSTEM_ERR; } /* We must be root to read/update shadow. */ if (geteuid() != 0) { return PAM_CRED_INSUFFICIENT; } option = argv[2]; if (strcmp(option, "update") == 0) { /* Attempting to change the password */ return set_password(argv[1], argv[3], argv[4]); } return PAM_SYSTEM_ERR; }
static int _check_expiry(const char *uname) { struct spwd *spent; struct passwd *pwent; int retval; int daysleft; retval = get_account_info(uname, &pwent, &spent); if (retval != PAM_SUCCESS) { helper_log_err(LOG_ALERT, "could not obtain user info (%s)", uname); printf("-1\n"); return retval; } if (spent == NULL) { printf("-1\n"); return retval; } retval = check_shadow_expiry(spent, &daysleft); printf("%d\n", daysleft); return retval; }
static int set_password(const char *forwho, const char *shadow, const char *remember) { struct passwd *pwd = NULL; int retval; char pass[MAXPASS + 1]; char towhat[MAXPASS + 1]; int npass = 0; /* we don't care about number format errors because the helper should be called internally only */ int doshadow = atoi(shadow); int nremember = atoi(remember); char *passwords[] = { pass, towhat }; /* read the password from stdin (a pipe from the pam_unix module) */ npass = read_passwords(STDIN_FILENO, 2, passwords); if (npass != 2) { /* is it a valid password? */ if (npass == 1) { helper_log_err(LOG_DEBUG, "no new password supplied"); memset(pass, '\0', MAXPASS); } else { helper_log_err(LOG_DEBUG, "no valid passwords supplied"); } return PAM_AUTHTOK_ERR; } if (lock_pwdf() != PAM_SUCCESS) return PAM_AUTHTOK_LOCK_BUSY; pwd = getpwnam(forwho); if (pwd == NULL) { retval = PAM_USER_UNKNOWN; goto done; } /* If real caller uid is not root we must verify that received old pass agrees with the current one. We always allow change from null pass. */ if (getuid()) { retval = helper_verify_password(forwho, pass, 1); if (retval != PAM_SUCCESS) { goto done; } } /* first, save old password */ if (save_old_password(forwho, pass, nremember)) { retval = PAM_AUTHTOK_ERR; goto done; } if (doshadow || is_pwd_shadowed(pwd)) { retval = unix_update_shadow(forwho, towhat); if (retval == PAM_SUCCESS) if (!is_pwd_shadowed(pwd)) retval = unix_update_passwd(forwho, "x"); } else { retval = unix_update_passwd(forwho, towhat); } done: memset(pass, '\0', MAXPASS); memset(towhat, '\0', MAXPASS); unlock_pwdf(); if (retval == PAM_SUCCESS) { return PAM_SUCCESS; } else { return PAM_AUTHTOK_ERR; } }
int main(int argc, char *argv[]) { char pass[MAXPASS + 1]; char *option; int npass, nullok; int blankpass = 0; int retval = PAM_AUTH_ERR; char *user; char *passwords[] = { pass }; /* * Catch or ignore as many signal as possible. */ setup_signals(); /* * we establish that this program is running with non-tty stdin. * this is to discourage casual use. It does *NOT* prevent an * intruder from repeatadly running this program to determine the * password of the current user (brute force attack, but one for * which the attacker must already have gained access to the user's * account). */ if (isatty(STDIN_FILENO) || argc != 3 ) { helper_log_err(LOG_NOTICE ,"inappropriate use of Unix helper binary [UID=%d]" ,getuid()); #ifdef HAVE_LIBAUDIT _audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR); #endif fprintf(stderr ,"This binary is not designed for running in this way\n" "-- the system administrator has been informed\n"); sleep(10); /* this should discourage/annoy the user */ return PAM_SYSTEM_ERR; } /* * Determine what the current user's name is. * We must thus skip the check if the real uid is 0. */ if (getuid() == 0) { user=argv[1]; } else { user = getuidname(getuid()); /* if the caller specifies the username, verify that user matches it */ if (strcmp(user, argv[1])) { user = argv[1]; /* no match -> permanently change to the real user and proceed */ if (setuid(getuid()) != 0) return PAM_AUTH_ERR; } } option=argv[2]; if (strcmp(option, "chkexpiry") == 0) /* Check account information from the shadow file */ return _check_expiry(argv[1]); /* read the nullok/nonull option */ else if (strcmp(option, "nullok") == 0) nullok = 1; else if (strcmp(option, "nonull") == 0) nullok = 0; else { #ifdef HAVE_LIBAUDIT _audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR); #endif return PAM_SYSTEM_ERR; } /* read the password from stdin (a pipe from the pam_unix module) */ npass = read_passwords(STDIN_FILENO, 1, passwords); if (npass != 1) { /* is it a valid password? */ helper_log_err(LOG_DEBUG, "no password supplied"); *pass = '******'; } if (*pass == '\0') { blankpass = 1; } retval = helper_verify_password(user, pass, nullok); memset(pass, '\0', MAXPASS); /* clear memory of the password */ /* return pass or fail */ if (retval != PAM_SUCCESS) { if (!nullok || !blankpass) { /* no need to log blank pass test */ #ifdef HAVE_LIBAUDIT if (getuid() != 0) _audit_log(AUDIT_USER_AUTH, user, PAM_AUTH_ERR); #endif helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user); } return PAM_AUTH_ERR; } else { if (getuid() != 0) { #ifdef HAVE_LIBAUDIT return _audit_log(AUDIT_USER_AUTH, user, PAM_SUCCESS); #else return PAM_SUCCESS; #endif } return PAM_SUCCESS; } }