c5d3ee3a3f
- account expiry checks moved to unix_chkpwd helper
3724 lines
100 KiB
Diff
3724 lines
100 KiB
Diff
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c.update-helper 2006-06-17 18:44:58.000000000 +0200
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c 2008-01-07 16:39:07.000000000 +0100
|
|
@@ -73,7 +73,7 @@ PAM_EXTERN int pam_sm_open_session(pam_h
|
|
|
|
D(("called."));
|
|
|
|
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
|
|
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
|
|
|
|
retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
|
|
if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
|
|
@@ -107,7 +107,7 @@ PAM_EXTERN int pam_sm_close_session(pam_
|
|
|
|
D(("called."));
|
|
|
|
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
|
|
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
|
|
|
|
retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
|
|
if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper 2007-04-30 12:47:30.000000000 +0200
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -2,6 +2,7 @@
|
|
* Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
|
|
* Copyright (C) 1996.
|
|
* Copyright (c) Jan Rêkorajski, 1999.
|
|
+ * Copyright (c) Red Hat, Inc., 2007, 2008.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
@@ -63,7 +64,6 @@
|
|
#ifdef WITH_SELINUX
|
|
static int selinux_enabled=-1;
|
|
#include <selinux/selinux.h>
|
|
-static security_context_t prev_context=NULL;
|
|
#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
|
|
#endif
|
|
|
|
@@ -84,6 +84,7 @@ static security_context_t prev_context=N
|
|
#include "yppasswd.h"
|
|
#include "md5.h"
|
|
#include "support.h"
|
|
+#include "passverify.h"
|
|
#include "bigcrypt.h"
|
|
|
|
#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
|
|
@@ -92,15 +93,6 @@ extern int getrpcport(const char *host,
|
|
#endif /* GNU libc 2.1 */
|
|
|
|
/*
|
|
- * PAM framework looks for these entry-points to pass control to the
|
|
- * password changing module.
|
|
- */
|
|
-
|
|
-#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
|
|
-# include "./lckpwdf.-c"
|
|
-#endif
|
|
-
|
|
-/*
|
|
How it works:
|
|
Gets in username (has to be done) from the calling program
|
|
Does authentication of user (only if we are not running as root)
|
|
@@ -108,82 +100,15 @@ extern int getrpcport(const char *host,
|
|
Sets it.
|
|
*/
|
|
|
|
-/* passwd/salt conversion macros */
|
|
-
|
|
-#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
|
|
-#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
|
|
-
|
|
/* data tokens */
|
|
|
|
#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
|
|
#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
|
|
|
|
#define MAX_PASSWD_TRIES 3
|
|
-#define PW_TMPFILE "/etc/npasswd"
|
|
-#define SH_TMPFILE "/etc/nshadow"
|
|
#ifndef CRACKLIB_DICTS
|
|
#define CRACKLIB_DICTS NULL
|
|
#endif
|
|
-#define OPW_TMPFILE "/etc/security/nopasswd"
|
|
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
|
|
-
|
|
-/*
|
|
- * i64c - convert an integer to a radix 64 character
|
|
- */
|
|
-static int i64c(int i)
|
|
-{
|
|
- if (i < 0)
|
|
- return ('.');
|
|
- else if (i > 63)
|
|
- return ('z');
|
|
- if (i == 0)
|
|
- return ('.');
|
|
- if (i == 1)
|
|
- return ('/');
|
|
- if (i >= 2 && i <= 11)
|
|
- return ('0' - 2 + i);
|
|
- if (i >= 12 && i <= 37)
|
|
- return ('A' - 12 + i);
|
|
- if (i >= 38 && i <= 63)
|
|
- return ('a' - 38 + i);
|
|
- return ('\0');
|
|
-}
|
|
-
|
|
-static char *crypt_md5_wrapper(const char *pass_new)
|
|
-{
|
|
- /*
|
|
- * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
|
|
- * removed use of static variables (AGM)
|
|
- */
|
|
-
|
|
- struct timeval tv;
|
|
- MD5_CTX ctx;
|
|
- unsigned char result[16];
|
|
- char *cp = (char *) result;
|
|
- unsigned char tmp[16];
|
|
- int i;
|
|
- char *x = NULL;
|
|
-
|
|
- GoodMD5Init(&ctx);
|
|
- gettimeofday(&tv, (struct timezone *) 0);
|
|
- GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
|
|
- i = getpid();
|
|
- GoodMD5Update(&ctx, (void *) &i, sizeof i);
|
|
- i = clock();
|
|
- GoodMD5Update(&ctx, (void *) &i, sizeof i);
|
|
- GoodMD5Update(&ctx, result, sizeof result);
|
|
- GoodMD5Final(tmp, &ctx);
|
|
- strcpy(cp, "$1$"); /* magic for the MD5 */
|
|
- cp += strlen(cp);
|
|
- for (i = 0; i < 8; i++)
|
|
- *cp++ = i64c(tmp[i] & 077);
|
|
- *cp = '\0';
|
|
-
|
|
- /* no longer need cleartext */
|
|
- x = Goodcrypt_md5(pass_new, (const char *) result);
|
|
-
|
|
- return x;
|
|
-}
|
|
|
|
static char *getNISserver(pam_handle_t *pamh)
|
|
{
|
|
@@ -217,7 +142,8 @@ static char *getNISserver(pam_handle_t *
|
|
|
|
#ifdef WITH_SELINUX
|
|
|
|
-static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
|
|
+static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user,
|
|
+ const char *fromwhat, const char *towhat, int remember)
|
|
{
|
|
int retval, child, fds[2];
|
|
void (*sighandler)(int) = NULL;
|
|
@@ -247,7 +173,8 @@ static int _unix_run_shadow_binary(pam_h
|
|
size_t i=0;
|
|
struct rlimit rlim;
|
|
static char *envp[] = { NULL };
|
|
- char *args[] = { NULL, NULL, NULL, NULL };
|
|
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
|
+ char buffer[16];
|
|
|
|
/* XXX - should really tidy up PAM here too */
|
|
|
|
@@ -270,11 +197,18 @@ static int _unix_run_shadow_binary(pam_h
|
|
}
|
|
|
|
/* exec binary helper */
|
|
- args[0] = x_strdup(CHKPWD_HELPER);
|
|
+ args[0] = x_strdup(UPDATE_HELPER);
|
|
args[1] = x_strdup(user);
|
|
- args[2] = x_strdup("shadow");
|
|
+ args[2] = x_strdup("update");
|
|
+ if (on(UNIX_SHADOW, ctrl))
|
|
+ args[3] = x_strdup("1");
|
|
+ else
|
|
+ args[3] = x_strdup("0");
|
|
|
|
- execve(CHKPWD_HELPER, args, envp);
|
|
+ snprintf(buffer, sizeof(buffer), "%d", remember);
|
|
+ args[4] = x_strdup(buffer);
|
|
+
|
|
+ execve(UPDATE_HELPER, args, envp);
|
|
|
|
/* should not get here: exit with error */
|
|
D(("helper binary is not available"));
|
|
@@ -297,7 +231,7 @@ static int _unix_run_shadow_binary(pam_h
|
|
close(fds[1]);
|
|
rc=waitpid(child, &retval, 0); /* wait for helper to complete */
|
|
if (rc<0) {
|
|
- pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
|
|
+ pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
|
|
retval = PAM_AUTH_ERR;
|
|
} else {
|
|
retval = WEXITSTATUS(retval);
|
|
@@ -354,393 +288,6 @@ static int check_old_password(const char
|
|
return retval;
|
|
}
|
|
|
|
-static int save_old_password(pam_handle_t *pamh,
|
|
- const char *forwho, const char *oldpass,
|
|
- int howmany)
|
|
-{
|
|
- static char buf[16384];
|
|
- static char nbuf[16384];
|
|
- char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
|
|
- int npas;
|
|
- FILE *pwfile, *opwfile;
|
|
- int err = 0;
|
|
- int oldmask;
|
|
- int found = 0;
|
|
- struct passwd *pwd = NULL;
|
|
- struct stat st;
|
|
-
|
|
- if (howmany < 0) {
|
|
- return PAM_SUCCESS;
|
|
- }
|
|
-
|
|
- if (oldpass == NULL) {
|
|
- return PAM_SUCCESS;
|
|
- }
|
|
-
|
|
- oldmask = umask(077);
|
|
-
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- security_context_t passwd_context=NULL;
|
|
- if (getfilecon("/etc/passwd",&passwd_context)<0) {
|
|
- return PAM_AUTHTOK_ERR;
|
|
- };
|
|
- if (getfscreatecon(&prev_context)<0) {
|
|
- freecon(passwd_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- if (setfscreatecon(passwd_context)) {
|
|
- freecon(passwd_context);
|
|
- freecon(prev_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- freecon(passwd_context);
|
|
- }
|
|
-#endif
|
|
- pwfile = fopen(OPW_TMPFILE, "w");
|
|
- umask(oldmask);
|
|
- if (pwfile == NULL) {
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- opwfile = fopen(OLD_PASSWORDS_FILE, "r");
|
|
- if (opwfile == NULL) {
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fstat(fileno(opwfile), &st) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- while (fgets(buf, 16380, opwfile)) {
|
|
- if (!strncmp(buf, forwho, strlen(forwho))) {
|
|
- char *sptr;
|
|
- buf[strlen(buf) - 1] = '\0';
|
|
- s_luser = strtok_r(buf, ":", &sptr);
|
|
- s_uid = strtok_r(NULL, ":", &sptr);
|
|
- s_npas = strtok_r(NULL, ":", &sptr);
|
|
- s_pas = strtok_r(NULL, ":", &sptr);
|
|
- npas = strtol(s_npas, NULL, 10) + 1;
|
|
- while (npas > howmany) {
|
|
- s_pas = strpbrk(s_pas, ",");
|
|
- if (s_pas != NULL)
|
|
- s_pas++;
|
|
- npas--;
|
|
- }
|
|
- pass = crypt_md5_wrapper(oldpass);
|
|
- if (s_pas == NULL)
|
|
- snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
|
|
- s_luser, s_uid, npas, pass);
|
|
- else
|
|
- snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
|
|
- s_luser, s_uid, npas, s_pas, pass);
|
|
- _pam_delete(pass);
|
|
- if (fputs(nbuf, pwfile) < 0) {
|
|
- err = 1;
|
|
- break;
|
|
- }
|
|
- found = 1;
|
|
- } else if (fputs(buf, pwfile) < 0) {
|
|
- err = 1;
|
|
- break;
|
|
- }
|
|
- }
|
|
- fclose(opwfile);
|
|
-
|
|
- if (!found) {
|
|
- pwd = pam_modutil_getpwnam(pamh, forwho);
|
|
- if (pwd == NULL) {
|
|
- err = 1;
|
|
- } else {
|
|
- pass = crypt_md5_wrapper(oldpass);
|
|
- snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
|
|
- forwho, (unsigned long)pwd->pw_uid, pass);
|
|
- _pam_delete(pass);
|
|
- if (fputs(nbuf, pwfile) < 0) {
|
|
- err = 1;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- if (fclose(pwfile)) {
|
|
- D(("error writing entries to old passwords file: %m"));
|
|
- err = 1;
|
|
- }
|
|
-
|
|
-done:
|
|
- if (!err) {
|
|
- if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
|
|
- err = 1;
|
|
- }
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- if (setfscreatecon(prev_context)) {
|
|
- err = 1;
|
|
- }
|
|
- if (prev_context)
|
|
- freecon(prev_context);
|
|
- prev_context=NULL;
|
|
- }
|
|
-#endif
|
|
- if (!err) {
|
|
- return PAM_SUCCESS;
|
|
- } else {
|
|
- unlink(OPW_TMPFILE);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
-}
|
|
-
|
|
-static int _update_passwd(pam_handle_t *pamh,
|
|
- const char *forwho, const char *towhat)
|
|
-{
|
|
- struct passwd *tmpent = NULL;
|
|
- struct stat st;
|
|
- FILE *pwfile, *opwfile;
|
|
- int err = 1;
|
|
- int oldmask;
|
|
-
|
|
- oldmask = umask(077);
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- security_context_t passwd_context=NULL;
|
|
- if (getfilecon("/etc/passwd",&passwd_context)<0) {
|
|
- return PAM_AUTHTOK_ERR;
|
|
- };
|
|
- if (getfscreatecon(&prev_context)<0) {
|
|
- freecon(passwd_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- if (setfscreatecon(passwd_context)) {
|
|
- freecon(passwd_context);
|
|
- freecon(prev_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- freecon(passwd_context);
|
|
- }
|
|
-#endif
|
|
- pwfile = fopen(PW_TMPFILE, "w");
|
|
- umask(oldmask);
|
|
- if (pwfile == NULL) {
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- opwfile = fopen("/etc/passwd", "r");
|
|
- if (opwfile == NULL) {
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fstat(fileno(opwfile), &st) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- tmpent = fgetpwent(opwfile);
|
|
- while (tmpent) {
|
|
- if (!strcmp(tmpent->pw_name, forwho)) {
|
|
- /* To shut gcc up */
|
|
- union {
|
|
- const char *const_charp;
|
|
- char *charp;
|
|
- } assigned_passwd;
|
|
- assigned_passwd.const_charp = towhat;
|
|
-
|
|
- tmpent->pw_passwd = assigned_passwd.charp;
|
|
- err = 0;
|
|
- }
|
|
- if (putpwent(tmpent, pwfile)) {
|
|
- D(("error writing entry to password file: %m"));
|
|
- err = 1;
|
|
- break;
|
|
- }
|
|
- tmpent = fgetpwent(opwfile);
|
|
- }
|
|
- fclose(opwfile);
|
|
-
|
|
- if (fclose(pwfile)) {
|
|
- D(("error writing entries to password file: %m"));
|
|
- err = 1;
|
|
- }
|
|
-
|
|
-done:
|
|
- if (!err) {
|
|
- if (!rename(PW_TMPFILE, "/etc/passwd"))
|
|
- pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
|
|
- else
|
|
- err = 1;
|
|
- }
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- if (setfscreatecon(prev_context)) {
|
|
- err = 1;
|
|
- }
|
|
- if (prev_context)
|
|
- freecon(prev_context);
|
|
- prev_context=NULL;
|
|
- }
|
|
-#endif
|
|
- if (!err) {
|
|
- return PAM_SUCCESS;
|
|
- } else {
|
|
- unlink(PW_TMPFILE);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
-}
|
|
-
|
|
-static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
|
|
-{
|
|
- struct spwd *spwdent = NULL, *stmpent = NULL;
|
|
- struct stat st;
|
|
- FILE *pwfile, *opwfile;
|
|
- int err = 1;
|
|
- int oldmask;
|
|
-
|
|
- spwdent = getspnam(forwho);
|
|
- if (spwdent == NULL) {
|
|
- return PAM_USER_UNKNOWN;
|
|
- }
|
|
- oldmask = umask(077);
|
|
-
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- security_context_t shadow_context=NULL;
|
|
- if (getfilecon("/etc/shadow",&shadow_context)<0) {
|
|
- return PAM_AUTHTOK_ERR;
|
|
- };
|
|
- if (getfscreatecon(&prev_context)<0) {
|
|
- freecon(shadow_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- if (setfscreatecon(shadow_context)) {
|
|
- freecon(shadow_context);
|
|
- freecon(prev_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- freecon(shadow_context);
|
|
- }
|
|
-#endif
|
|
- pwfile = fopen(SH_TMPFILE, "w");
|
|
- umask(oldmask);
|
|
- if (pwfile == NULL) {
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- opwfile = fopen("/etc/shadow", "r");
|
|
- if (opwfile == NULL) {
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fstat(fileno(opwfile), &st) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- stmpent = fgetspent(opwfile);
|
|
- while (stmpent) {
|
|
-
|
|
- if (!strcmp(stmpent->sp_namp, forwho)) {
|
|
- stmpent->sp_pwdp = towhat;
|
|
- stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
|
|
- err = 0;
|
|
- D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
|
|
- }
|
|
-
|
|
- if (putspent(stmpent, pwfile)) {
|
|
- D(("error writing entry to shadow file: %m"));
|
|
- err = 1;
|
|
- break;
|
|
- }
|
|
-
|
|
- stmpent = fgetspent(opwfile);
|
|
- }
|
|
- fclose(opwfile);
|
|
-
|
|
- if (fclose(pwfile)) {
|
|
- D(("error writing entries to shadow file: %m"));
|
|
- err = 1;
|
|
- }
|
|
-
|
|
- done:
|
|
- if (!err) {
|
|
- if (!rename(SH_TMPFILE, "/etc/shadow"))
|
|
- pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
|
|
- else
|
|
- err = 1;
|
|
- }
|
|
-
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- if (setfscreatecon(prev_context)) {
|
|
- err = 1;
|
|
- }
|
|
- if (prev_context)
|
|
- freecon(prev_context);
|
|
- prev_context=NULL;
|
|
- }
|
|
-#endif
|
|
-
|
|
- if (!err) {
|
|
- return PAM_SUCCESS;
|
|
- } else {
|
|
- unlink(SH_TMPFILE);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
-}
|
|
-
|
|
static int _do_setpass(pam_handle_t* pamh, const char *forwho,
|
|
const char *fromwhat,
|
|
char *towhat, unsigned int ctrl, int remember)
|
|
@@ -768,9 +315,7 @@ static int _do_setpass(pam_handle_t* pam
|
|
enum clnt_stat err;
|
|
|
|
/* Unlock passwd file to avoid deadlock */
|
|
-#ifdef USE_LCKPWDF
|
|
- ulckpwdf();
|
|
-#endif
|
|
+ unlock_pwdf();
|
|
unlocked = 1;
|
|
|
|
/* Initialize password information */
|
|
@@ -830,129 +375,63 @@ static int _do_setpass(pam_handle_t* pam
|
|
}
|
|
|
|
if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
|
|
-#ifdef USE_LCKPWDF
|
|
if(unlocked) {
|
|
- int i = 0;
|
|
- /* These values for the number of attempts and the sleep time
|
|
- are, of course, completely arbitrary.
|
|
- My reading of the PAM docs is that, once pam_chauthtok() has been
|
|
- called with PAM_UPDATE_AUTHTOK, we are obliged to take any
|
|
- reasonable steps to make sure the token is updated; so retrying
|
|
- for 1/10 sec. isn't overdoing it. */
|
|
- while((retval = lckpwdf()) != 0 && i < 100) {
|
|
- usleep(1000);
|
|
- i++;
|
|
- }
|
|
- if(retval != 0) {
|
|
+ if (lock_pwdf() != PAM_SUCCESS) {
|
|
return PAM_AUTHTOK_LOCK_BUSY;
|
|
}
|
|
}
|
|
+#ifdef WITH_SELINUX
|
|
+ if (unix_selinux_confined())
|
|
+ return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
|
|
#endif
|
|
/* first, save old password */
|
|
- if (save_old_password(pamh, forwho, fromwhat, remember)) {
|
|
+ if (save_old_password(forwho, fromwhat, remember)) {
|
|
retval = PAM_AUTHTOK_ERR;
|
|
goto done;
|
|
}
|
|
- if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
|
|
- retval = _update_shadow(pamh, forwho, towhat);
|
|
-#ifdef WITH_SELINUX
|
|
- if (retval != PAM_SUCCESS && SELINUX_ENABLED)
|
|
- retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
|
|
-#endif
|
|
+ if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
|
|
+ retval = unix_update_shadow(pamh, forwho, towhat);
|
|
if (retval == PAM_SUCCESS)
|
|
- if (!_unix_shadowed(pwd))
|
|
- retval = _update_passwd(pamh, forwho, "x");
|
|
+ if (!is_pwd_shadowed(pwd))
|
|
+ retval = unix_update_passwd(pamh, forwho, "x");
|
|
} else {
|
|
- retval = _update_passwd(pamh, forwho, towhat);
|
|
+ retval = unix_update_passwd(pamh, forwho, towhat);
|
|
}
|
|
}
|
|
|
|
|
|
done:
|
|
-#ifdef USE_LCKPWDF
|
|
- ulckpwdf();
|
|
-#endif
|
|
+ unlock_pwdf();
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
|
|
{
|
|
- struct passwd *pwd = NULL; /* Password and shadow password */
|
|
- struct spwd *spwdent = NULL; /* file entries for the user */
|
|
- time_t curdays;
|
|
- int retval = PAM_SUCCESS;
|
|
+ struct passwd *pwent = NULL; /* Password and shadow password */
|
|
+ struct spwd *spent = NULL; /* file entries for the user */
|
|
+ int daysleft;
|
|
+ int retval;
|
|
|
|
- /* UNIX passwords area */
|
|
- pwd = getpwnam(user); /* Get password file entry... */
|
|
- if (pwd == NULL)
|
|
- return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
|
|
-
|
|
- if (_unix_shadowed(pwd)) {
|
|
- /* ...and shadow password file entry for this user, if shadowing
|
|
- is enabled */
|
|
- setspent();
|
|
- spwdent = getspnam(user);
|
|
- endspent();
|
|
+ retval = get_account_info(pamh, user, &pwent, &spent);
|
|
+ if (retval == PAM_USER_UNKNOWN) {
|
|
+ return retval;
|
|
+ }
|
|
|
|
-#ifdef WITH_SELINUX
|
|
- if (spwdent == NULL && SELINUX_ENABLED )
|
|
- spwdent = _unix_run_verify_binary(pamh, ctrl, user);
|
|
-#endif
|
|
- if (spwdent == NULL)
|
|
- return PAM_AUTHINFO_UNAVAIL;
|
|
- } else {
|
|
- if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
|
|
- uid_t save_uid;
|
|
+ if (retval == PAM_SUCCESS && spent == NULL)
|
|
+ return PAM_SUCCESS;
|
|
|
|
- save_uid = geteuid();
|
|
- seteuid (pwd->pw_uid);
|
|
- spwdent = getspnam( user );
|
|
- seteuid (save_uid);
|
|
-
|
|
- if (spwdent == NULL)
|
|
- return PAM_AUTHINFO_UNAVAIL;
|
|
- } else
|
|
- spwdent = NULL;
|
|
+ if (retval == PAM_UNIX_RUN_HELPER) {
|
|
+ retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
|
|
+ if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
|
|
+ return retval;
|
|
}
|
|
+ else if (retval == PAM_SUCCESS)
|
|
+ retval = check_shadow_expiry(pamh, spent, &daysleft);
|
|
+
|
|
+ if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
|
|
+ return PAM_SUCCESS;
|
|
|
|
- if (spwdent != NULL) {
|
|
- /* We have the user's information, now let's check if their account
|
|
- has expired (60 * 60 * 24 = number of seconds in a day) */
|
|
-
|
|
- if (off(UNIX__IAMROOT, ctrl)) {
|
|
- /* Get the current number of days since 1970 */
|
|
- curdays = time(NULL) / (60 * 60 * 24);
|
|
- if (curdays < spwdent->sp_lstchg) {
|
|
- pam_syslog(pamh, LOG_DEBUG,
|
|
- "account %s has password changed in future",
|
|
- user);
|
|
- curdays = spwdent->sp_lstchg;
|
|
- }
|
|
- if ((curdays - spwdent->sp_lstchg < spwdent->sp_min)
|
|
- && (spwdent->sp_min != -1))
|
|
- /*
|
|
- * The last password change was too recent.
|
|
- */
|
|
- retval = PAM_AUTHTOK_ERR;
|
|
- else if ((curdays - spwdent->sp_lstchg > spwdent->sp_max)
|
|
- && (curdays - spwdent->sp_lstchg > spwdent->sp_inact)
|
|
- && (curdays - spwdent->sp_lstchg >
|
|
- spwdent->sp_max + spwdent->sp_inact)
|
|
- && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
|
|
- && (spwdent->sp_lstchg != 0))
|
|
- /*
|
|
- * Their password change has been put off too long,
|
|
- */
|
|
- retval = PAM_ACCT_EXPIRED;
|
|
- else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
|
|
- && (spwdent->sp_lstchg != 0))
|
|
- /*
|
|
- * OR their account has just plain expired
|
|
- */
|
|
- retval = PAM_ACCT_EXPIRED;
|
|
- }
|
|
- }
|
|
return retval;
|
|
}
|
|
|
|
@@ -1020,8 +499,9 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
|
|
int argc, const char **argv)
|
|
{
|
|
unsigned int ctrl, lctrl;
|
|
- int retval, i;
|
|
+ int retval;
|
|
int remember = -1;
|
|
+ int rounds = -1;
|
|
|
|
/* <DO NOT free() THESE> */
|
|
const char *user;
|
|
@@ -1030,7 +510,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
|
|
|
|
D(("called."));
|
|
|
|
- ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
|
|
+ ctrl = _set_ctrl(pamh, flags, &remember, &rounds, argc, argv);
|
|
|
|
/*
|
|
* First get the name of a user
|
|
@@ -1239,40 +719,23 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
|
|
pass_new = pass_old = NULL; /* tidy up */
|
|
return retval;
|
|
}
|
|
-#ifdef USE_LCKPWDF
|
|
- /* These values for the number of attempts and the sleep time
|
|
- are, of course, completely arbitrary.
|
|
- My reading of the PAM docs is that, once pam_chauthtok() has been
|
|
- called with PAM_UPDATE_AUTHTOK, we are obliged to take any
|
|
- reasonable steps to make sure the token is updated; so retrying
|
|
- for 1/10 sec. isn't overdoing it. */
|
|
- i=0;
|
|
- while((retval = lckpwdf()) != 0 && i < 100) {
|
|
- usleep(1000);
|
|
- i++;
|
|
- }
|
|
- if(retval != 0) {
|
|
+ if (lock_pwdf() != PAM_SUCCESS) {
|
|
return PAM_AUTHTOK_LOCK_BUSY;
|
|
}
|
|
-#endif
|
|
|
|
if (pass_old) {
|
|
retval = _unix_verify_password(pamh, user, pass_old, ctrl);
|
|
if (retval != PAM_SUCCESS) {
|
|
pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
|
|
-#ifdef USE_LCKPWDF
|
|
- ulckpwdf();
|
|
-#endif
|
|
+ unlock_pwdf();
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
retval = _unix_verify_shadow(pamh, user, ctrl);
|
|
if (retval != PAM_SUCCESS) {
|
|
- pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
|
|
-#ifdef USE_LCKPWDF
|
|
- ulckpwdf();
|
|
-#endif
|
|
+ pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
|
|
+ unlock_pwdf();
|
|
return retval;
|
|
}
|
|
|
|
@@ -1281,9 +744,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
|
|
pam_syslog(pamh, LOG_NOTICE,
|
|
"new password not acceptable 2");
|
|
pass_new = pass_old = NULL; /* tidy up */
|
|
-#ifdef USE_LCKPWDF
|
|
- ulckpwdf();
|
|
-#endif
|
|
+ unlock_pwdf();
|
|
return retval;
|
|
}
|
|
|
|
@@ -1296,51 +757,13 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
|
|
* First we encrypt the new password.
|
|
*/
|
|
|
|
- if (on(UNIX_MD5_PASS, ctrl)) {
|
|
- tpass = crypt_md5_wrapper(pass_new);
|
|
- } else {
|
|
- /*
|
|
- * Salt manipulation is stolen from Rick Faith's passwd
|
|
- * program. Sorry Rick :) -- alex
|
|
- */
|
|
-
|
|
- time_t tm;
|
|
- char salt[3];
|
|
-
|
|
- time(&tm);
|
|
- salt[0] = bin_to_ascii(tm & 0x3f);
|
|
- salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
|
|
- salt[2] = '\0';
|
|
-
|
|
- if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
|
|
- /*
|
|
- * to avoid using the _extensions_ of the bigcrypt()
|
|
- * function we truncate the newly entered password
|
|
- * [Problems that followed from this are fixed as per
|
|
- * Bug 521314.]
|
|
- */
|
|
- char *temp = malloc(9);
|
|
-
|
|
- if (temp == NULL) {
|
|
- pam_syslog(pamh, LOG_CRIT,
|
|
- "out of memory for password");
|
|
- pass_new = pass_old = NULL; /* tidy up */
|
|
-#ifdef USE_LCKPWDF
|
|
- ulckpwdf();
|
|
-#endif
|
|
- return PAM_BUF_ERR;
|
|
- }
|
|
- /* copy first 8 bytes of password */
|
|
- strncpy(temp, pass_new, 8);
|
|
- temp[8] = '\0';
|
|
-
|
|
- /* no longer need cleartext */
|
|
- tpass = bigcrypt(temp, salt);
|
|
-
|
|
- _pam_delete(temp); /* tidy up */
|
|
- } else {
|
|
- tpass = bigcrypt(pass_new, salt);
|
|
- }
|
|
+ tpass = create_password_hash(pass_new, ctrl, rounds);
|
|
+ if (tpass == NULL) {
|
|
+ pam_syslog(pamh, LOG_CRIT,
|
|
+ "out of memory for password");
|
|
+ pass_new = pass_old = NULL; /* tidy up */
|
|
+ unlock_pwdf();
|
|
+ return PAM_BUF_ERR;
|
|
}
|
|
|
|
D(("password processed"));
|
|
@@ -1349,7 +772,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
|
|
|
|
retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
|
|
remember);
|
|
- /* _do_setpass has called ulckpwdf for us */
|
|
+ /* _do_setpass has called unlock_pwdf for us */
|
|
|
|
_pam_delete(tpass);
|
|
pass_old = pass_new = NULL;
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper 2007-03-12 15:35:14.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -13,7 +13,6 @@
|
|
|
|
#include "config.h"
|
|
|
|
-#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
@@ -25,401 +24,34 @@
|
|
#include <shadow.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
-#ifdef WITH_SELINUX
|
|
-#include <selinux/selinux.h>
|
|
-#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
|
|
-static security_context_t prev_context=NULL;
|
|
-static int selinux_enabled=-1;
|
|
-#else
|
|
-#define SELINUX_ENABLED 0
|
|
-#endif
|
|
-
|
|
-#define MAXPASS 200 /* the maximum length of a password */
|
|
|
|
#include <security/_pam_types.h>
|
|
#include <security/_pam_macros.h>
|
|
|
|
-#include "md5.h"
|
|
-#include "bigcrypt.h"
|
|
-
|
|
-/* syslogging function for errors and other information */
|
|
-
|
|
-static void _log_err(int err, const char *format,...)
|
|
-{
|
|
- va_list args;
|
|
-
|
|
- va_start(args, format);
|
|
- openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
|
|
- vsyslog(err, format, args);
|
|
- va_end(args);
|
|
- closelog();
|
|
-}
|
|
-
|
|
-static int _unix_shadowed(const struct passwd *pwd)
|
|
-{
|
|
- char hashpass[1024];
|
|
- if (pwd != NULL) {
|
|
- if (strcmp(pwd->pw_passwd, "x") == 0) {
|
|
- return 1;
|
|
- }
|
|
- if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) {
|
|
- strcpy(hashpass, "##");
|
|
- strcpy(hashpass + 2, pwd->pw_name);
|
|
- if (strcmp(pwd->pw_passwd, hashpass) == 0) {
|
|
- return 1;
|
|
- }
|
|
- }
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void su_sighandler(int sig)
|
|
-{
|
|
-#ifndef SA_RESETHAND
|
|
- /* emulate the behaviour of the SA_RESETHAND flag */
|
|
- if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
|
|
- signal(sig, SIG_DFL);
|
|
-#endif
|
|
- if (sig > 0) {
|
|
- _log_err(LOG_NOTICE, "caught signal %d.", sig);
|
|
- exit(sig);
|
|
- }
|
|
-}
|
|
-
|
|
-static void setup_signals(void)
|
|
-{
|
|
- struct sigaction action; /* posix signal structure */
|
|
-
|
|
- /*
|
|
- * Setup signal handlers
|
|
- */
|
|
- (void) memset((void *) &action, 0, sizeof(action));
|
|
- action.sa_handler = su_sighandler;
|
|
-#ifdef SA_RESETHAND
|
|
- action.sa_flags = SA_RESETHAND;
|
|
-#endif
|
|
- (void) sigaction(SIGILL, &action, NULL);
|
|
- (void) sigaction(SIGTRAP, &action, NULL);
|
|
- (void) sigaction(SIGBUS, &action, NULL);
|
|
- (void) sigaction(SIGSEGV, &action, NULL);
|
|
- action.sa_handler = SIG_IGN;
|
|
- action.sa_flags = 0;
|
|
- (void) sigaction(SIGTERM, &action, NULL);
|
|
- (void) sigaction(SIGHUP, &action, NULL);
|
|
- (void) sigaction(SIGINT, &action, NULL);
|
|
- (void) sigaction(SIGQUIT, &action, NULL);
|
|
-}
|
|
+#include "passverify.h"
|
|
|
|
-static int _verify_account(const char * const uname)
|
|
+static int _check_expiry(const char *uname)
|
|
{
|
|
struct spwd *spent;
|
|
struct passwd *pwent;
|
|
+ int retval;
|
|
+ int daysleft;
|
|
|
|
- pwent = getpwnam(uname);
|
|
- if (!pwent) {
|
|
- _log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
|
|
- return PAM_USER_UNKNOWN;
|
|
- }
|
|
-
|
|
- spent = getspnam( uname );
|
|
- if (!spent) {
|
|
- _log_err(LOG_ALERT, "could not get username from shadow (%s))", uname);
|
|
- return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */
|
|
- }
|
|
- printf("%ld:%ld:%ld:%ld:%ld:%ld",
|
|
- spent->sp_lstchg, /* last password change */
|
|
- spent->sp_min, /* days until change allowed. */
|
|
- spent->sp_max, /* days before change required */
|
|
- spent->sp_warn, /* days warning for expiration */
|
|
- spent->sp_inact, /* days before account inactive */
|
|
- spent->sp_expire); /* date when account expires */
|
|
-
|
|
- return PAM_SUCCESS;
|
|
-}
|
|
-
|
|
-static int _unix_verify_password(const char *name, const char *p, int nullok)
|
|
-{
|
|
- struct passwd *pwd = NULL;
|
|
- struct spwd *spwdent = NULL;
|
|
- char *salt = NULL;
|
|
- char *pp = NULL;
|
|
- int retval = PAM_AUTH_ERR;
|
|
- size_t salt_len;
|
|
-
|
|
- /* UNIX passwords area */
|
|
- setpwent();
|
|
- pwd = getpwnam(name); /* Get password file entry... */
|
|
- endpwent();
|
|
- if (pwd != NULL) {
|
|
- if (_unix_shadowed(pwd)) {
|
|
- /*
|
|
- * ...and shadow password file entry for this user,
|
|
- * if shadowing is enabled
|
|
- */
|
|
- setspent();
|
|
- spwdent = getspnam(name);
|
|
- endspent();
|
|
- if (spwdent != NULL)
|
|
- salt = x_strdup(spwdent->sp_pwdp);
|
|
- else
|
|
- pwd = NULL;
|
|
- } else {
|
|
- if (strcmp(pwd->pw_passwd, "*NP*") == 0) { /* NIS+ */
|
|
- uid_t save_uid;
|
|
-
|
|
- save_uid = geteuid();
|
|
- seteuid(pwd->pw_uid);
|
|
- spwdent = getspnam(name);
|
|
- seteuid(save_uid);
|
|
-
|
|
- salt = x_strdup(spwdent->sp_pwdp);
|
|
- } else {
|
|
- salt = x_strdup(pwd->pw_passwd);
|
|
- }
|
|
- }
|
|
- }
|
|
- if (pwd == NULL || salt == NULL) {
|
|
- _log_err(LOG_ALERT, "check pass; user unknown");
|
|
- p = NULL;
|
|
- return PAM_USER_UNKNOWN;
|
|
- }
|
|
-
|
|
- salt_len = strlen(salt);
|
|
- if (salt_len == 0) {
|
|
- return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
|
|
- }
|
|
- if (p == NULL || strlen(p) == 0) {
|
|
- _pam_overwrite(salt);
|
|
- _pam_drop(salt);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
-
|
|
- /* the moment of truth -- do we agree with the password? */
|
|
- retval = PAM_AUTH_ERR;
|
|
- if (!strncmp(salt, "$1$", 3)) {
|
|
- pp = Goodcrypt_md5(p, salt);
|
|
- if (pp && strcmp(pp, salt) == 0) {
|
|
- retval = PAM_SUCCESS;
|
|
- } else {
|
|
- _pam_overwrite(pp);
|
|
- _pam_drop(pp);
|
|
- pp = Brokencrypt_md5(p, salt);
|
|
- if (pp && strcmp(pp, salt) == 0)
|
|
- retval = PAM_SUCCESS;
|
|
- }
|
|
- } else if (*salt == '$') {
|
|
- /*
|
|
- * Ok, we don't know the crypt algorithm, but maybe
|
|
- * libcrypt nows about it? We should try it.
|
|
- */
|
|
- pp = x_strdup (crypt(p, salt));
|
|
- if (pp && strcmp(pp, salt) == 0) {
|
|
- retval = PAM_SUCCESS;
|
|
- }
|
|
- } else if (*salt == '*' || *salt == '!' || salt_len < 13) {
|
|
- retval = PAM_AUTH_ERR;
|
|
- } else {
|
|
- pp = bigcrypt(p, salt);
|
|
- /*
|
|
- * Note, we are comparing the bigcrypt of the password with
|
|
- * the contents of the password field. If the latter was
|
|
- * encrypted with regular crypt (and not bigcrypt) it will
|
|
- * have been truncated for storage relative to the output
|
|
- * of bigcrypt here. As such we need to compare only the
|
|
- * stored string with the subset of bigcrypt's result.
|
|
- * Bug 521314.
|
|
- */
|
|
- if (pp && salt_len == 13 && strlen(pp) > salt_len) {
|
|
- _pam_overwrite(pp+salt_len);
|
|
- }
|
|
-
|
|
- if (pp && strcmp(pp, salt) == 0) {
|
|
- retval = PAM_SUCCESS;
|
|
- }
|
|
+ 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;
|
|
}
|
|
- p = NULL; /* no longer needed here */
|
|
|
|
- /* clean up */
|
|
- _pam_overwrite(pp);
|
|
- _pam_drop(pp);
|
|
-
|
|
- return retval;
|
|
-}
|
|
-
|
|
-static char *getuidname(uid_t uid)
|
|
-{
|
|
- struct passwd *pw;
|
|
- static char username[32];
|
|
-
|
|
- pw = getpwuid(uid);
|
|
- if (pw == NULL)
|
|
- return NULL;
|
|
-
|
|
- strncpy(username, pw->pw_name, sizeof(username));
|
|
- username[sizeof(username) - 1] = '\0';
|
|
-
|
|
- return username;
|
|
-}
|
|
-
|
|
-#define SH_TMPFILE "/etc/nshadow"
|
|
-static int _update_shadow(const char *forwho)
|
|
-{
|
|
- struct spwd *spwdent = NULL, *stmpent = NULL;
|
|
- FILE *pwfile, *opwfile;
|
|
- int err = 1;
|
|
- int oldmask;
|
|
- struct stat st;
|
|
- char pass[MAXPASS + 1];
|
|
- char towhat[MAXPASS + 1];
|
|
- int npass=0;
|
|
-
|
|
- /* read the password from stdin (a pipe from the pam_unix module) */
|
|
-
|
|
- npass = read(STDIN_FILENO, pass, MAXPASS);
|
|
-
|
|
- if (npass < 0) { /* is it a valid password? */
|
|
-
|
|
- _log_err(LOG_DEBUG, "no password supplied");
|
|
- return PAM_AUTHTOK_ERR;
|
|
-
|
|
- } else if (npass >= MAXPASS) {
|
|
-
|
|
- _log_err(LOG_DEBUG, "password too long");
|
|
- return PAM_AUTHTOK_ERR;
|
|
-
|
|
- } else {
|
|
- /* does pass agree with the official one? */
|
|
- int retval=0;
|
|
- pass[npass] = '\0'; /* NUL terminate */
|
|
- retval = _unix_verify_password(forwho, pass, 0);
|
|
- if (retval != PAM_SUCCESS) {
|
|
+ retval = check_shadow_expiry(spent, &daysleft);
|
|
+ printf("%d\n", daysleft);
|
|
return retval;
|
|
- }
|
|
- }
|
|
-
|
|
- /* read the password from stdin (a pipe from the pam_unix module) */
|
|
-
|
|
- npass = read(STDIN_FILENO, towhat, MAXPASS);
|
|
-
|
|
- if (npass < 0) { /* is it a valid password? */
|
|
-
|
|
- _log_err(LOG_DEBUG, "no new password supplied");
|
|
- return PAM_AUTHTOK_ERR;
|
|
-
|
|
- } else if (npass >= MAXPASS) {
|
|
-
|
|
- _log_err(LOG_DEBUG, "new password too long");
|
|
- return PAM_AUTHTOK_ERR;
|
|
-
|
|
- }
|
|
-
|
|
- towhat[npass] = '\0'; /* NUL terminate */
|
|
- spwdent = getspnam(forwho);
|
|
- if (spwdent == NULL) {
|
|
- return PAM_USER_UNKNOWN;
|
|
- }
|
|
- oldmask = umask(077);
|
|
-
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- security_context_t shadow_context=NULL;
|
|
- if (getfilecon("/etc/shadow",&shadow_context)<0) {
|
|
- return PAM_AUTHTOK_ERR;
|
|
- };
|
|
- if (getfscreatecon(&prev_context)<0) {
|
|
- freecon(shadow_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- if (setfscreatecon(shadow_context)) {
|
|
- freecon(shadow_context);
|
|
- freecon(prev_context);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
- freecon(shadow_context);
|
|
- }
|
|
-#endif
|
|
- pwfile = fopen(SH_TMPFILE, "w");
|
|
- umask(oldmask);
|
|
- if (pwfile == NULL) {
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- opwfile = fopen("/etc/shadow", "r");
|
|
- if (opwfile == NULL) {
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fstat(fileno(opwfile), &st) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
|
|
- fclose(opwfile);
|
|
- fclose(pwfile);
|
|
- err = 1;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- stmpent = fgetspent(opwfile);
|
|
- while (stmpent) {
|
|
-
|
|
- if (!strcmp(stmpent->sp_namp, forwho)) {
|
|
- stmpent->sp_pwdp = towhat;
|
|
- stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
|
|
- err = 0;
|
|
- D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
|
|
- }
|
|
-
|
|
- if (putspent(stmpent, pwfile)) {
|
|
- D(("error writing entry to shadow file: %m"));
|
|
- err = 1;
|
|
- break;
|
|
- }
|
|
-
|
|
- stmpent = fgetspent(opwfile);
|
|
- }
|
|
- fclose(opwfile);
|
|
-
|
|
- if (fclose(pwfile)) {
|
|
- D(("error writing entries to shadow file: %m"));
|
|
- err = 1;
|
|
- }
|
|
-
|
|
- done:
|
|
- if (!err) {
|
|
- if (rename(SH_TMPFILE, "/etc/shadow"))
|
|
- err = 1;
|
|
- }
|
|
-
|
|
-#ifdef WITH_SELINUX
|
|
- if (SELINUX_ENABLED) {
|
|
- if (setfscreatecon(prev_context)) {
|
|
- err = 1;
|
|
- }
|
|
- if (prev_context)
|
|
- freecon(prev_context);
|
|
- prev_context=NULL;
|
|
- }
|
|
-#endif
|
|
-
|
|
- if (!err) {
|
|
- return PAM_SUCCESS;
|
|
- } else {
|
|
- unlink(SH_TMPFILE);
|
|
- return PAM_AUTHTOK_ERR;
|
|
- }
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
@@ -427,9 +59,10 @@ int main(int argc, char *argv[])
|
|
char pass[MAXPASS + 1];
|
|
char *option;
|
|
int npass, nullok;
|
|
- int force_failure = 0;
|
|
+ int blankpass = 0;
|
|
int retval = PAM_AUTH_ERR;
|
|
char *user;
|
|
+ char *passwords[] = { pass };
|
|
|
|
/*
|
|
* Catch or ignore as many signal as possible.
|
|
@@ -446,7 +79,7 @@ int main(int argc, char *argv[])
|
|
*/
|
|
|
|
if (isatty(STDIN_FILENO) || argc != 3 ) {
|
|
- _log_err(LOG_NOTICE
|
|
+ helper_log_err(LOG_NOTICE
|
|
,"inappropriate use of Unix helper binary [UID=%d]"
|
|
,getuid());
|
|
fprintf(stderr
|
|
@@ -458,11 +91,9 @@ int main(int argc, char *argv[])
|
|
|
|
/*
|
|
* Determine what the current user's name is.
|
|
- * On a SELinux enabled system with a strict policy leaving the
|
|
- * existing check prevents shadow password authentication from working.
|
|
* We must thus skip the check if the real uid is 0.
|
|
*/
|
|
- if (SELINUX_ENABLED && getuid() == 0) {
|
|
+ if (getuid() == 0) {
|
|
user=argv[1];
|
|
}
|
|
else {
|
|
@@ -476,63 +107,49 @@ int main(int argc, char *argv[])
|
|
|
|
option=argv[2];
|
|
|
|
- if (strncmp(argv[2], "verify", 8) == 0) {
|
|
- /* Get the account information from the shadow file */
|
|
- return _verify_account(argv[1]);
|
|
- }
|
|
-
|
|
- if (strncmp(option, "shadow", 8) == 0) {
|
|
- /* Attempting to change the password */
|
|
- return _update_shadow(argv[1]);
|
|
- }
|
|
-
|
|
+ if (strcmp(option, "chkexpiry") == 0)
|
|
+ /* Check account information from the shadow file */
|
|
+ return _check_expiry(argv[1]);
|
|
/* read the nullok/nonull option */
|
|
- if (strncmp(option, "nullok", 8) == 0)
|
|
+ else if (strcmp(option, "nullok") == 0)
|
|
nullok = 1;
|
|
- else
|
|
+ else if (strcmp(option, "nonull") == 0)
|
|
nullok = 0;
|
|
+ else
|
|
+ return PAM_SYSTEM_ERR;
|
|
|
|
/* read the password from stdin (a pipe from the pam_unix module) */
|
|
|
|
- npass = read(STDIN_FILENO, pass, MAXPASS);
|
|
-
|
|
- if (npass < 0) { /* is it a valid password? */
|
|
-
|
|
- _log_err(LOG_DEBUG, "no password supplied");
|
|
+ npass = read_passwords(STDIN_FILENO, 1, passwords);
|
|
|
|
- } else if (npass >= MAXPASS) {
|
|
-
|
|
- _log_err(LOG_DEBUG, "password too long");
|
|
-
|
|
- } else {
|
|
- if (npass == 0) {
|
|
- /* the password is NULL */
|
|
-
|
|
- retval = _unix_verify_password(user, NULL, nullok);
|
|
-
|
|
- } else {
|
|
- /* does pass agree with the official one? */
|
|
-
|
|
- pass[npass] = '\0'; /* NUL terminate */
|
|
- retval = _unix_verify_password(user, pass, nullok);
|
|
+ if (npass != 1) { /* is it a valid password? */
|
|
+ helper_log_err(LOG_DEBUG, "no password supplied");
|
|
+ *pass = '\0';
|
|
+ }
|
|
|
|
- }
|
|
+ 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) || force_failure) {
|
|
- _log_err(LOG_NOTICE, "password check failed for user (%s)", user);
|
|
- return PAM_AUTH_ERR;
|
|
+ if (retval != PAM_SUCCESS) {
|
|
+ if (!nullok || !blankpass)
|
|
+ /* no need to log blank pass test */
|
|
+ helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user);
|
|
+ return PAM_AUTH_ERR;
|
|
} else {
|
|
- return PAM_SUCCESS;
|
|
+ return PAM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copyright (c) Andrew G. Morgan, 1996. All rights reserved
|
|
+ * Copyright (c) Red Hat, Inc., 2007,2008. All rights reserved
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/support.h.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/support.h
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/support.h.update-helper 2007-01-23 10:30:23.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/support.h 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- * $Id: pam-0.99.8.1-unix-update-helper.patch,v 1.3 2008/01/08 18:54:47 tmraz Exp $
|
|
+ * $Id: pam-0.99.8.1-unix-update-helper.patch,v 1.3 2008/01/08 18:54:47 tmraz Exp $
|
|
*/
|
|
|
|
#ifndef _PAM_UNIX_SUPPORT_H
|
|
@@ -84,8 +84,12 @@ typedef struct {
|
|
#define UNIX_NOREAP 21 /* don't reap child process */
|
|
#define UNIX_BROKEN_SHADOW 22 /* ignore errors reading password aging
|
|
* information during acct management */
|
|
+#define UNIX_SHA256_PASS 23 /* new password hashes will use SHA256 */
|
|
+#define UNIX_SHA512_PASS 24 /* new password hashes will use SHA512 */
|
|
+#define UNIX_ALGO_ROUNDS 25 /* optional number of rounds for new
|
|
+ password hash algorithms */
|
|
/* -------------- */
|
|
-#define UNIX_CTRLS_ 23 /* number of ctrl arguments defined */
|
|
+#define UNIX_CTRLS_ 26 /* number of ctrl arguments defined */
|
|
|
|
|
|
static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
|
|
@@ -116,6 +120,9 @@ static const UNIX_Ctrls unix_args[UNIX_C
|
|
/* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000},
|
|
/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000},
|
|
/* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000},
|
|
+/* UNIX_SHA256_PASS */ {"sha256", _ALL_ON_^(040420000), 020000000},
|
|
+/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(020420000), 040000000},
|
|
+/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000},
|
|
};
|
|
|
|
#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
|
|
@@ -131,8 +138,8 @@ static const UNIX_Ctrls unix_args[UNIX_C
|
|
|
|
extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl
|
|
,int type, const char *text);
|
|
-extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int argc,
|
|
- const char **argv);
|
|
+extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int *rounds,
|
|
+ int argc, const char **argv);
|
|
extern int _unix_getpwnam (pam_handle_t *pamh,
|
|
const char *name, int files, int nis,
|
|
struct passwd **ret);
|
|
@@ -149,7 +156,7 @@ extern int _unix_read_password(pam_handl
|
|
,const char *prompt2
|
|
,const char *data_name
|
|
,const void **pass);
|
|
-extern int _unix_shadowed(const struct passwd *pwd);
|
|
|
|
-extern struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user);
|
|
+extern int _unix_run_verify_binary(pam_handle_t *pamh,
|
|
+ unsigned int ctrl, const char *user, int *daysleft);
|
|
#endif /* _PAM_UNIX_SUPPORT_H */
|
|
diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h
|
|
--- /dev/null 2008-01-05 20:21:31.621001512 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -0,0 +1,124 @@
|
|
+/*
|
|
+ * Copyright information at end of file.
|
|
+ */
|
|
+
|
|
+#include <sys/types.h>
|
|
+#include <pwd.h>
|
|
+#include <security/pam_modules.h>
|
|
+
|
|
+#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT
|
|
+
|
|
+#define MAXPASS 200 /* the maximum length of a password */
|
|
+
|
|
+#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
|
|
+
|
|
+int
|
|
+verify_pwd_hash(const char *p, const char *hash, unsigned int nullok);
|
|
+
|
|
+int
|
|
+is_pwd_shadowed(const struct passwd *pwd);
|
|
+
|
|
+char *
|
|
+crypt_md5_wrapper(const char *pass_new);
|
|
+
|
|
+char *
|
|
+create_password_hash(const char *password, unsigned int ctrl, int rounds);
|
|
+
|
|
+int
|
|
+unix_selinux_confined(void);
|
|
+
|
|
+int
|
|
+lock_pwdf(void);
|
|
+
|
|
+void
|
|
+unlock_pwdf(void);
|
|
+
|
|
+int
|
|
+save_old_password(const char *forwho, const char *oldpass,
|
|
+ int howmany);
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+void
|
|
+helper_log_err(int err, const char *format,...);
|
|
+
|
|
+int
|
|
+helper_verify_password(const char *name, const char *p, int nullok);
|
|
+
|
|
+void
|
|
+setup_signals(void);
|
|
+
|
|
+char *
|
|
+getuidname(uid_t uid);
|
|
+
|
|
+int
|
|
+read_passwords(int fd, int npass, char **passwords);
|
|
+
|
|
+int
|
|
+get_account_info(const char *name,
|
|
+ struct passwd **pwd, struct spwd **spwdent);
|
|
+
|
|
+int
|
|
+get_pwd_hash(const char *name,
|
|
+ struct passwd **pwd, char **hash);
|
|
+
|
|
+int
|
|
+check_shadow_expiry(struct spwd *spent, int *daysleft);
|
|
+
|
|
+int
|
|
+unix_update_passwd(const char *forwho, const char *towhat);
|
|
+
|
|
+int
|
|
+unix_update_shadow(const char *forwho, char *towhat);
|
|
+#else
|
|
+int
|
|
+get_account_info(pam_handle_t *pamh, const char *name,
|
|
+ struct passwd **pwd, struct spwd **spwdent);
|
|
+
|
|
+int
|
|
+get_pwd_hash(pam_handle_t *pamh, const char *name,
|
|
+ struct passwd **pwd, char **hash);
|
|
+
|
|
+int
|
|
+check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft);
|
|
+
|
|
+int
|
|
+unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat);
|
|
+
|
|
+int
|
|
+unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat);
|
|
+#endif
|
|
+
|
|
+/* ****************************************************************** *
|
|
+ * Copyright (c) Red Hat, Inc. 2007.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, and the entire permission notice in its entirety,
|
|
+ * including the disclaimer of warranties.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. The name of the author may not be used to endorse or promote
|
|
+ * products derived from this software without specific prior
|
|
+ * written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
|
+ * the GNU Public License, in which case the provisions of the GPL are
|
|
+ * required INSTEAD OF the above restrictions. (This clause is
|
|
+ * necessary due to a potential bad interaction between the GPL and
|
|
+ * the restrictions contained in a BSD-style copyright.)
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c.update-helper 2006-06-27 10:38:14.000000000 +0200
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -47,10 +47,6 @@
|
|
#include <time.h> /* for time() */
|
|
#include <errno.h>
|
|
#include <sys/wait.h>
|
|
-#ifdef WITH_SELINUX
|
|
-#include <selinux/selinux.h>
|
|
-#define SELINUX_ENABLED is_selinux_enabled()>0
|
|
-#endif
|
|
|
|
#include <security/_pam_macros.h>
|
|
|
|
@@ -63,12 +59,10 @@
|
|
#include <security/pam_modutil.h>
|
|
|
|
#include "support.h"
|
|
+#include "passverify.h"
|
|
|
|
-#ifdef WITH_SELINUX
|
|
-
|
|
-struct spwd spwd;
|
|
-
|
|
-struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user)
|
|
+int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
|
|
+ const char *user, int *daysleft)
|
|
{
|
|
int retval=0, child, fds[2];
|
|
void (*sighandler)(int) = NULL;
|
|
@@ -78,7 +72,7 @@ struct spwd *_unix_run_verify_binary(pam
|
|
if (pipe(fds) != 0) {
|
|
D(("could not make pipe"));
|
|
pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m");
|
|
- return NULL;
|
|
+ return PAM_AUTH_ERR;
|
|
}
|
|
D(("called."));
|
|
|
|
@@ -117,7 +111,7 @@ struct spwd *_unix_run_verify_binary(pam
|
|
}
|
|
}
|
|
|
|
- if (SELINUX_ENABLED && geteuid() == 0) {
|
|
+ if (geteuid() == 0) {
|
|
/* must set the real uid to 0 so the helper will not error
|
|
out if pam is called from setuid binary (su, sudo...) */
|
|
setuid(0);
|
|
@@ -126,7 +120,7 @@ struct spwd *_unix_run_verify_binary(pam
|
|
/* exec binary helper */
|
|
args[0] = x_strdup(CHKPWD_HELPER);
|
|
args[1] = x_strdup(user);
|
|
- args[2] = x_strdup("verify");
|
|
+ args[2] = x_strdup("chkexpiry");
|
|
|
|
execve(CHKPWD_HELPER, args, envp);
|
|
|
|
@@ -134,11 +128,12 @@ struct spwd *_unix_run_verify_binary(pam
|
|
/* should not get here: exit with error */
|
|
close (fds[1]);
|
|
D(("helper binary is not available"));
|
|
+ printf("-1\n");
|
|
exit(PAM_AUTHINFO_UNAVAIL);
|
|
} else {
|
|
close(fds[1]);
|
|
if (child > 0) {
|
|
- char buf[1024];
|
|
+ char buf[32];
|
|
int rc=0;
|
|
rc=waitpid(child, &retval, 0); /* wait for helper to complete */
|
|
if (rc<0) {
|
|
@@ -146,22 +141,16 @@ struct spwd *_unix_run_verify_binary(pam
|
|
retval = PAM_AUTH_ERR;
|
|
} else {
|
|
retval = WEXITSTATUS(retval);
|
|
- if (retval != PAM_AUTHINFO_UNAVAIL) {
|
|
- rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
|
|
- if(rc > 0) {
|
|
+ rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
|
|
+ if(rc > 0) {
|
|
buf[rc] = '\0';
|
|
- if (sscanf(buf,"%ld:%ld:%ld:%ld:%ld:%ld",
|
|
- &spwd.sp_lstchg, /* last password change */
|
|
- &spwd.sp_min, /* days until change allowed. */
|
|
- &spwd.sp_max, /* days before change required */
|
|
- &spwd.sp_warn, /* days warning for expiration */
|
|
- &spwd.sp_inact, /* days before account inactive */
|
|
- &spwd.sp_expire) /* date when account expires */ != 6 ) retval = PAM_AUTH_ERR;
|
|
+ if (sscanf(buf,"%d", daysleft) != 1 )
|
|
+ retval = PAM_AUTH_ERR;
|
|
}
|
|
- else {
|
|
- pam_syslog(pamh, LOG_ERR, " ERROR %d: %m", rc); retval = PAM_AUTH_ERR;
|
|
+ else {
|
|
+ pam_syslog(pamh, LOG_ERR, "read unix_chkpwd output error %d: %m", rc);
|
|
+ retval = PAM_AUTH_ERR;
|
|
}
|
|
- }
|
|
}
|
|
} else {
|
|
pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
|
|
@@ -174,15 +163,9 @@ struct spwd *_unix_run_verify_binary(pam
|
|
(void) signal(SIGCHLD, sighandler); /* restore old signal handler */
|
|
}
|
|
D(("Returning %d",retval));
|
|
- if (retval != PAM_SUCCESS) {
|
|
- return NULL;
|
|
- }
|
|
- return &spwd;
|
|
+ return retval;
|
|
}
|
|
|
|
-#endif
|
|
-
|
|
-
|
|
/*
|
|
* PAM framework looks for this entry-point to pass control to the
|
|
* account management module.
|
|
@@ -195,14 +178,13 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_hand
|
|
const void *void_uname;
|
|
const char *uname;
|
|
int retval, daysleft;
|
|
- time_t curdays;
|
|
struct spwd *spent;
|
|
struct passwd *pwent;
|
|
char buf[256];
|
|
|
|
D(("called."));
|
|
|
|
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
|
|
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
|
|
|
|
retval = pam_get_item(pamh, PAM_USER, &void_uname);
|
|
uname = void_uname;
|
|
@@ -214,134 +196,90 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_hand
|
|
return PAM_USER_UNKNOWN;
|
|
}
|
|
|
|
- pwent = pam_modutil_getpwnam(pamh, uname);
|
|
- if (!pwent) {
|
|
+ retval = get_account_info(pamh, uname, &pwent, &spent);
|
|
+ if (retval == PAM_USER_UNKNOWN) {
|
|
pam_syslog(pamh, LOG_ALERT,
|
|
"could not identify user (from getpwnam(%s))",
|
|
uname);
|
|
- return PAM_USER_UNKNOWN;
|
|
+ return retval;
|
|
}
|
|
|
|
- if (!strcmp( pwent->pw_passwd, "*NP*" )) { /* NIS+ */
|
|
- uid_t save_euid, save_uid;
|
|
-
|
|
- save_euid = geteuid();
|
|
- save_uid = getuid();
|
|
- if (save_uid == pwent->pw_uid)
|
|
- setreuid( save_euid, save_uid );
|
|
- else {
|
|
- setreuid( 0, -1 );
|
|
- if (setreuid( -1, pwent->pw_uid ) == -1) {
|
|
- setreuid( -1, 0 );
|
|
- setreuid( 0, -1 );
|
|
- if(setreuid( -1, pwent->pw_uid ) == -1)
|
|
- return PAM_CRED_INSUFFICIENT;
|
|
- }
|
|
- }
|
|
- spent = pam_modutil_getspnam (pamh, uname);
|
|
- if (save_uid == pwent->pw_uid)
|
|
- setreuid( save_uid, save_euid );
|
|
- else {
|
|
- if (setreuid( -1, 0 ) == -1)
|
|
- setreuid( save_uid, -1 );
|
|
- setreuid( -1, save_euid );
|
|
- }
|
|
-
|
|
- } else if (_unix_shadowed (pwent))
|
|
- spent = pam_modutil_getspnam (pamh, uname);
|
|
- else
|
|
+ if (retval == PAM_SUCCESS && spent == NULL)
|
|
return PAM_SUCCESS;
|
|
|
|
-#ifdef WITH_SELINUX
|
|
- if (!spent && SELINUX_ENABLED )
|
|
- spent = _unix_run_verify_binary(pamh, ctrl, uname);
|
|
-#endif
|
|
-
|
|
- if (!spent)
|
|
+ if (retval == PAM_UNIX_RUN_HELPER) {
|
|
+ retval = _unix_run_verify_binary(pamh, ctrl, uname, &daysleft);
|
|
+ if (retval == PAM_AUTHINFO_UNAVAIL &&
|
|
+ on(UNIX_BROKEN_SHADOW, ctrl))
|
|
+ return PAM_SUCCESS;
|
|
+ } else if (retval != PAM_SUCCESS) {
|
|
if (on(UNIX_BROKEN_SHADOW,ctrl))
|
|
return PAM_SUCCESS;
|
|
+ else
|
|
+ return retval;
|
|
+ } else
|
|
+ retval = check_shadow_expiry(pamh, spent, &daysleft);
|
|
|
|
- if (!spent)
|
|
- return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */
|
|
-
|
|
- curdays = time(NULL) / (60 * 60 * 24);
|
|
- D(("today is %d, last change %d", curdays, spent->sp_lstchg));
|
|
- if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
|
|
+ switch (retval) {
|
|
+ case PAM_ACCT_EXPIRED:
|
|
pam_syslog(pamh, LOG_NOTICE,
|
|
- "account %s has expired (account expired)",
|
|
- uname);
|
|
+ "account %s has expired (account expired)",
|
|
+ uname);
|
|
_make_remark(pamh, ctrl, PAM_ERROR_MSG,
|
|
- _("Your account has expired; please contact your system administrator"));
|
|
- D(("account expired"));
|
|
- return PAM_ACCT_EXPIRED;
|
|
- }
|
|
- if (spent->sp_lstchg == 0) {
|
|
- pam_syslog(pamh, LOG_NOTICE,
|
|
- "expired password for user %s (root enforced)",
|
|
- uname);
|
|
- _make_remark(pamh, ctrl, PAM_ERROR_MSG,
|
|
- _("You are required to change your password immediately (root enforced)"));
|
|
- D(("need a new password"));
|
|
- return PAM_NEW_AUTHTOK_REQD;
|
|
- }
|
|
- if (curdays < spent->sp_lstchg) {
|
|
- pam_syslog(pamh, LOG_DEBUG,
|
|
- "account %s has password changed in future",
|
|
- uname);
|
|
- return PAM_SUCCESS;
|
|
- }
|
|
- if ((curdays - spent->sp_lstchg > spent->sp_max)
|
|
- && (curdays - spent->sp_lstchg > spent->sp_inact)
|
|
- && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
|
|
- && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
|
|
+ _("Your account has expired; please contact your system administrator"));
|
|
+ break;
|
|
+ case PAM_NEW_AUTHTOK_REQD:
|
|
+ if (daysleft == 0) {
|
|
+ pam_syslog(pamh, LOG_NOTICE,
|
|
+ "expired password for user %s (root enforced)",
|
|
+ uname);
|
|
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
|
|
+ _("You are required to change your password immediately (root enforced)"));
|
|
+ } else {
|
|
+ pam_syslog(pamh, LOG_DEBUG,
|
|
+ "expired password for user %s (password aged)",
|
|
+ uname);
|
|
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
|
|
+ _("You are required to change your password immediately (password aged)"));
|
|
+ }
|
|
+ break;
|
|
+ case PAM_AUTHTOK_EXPIRED:
|
|
pam_syslog(pamh, LOG_NOTICE,
|
|
- "account %s has expired (failed to change password)",
|
|
- uname);
|
|
- _make_remark(pamh, ctrl, PAM_ERROR_MSG,
|
|
- _("Your account has expired; please contact your system administrator"));
|
|
- D(("account expired 2"));
|
|
- return PAM_ACCT_EXPIRED;
|
|
- }
|
|
- if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
|
|
- pam_syslog(pamh, LOG_DEBUG,
|
|
- "expired password for user %s (password aged)",
|
|
- uname);
|
|
+ "account %s has expired (failed to change password)",
|
|
+ uname);
|
|
_make_remark(pamh, ctrl, PAM_ERROR_MSG,
|
|
- _("You are required to change your password immediately (password aged)"));
|
|
- D(("need a new password 2"));
|
|
- return PAM_NEW_AUTHTOK_REQD;
|
|
- }
|
|
- if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
|
|
- && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
|
|
- daysleft = (spent->sp_lstchg + spent->sp_max) - curdays;
|
|
- pam_syslog(pamh, LOG_DEBUG,
|
|
- "password for user %s will expire in %d days",
|
|
- uname, daysleft);
|
|
-#ifdef HAVE_DNGETTEXT
|
|
- snprintf (buf, sizeof (buf),
|
|
- dngettext(PACKAGE,
|
|
- "Warning: your password will expire in %d day",
|
|
- "Warning: your password will expire in %d days",
|
|
- daysleft),
|
|
- daysleft);
|
|
+ _("Your account has expired; please contact your system administrator"));
|
|
+ break;
|
|
+ case PAM_SUCCESS:
|
|
+ if (daysleft >= 0) {
|
|
+ pam_syslog(pamh, LOG_DEBUG,
|
|
+ "password for user %s will expire in %d days",
|
|
+ uname, daysleft);
|
|
+#if defined HAVE_DNGETTEXT && defined ENABLE_NLS
|
|
+ snprintf (buf, sizeof (buf),
|
|
+ dngettext(PACKAGE,
|
|
+ "Warning: your password will expire in %d day",
|
|
+ "Warning: your password will expire in %d days",
|
|
+ daysleft),
|
|
+ daysleft);
|
|
#else
|
|
- if (daysleft == 1)
|
|
- snprintf(buf, sizeof (buf),
|
|
- _("Warning: your password will expire in %d day"),
|
|
- daysleft);
|
|
- else
|
|
- snprintf(buf, sizeof (buf),
|
|
- /* TRANSLATORS: only used if dngettext is not support
|
|
-ed */
|
|
- _("Warning: your password will expire in %d days"),
|
|
- daysleft);
|
|
+ if (daysleft == 1)
|
|
+ snprintf(buf, sizeof (buf),
|
|
+ _("Warning: your password will expire in %d day"),
|
|
+ daysleft);
|
|
+ else
|
|
+ snprintf(buf, sizeof (buf),
|
|
+ /* TRANSLATORS: only used if dngettext is not supported */
|
|
+ _("Warning: your password will expire in %d days"),
|
|
+ daysleft);
|
|
#endif
|
|
- _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
|
|
+ _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
|
|
+ }
|
|
}
|
|
|
|
D(("all done"));
|
|
|
|
- return PAM_SUCCESS;
|
|
+ return retval;
|
|
}
|
|
|
|
|
|
diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c
|
|
--- /dev/null 2008-01-05 20:21:31.621001512 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -0,0 +1,194 @@
|
|
+/*
|
|
+ * This program is designed to run setuid(root) or with sufficient
|
|
+ * privilege to read all of the unix password databases. It is designed
|
|
+ * to provide a mechanism for the current user (defined by this
|
|
+ * process' uid) to verify their own password.
|
|
+ *
|
|
+ * The password is read from the standard input. The exit status of
|
|
+ * this program indicates whether the user is authenticated or not.
|
|
+ *
|
|
+ * Copyright information is located at the end of the file.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+
|
|
+#include <stdarg.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <syslog.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <pwd.h>
|
|
+#include <shadow.h>
|
|
+#include <signal.h>
|
|
+#include <time.h>
|
|
+#include <sys/time.h>
|
|
+#ifdef WITH_SELINUX
|
|
+#include <selinux/selinux.h>
|
|
+#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
|
|
+static int selinux_enabled=-1;
|
|
+#else
|
|
+#define SELINUX_ENABLED 0
|
|
+#endif
|
|
+
|
|
+#include <security/_pam_types.h>
|
|
+#include <security/_pam_macros.h>
|
|
+
|
|
+#include "passverify.h"
|
|
+
|
|
+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;
|
|
+ }
|
|
+
|
|
+ /* does pass agree with the official one?
|
|
+ we always allow change from null pass */
|
|
+ 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 *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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
|
|
+ * Copyright (c) Red Hat, Inc., 2007, 2008. All rights reserved
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, and the entire permission notice in its entirety,
|
|
+ * including the disclaimer of warranties.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. The name of the author may not be used to endorse or promote
|
|
+ * products derived from this software without specific prior
|
|
+ * written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
|
+ * the GNU Public License, in which case the provisions of the GPL are
|
|
+ * required INSTEAD OF the above restrictions. (This clause is
|
|
+ * necessary due to a potential bad interaction between the GPL and
|
|
+ * the restrictions contained in a BSD-style copyright.)
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c
|
|
--- /dev/null 2008-01-05 20:21:31.621001512 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -0,0 +1,1083 @@
|
|
+/*
|
|
+ * Copyright information at end of file.
|
|
+ */
|
|
+#include "config.h"
|
|
+#include <security/_pam_macros.h>
|
|
+#include <security/pam_modules.h>
|
|
+#include "support.h"
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
+#include <pwd.h>
|
|
+#include <shadow.h>
|
|
+#include <syslog.h>
|
|
+#include <stdarg.h>
|
|
+#include <signal.h>
|
|
+#include <errno.h>
|
|
+#include <time.h>
|
|
+#include <sys/time.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+
|
|
+#include "md5.h"
|
|
+#include "bigcrypt.h"
|
|
+#include "passverify.h"
|
|
+
|
|
+#ifdef WITH_SELINUX
|
|
+#include <selinux/selinux.h>
|
|
+#define SELINUX_ENABLED is_selinux_enabled()>0
|
|
+#else
|
|
+#define SELINUX_ENABLED 0
|
|
+#endif
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+#define pam_modutil_getpwnam(h,n) getpwnam(n)
|
|
+#define pam_modutil_getspnam(h,n) getspnam(n)
|
|
+#define pam_syslog(h,a,b,c) helper_log_err(a,b,c)
|
|
+#else
|
|
+#include <security/pam_modutil.h>
|
|
+#include <security/pam_ext.h>
|
|
+#endif
|
|
+
|
|
+#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
|
|
+# include "./lckpwdf.-c"
|
|
+#endif
|
|
+
|
|
+int
|
|
+verify_pwd_hash(const char *p, const char *hash, unsigned int nullok)
|
|
+{
|
|
+ size_t hash_len = strlen(hash);
|
|
+ char *pp = NULL;
|
|
+ int retval;
|
|
+ D(("called"));
|
|
+
|
|
+ if (!hash_len) {
|
|
+ /* the stored password is NULL */
|
|
+ if (nullok) { /* this means we've succeeded */
|
|
+ D(("user has empty password - access granted"));
|
|
+ retval = PAM_SUCCESS;
|
|
+ } else {
|
|
+ D(("user has empty password - access denied"));
|
|
+ retval = PAM_AUTH_ERR;
|
|
+ }
|
|
+ } else if (!p || *hash == '*' || *hash == '!') {
|
|
+ retval = PAM_AUTH_ERR;
|
|
+ } else {
|
|
+ if (!strncmp(hash, "$1$", 3)) {
|
|
+ pp = Goodcrypt_md5(p, hash);
|
|
+ if (pp && strcmp(pp, hash) != 0) {
|
|
+ _pam_delete(pp);
|
|
+ pp = Brokencrypt_md5(p, hash);
|
|
+ }
|
|
+ } else if (*hash != '$' && hash_len >= 13) {
|
|
+ pp = bigcrypt(p, hash);
|
|
+ if (pp && hash_len == 13 && strlen(pp) > hash_len) {
|
|
+ _pam_overwrite(pp + hash_len);
|
|
+ }
|
|
+ } else {
|
|
+ /*
|
|
+ * Ok, we don't know the crypt algorithm, but maybe
|
|
+ * libcrypt nows about it? We should try it.
|
|
+ */
|
|
+ pp = x_strdup(crypt(p, hash));
|
|
+ }
|
|
+ p = NULL; /* no longer needed here */
|
|
+
|
|
+ /* the moment of truth -- do we agree with the password? */
|
|
+ D(("comparing state of pp[%s] and salt[%s]", pp, salt));
|
|
+
|
|
+ if (pp && strcmp(pp, hash) == 0) {
|
|
+ retval = PAM_SUCCESS;
|
|
+ } else {
|
|
+ retval = PAM_AUTH_ERR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (pp)
|
|
+ _pam_delete(pp);
|
|
+ D(("done [%d].", retval));
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+int
|
|
+is_pwd_shadowed(const struct passwd *pwd)
|
|
+{
|
|
+ if (pwd != NULL) {
|
|
+ if (strcmp(pwd->pw_passwd, "x") == 0) {
|
|
+ return 1;
|
|
+ }
|
|
+ if ((pwd->pw_passwd[0] == '#') &&
|
|
+ (pwd->pw_passwd[1] == '#') &&
|
|
+ (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+int
|
|
+get_account_info(const char *name,
|
|
+ struct passwd **pwd, struct spwd **spwdent)
|
|
+#else
|
|
+int
|
|
+get_account_info(pam_handle_t *pamh, const char *name,
|
|
+ struct passwd **pwd, struct spwd **spwdent)
|
|
+#endif
|
|
+{
|
|
+ /* UNIX passwords area */
|
|
+ *pwd = pam_modutil_getpwnam(pamh, name); /* Get password file entry... */
|
|
+ *spwdent = NULL;
|
|
+
|
|
+ if (*pwd != NULL) {
|
|
+ if (strcmp((*pwd)->pw_passwd, "*NP*") == 0)
|
|
+ { /* NIS+ */
|
|
+#ifdef HELPER_COMPILE
|
|
+ uid_t save_euid, save_uid;
|
|
+
|
|
+ save_euid = geteuid();
|
|
+ save_uid = getuid();
|
|
+ if (save_uid == (*pwd)->pw_uid)
|
|
+ setreuid(save_euid, save_uid);
|
|
+ else {
|
|
+ setreuid(0, -1);
|
|
+ if (setreuid(-1, (*pwd)->pw_uid) == -1) {
|
|
+ setreuid(-1, 0);
|
|
+ setreuid(0, -1);
|
|
+ if(setreuid(-1, (*pwd)->pw_uid) == -1)
|
|
+ return PAM_CRED_INSUFFICIENT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *spwdent = pam_modutil_getspnam(pamh, name);
|
|
+ if (save_uid == (*pwd)->pw_uid)
|
|
+ setreuid(save_uid, save_euid);
|
|
+ else {
|
|
+ setreuid(-1, 0);
|
|
+ setreuid(save_uid, -1);
|
|
+ setreuid(-1, save_euid);
|
|
+ }
|
|
+
|
|
+ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
|
|
+ return PAM_AUTHINFO_UNAVAIL;
|
|
+#else
|
|
+ /* we must run helper for NIS+ passwords */
|
|
+ return PAM_UNIX_RUN_HELPER;
|
|
+#endif
|
|
+ } else if (is_pwd_shadowed(*pwd)) {
|
|
+ /*
|
|
+ * ...and shadow password file entry for this user,
|
|
+ * if shadowing is enabled
|
|
+ */
|
|
+#ifndef HELPER_COMPILE
|
|
+ if (geteuid() || SELINUX_ENABLED)
|
|
+ return PAM_UNIX_RUN_HELPER;
|
|
+#endif
|
|
+ *spwdent = pam_modutil_getspnam(pamh, name);
|
|
+ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
|
|
+ return PAM_AUTHINFO_UNAVAIL;
|
|
+ }
|
|
+ } else {
|
|
+ return PAM_USER_UNKNOWN;
|
|
+ }
|
|
+ return PAM_SUCCESS;
|
|
+}
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+int
|
|
+get_pwd_hash(const char *name,
|
|
+ struct passwd **pwd, char **hash)
|
|
+#else
|
|
+int
|
|
+get_pwd_hash(pam_handle_t *pamh, const char *name,
|
|
+ struct passwd **pwd, char **hash)
|
|
+#endif
|
|
+{
|
|
+ int retval;
|
|
+ struct spwd *spwdent = NULL;
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+ retval = get_account_info(name, pwd, &spwdent);
|
|
+#else
|
|
+ retval = get_account_info(pamh, name, pwd, &spwdent);
|
|
+#endif
|
|
+ if (retval != PAM_SUCCESS) {
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ if (spwdent)
|
|
+ *hash = x_strdup(spwdent->sp_pwdp);
|
|
+ else
|
|
+ *hash = x_strdup((*pwd)->pw_passwd);
|
|
+ if (*hash == NULL)
|
|
+ return PAM_BUF_ERR;
|
|
+
|
|
+ return PAM_SUCCESS;
|
|
+}
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+int
|
|
+check_shadow_expiry(struct spwd *spent, int *daysleft)
|
|
+#else
|
|
+int
|
|
+check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft)
|
|
+#endif
|
|
+{
|
|
+ long int curdays;
|
|
+ *daysleft = -1;
|
|
+ curdays = (long int)(time(NULL) / (60 * 60 * 24));
|
|
+ D(("today is %d, last change %d", curdays, spent->sp_lstchg));
|
|
+ if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
|
|
+ D(("account expired"));
|
|
+ return PAM_ACCT_EXPIRED;
|
|
+ }
|
|
+ if (spent->sp_lstchg == 0) {
|
|
+ D(("need a new password"));
|
|
+ *daysleft = 0;
|
|
+ return PAM_NEW_AUTHTOK_REQD;
|
|
+ }
|
|
+ if (curdays < spent->sp_lstchg) {
|
|
+ pam_syslog(pamh, LOG_DEBUG,
|
|
+ "account %s has password changed in future",
|
|
+ spent->sp_namp);
|
|
+ return PAM_SUCCESS;
|
|
+ }
|
|
+ if ((curdays - spent->sp_lstchg > spent->sp_max)
|
|
+ && (curdays - spent->sp_lstchg > spent->sp_inact)
|
|
+ && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
|
|
+ && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
|
|
+ *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
|
|
+ D(("authtok expired"));
|
|
+ return PAM_AUTHTOK_EXPIRED;
|
|
+ }
|
|
+ if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
|
|
+ D(("need a new password 2"));
|
|
+ return PAM_NEW_AUTHTOK_REQD;
|
|
+ }
|
|
+ if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
|
|
+ && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
|
|
+ *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
|
|
+ D(("warn before expiry"));
|
|
+ }
|
|
+ return PAM_SUCCESS;
|
|
+
|
|
+}
|
|
+
|
|
+/* passwd/salt conversion macros */
|
|
+
|
|
+#define PW_TMPFILE "/etc/npasswd"
|
|
+#define SH_TMPFILE "/etc/nshadow"
|
|
+#define OPW_TMPFILE "/etc/security/nopasswd"
|
|
+
|
|
+/*
|
|
+ * i64c - convert an integer to a radix 64 character
|
|
+ */
|
|
+static int
|
|
+i64c(int i)
|
|
+{
|
|
+ if (i < 0)
|
|
+ return ('.');
|
|
+ else if (i > 63)
|
|
+ return ('z');
|
|
+ if (i == 0)
|
|
+ return ('.');
|
|
+ if (i == 1)
|
|
+ return ('/');
|
|
+ if (i >= 2 && i <= 11)
|
|
+ return ('0' - 2 + i);
|
|
+ if (i >= 12 && i <= 37)
|
|
+ return ('A' - 12 + i);
|
|
+ if (i >= 38 && i <= 63)
|
|
+ return ('a' - 38 + i);
|
|
+ return ('\0');
|
|
+}
|
|
+
|
|
+static void
|
|
+crypt_make_salt(char *where, int length)
|
|
+{
|
|
+ struct timeval tv;
|
|
+ MD5_CTX ctx;
|
|
+ unsigned char tmp[16];
|
|
+ unsigned char *src = (unsigned char *)where;
|
|
+ int i;
|
|
+#ifdef PATH_RANDOMDEV
|
|
+ int fd;
|
|
+ int rv;
|
|
+
|
|
+ if ((rv = fd = open (PATH_RANDOMDEV, O_RDONLY)) != -1) {
|
|
+ while ((rv = read(fd, where, length)) != length && errno == EINTR);
|
|
+ close (fd);
|
|
+ }
|
|
+ if (rv != length) {
|
|
+#endif
|
|
+ /*
|
|
+ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
|
|
+ * removed use of static variables (AGM)
|
|
+ *
|
|
+ * will work correctly only for length <= 16 */
|
|
+ src = tmp;
|
|
+ GoodMD5Init(&ctx);
|
|
+ gettimeofday(&tv, (struct timezone *) 0);
|
|
+ GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
|
|
+ i = getpid();
|
|
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
|
|
+ i = clock();
|
|
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
|
|
+ GoodMD5Update(&ctx, src, length);
|
|
+ GoodMD5Final(tmp, &ctx);
|
|
+#ifdef PATH_RANDOMDEV
|
|
+ }
|
|
+#endif
|
|
+ for (i = 0; i < length; i++)
|
|
+ *where++ = i64c(src[i] & 077);
|
|
+ *where = '\0';
|
|
+}
|
|
+
|
|
+char *
|
|
+crypt_md5_wrapper(const char *pass_new)
|
|
+{
|
|
+ unsigned char result[16];
|
|
+ char *cp = (char *) result;
|
|
+
|
|
+ cp = stpcpy(cp, "$1$"); /* magic for the MD5 */
|
|
+ crypt_make_salt(cp, 9);
|
|
+
|
|
+ /* no longer need cleartext */
|
|
+ cp = Goodcrypt_md5(pass_new, (const char *) result);
|
|
+ pass_new = NULL;
|
|
+
|
|
+ return cp;
|
|
+}
|
|
+
|
|
+char *
|
|
+create_password_hash(const char *password, unsigned int ctrl, int rounds)
|
|
+{
|
|
+ const char *algoid;
|
|
+ char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */
|
|
+ char *sp;
|
|
+
|
|
+ if (on(UNIX_MD5_PASS, ctrl)) {
|
|
+ return crypt_md5_wrapper(password);
|
|
+ }
|
|
+ if (on(UNIX_SHA256_PASS, ctrl)) {
|
|
+ algoid = "$5$";
|
|
+ } else if (on(UNIX_SHA512_PASS, ctrl)) {
|
|
+ algoid = "$6$";
|
|
+ } else { /* must be crypt/bigcrypt */
|
|
+ char tmppass[9];
|
|
+ char *crypted;
|
|
+
|
|
+ crypt_make_salt(salt, 2);
|
|
+ if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) {
|
|
+ strncpy(tmppass, password, sizeof(tmppass)-1);
|
|
+ tmppass[sizeof(tmppass)-1] = '\0';
|
|
+ password = tmppass;
|
|
+ }
|
|
+ crypted = bigcrypt(password, salt);
|
|
+ memset(tmppass, '\0', sizeof(tmppass));
|
|
+ password = NULL;
|
|
+ return crypted;
|
|
+ }
|
|
+
|
|
+ sp = stpcpy(salt, algoid);
|
|
+ if (on(UNIX_ALGO_ROUNDS, ctrl)) {
|
|
+ sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds);
|
|
+ }
|
|
+ crypt_make_salt(sp, 8);
|
|
+ /* For now be conservative so the resulting hashes
|
|
+ * are not too long. 8 bytes of salt prevents dictionary
|
|
+ * attacks well enough. */
|
|
+ return x_strdup(crypt(password, salt));
|
|
+}
|
|
+
|
|
+#ifdef WITH_SELINUX
|
|
+int
|
|
+unix_selinux_confined(void)
|
|
+{
|
|
+ static int confined = -1;
|
|
+ int fd;
|
|
+ char tempfile[]="/etc/.pwdXXXXXX";
|
|
+
|
|
+ if (confined != -1)
|
|
+ return confined;
|
|
+
|
|
+ /* cannot be confined without SELinux enabled */
|
|
+ if (!SELINUX_ENABLED){
|
|
+ confined = 0;
|
|
+ return confined;
|
|
+ }
|
|
+
|
|
+ /* let's try opening shadow read only */
|
|
+ if ((fd=open("/etc/shadow", O_RDONLY)) != -1) {
|
|
+ close(fd);
|
|
+ confined = 0;
|
|
+ return confined;
|
|
+ }
|
|
+
|
|
+ if (errno == EACCES) {
|
|
+ confined = 1;
|
|
+ return confined;
|
|
+ }
|
|
+
|
|
+ /* shadow opening failed because of other reasons let's try
|
|
+ creating a file in /etc */
|
|
+ if ((fd=mkstemp(tempfile)) != -1) {
|
|
+ unlink(tempfile);
|
|
+ close(fd);
|
|
+ confined = 0;
|
|
+ return confined;
|
|
+ }
|
|
+
|
|
+ confined = 1;
|
|
+ return confined;
|
|
+}
|
|
+
|
|
+#else
|
|
+int
|
|
+unix_selinux_confined(void)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+#ifdef USE_LCKPWDF
|
|
+int
|
|
+lock_pwdf(void)
|
|
+{
|
|
+ int i;
|
|
+ int retval;
|
|
+
|
|
+#ifndef HELPER_COMPILE
|
|
+ if (unix_selinux_confined()) {
|
|
+ return PAM_SUCCESS;
|
|
+ }
|
|
+#endif
|
|
+ /* These values for the number of attempts and the sleep time
|
|
+ are, of course, completely arbitrary.
|
|
+ My reading of the PAM docs is that, once pam_chauthtok() has been
|
|
+ called with PAM_UPDATE_AUTHTOK, we are obliged to take any
|
|
+ reasonable steps to make sure the token is updated; so retrying
|
|
+ for 1/10 sec. isn't overdoing it. */
|
|
+ i=0;
|
|
+ while((retval = lckpwdf()) != 0 && i < 100) {
|
|
+ usleep(1000);
|
|
+ i++;
|
|
+ }
|
|
+ if(retval != 0) {
|
|
+ return PAM_AUTHTOK_LOCK_BUSY;
|
|
+ }
|
|
+ return PAM_SUCCESS;
|
|
+}
|
|
+
|
|
+void
|
|
+unlock_pwdf(void)
|
|
+{
|
|
+#ifndef HELPER_COMPILE
|
|
+ if (unix_selinux_confined()) {
|
|
+ return;
|
|
+ }
|
|
+#endif
|
|
+ ulckpwdf();
|
|
+}
|
|
+#else
|
|
+int
|
|
+lock_pwdf(void)
|
|
+{
|
|
+ return PAM_SUCCESS;
|
|
+}
|
|
+
|
|
+void
|
|
+unlock_pwdf(void)
|
|
+{
|
|
+ return;
|
|
+}
|
|
+#endif
|
|
+
|
|
+int
|
|
+save_old_password(const char *forwho, const char *oldpass,
|
|
+ int howmany)
|
|
+{
|
|
+ static char buf[16384];
|
|
+ static char nbuf[16384];
|
|
+ char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
|
|
+ int npas;
|
|
+ FILE *pwfile, *opwfile;
|
|
+ int err = 0;
|
|
+ int oldmask;
|
|
+ int found = 0;
|
|
+ struct passwd *pwd = NULL;
|
|
+ struct stat st;
|
|
+ security_context_t prev_context=NULL;
|
|
+
|
|
+ if (howmany < 0) {
|
|
+ return PAM_SUCCESS;
|
|
+ }
|
|
+
|
|
+ if (oldpass == NULL) {
|
|
+ return PAM_SUCCESS;
|
|
+ }
|
|
+
|
|
+ oldmask = umask(077);
|
|
+
|
|
+#ifdef WITH_SELINUX
|
|
+ if (SELINUX_ENABLED) {
|
|
+ security_context_t passwd_context=NULL;
|
|
+ if (getfilecon("/etc/passwd",&passwd_context)<0) {
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ };
|
|
+ if (getfscreatecon(&prev_context)<0) {
|
|
+ freecon(passwd_context);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+ if (setfscreatecon(passwd_context)) {
|
|
+ freecon(passwd_context);
|
|
+ freecon(prev_context);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+ freecon(passwd_context);
|
|
+ }
|
|
+#endif
|
|
+ pwfile = fopen(OPW_TMPFILE, "w");
|
|
+ umask(oldmask);
|
|
+ if (pwfile == NULL) {
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
|
|
+ if (opwfile == NULL) {
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (fstat(fileno(opwfile), &st) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ while (fgets(buf, 16380, opwfile)) {
|
|
+ if (!strncmp(buf, forwho, strlen(forwho))) {
|
|
+ char *sptr = NULL;
|
|
+ found = 1;
|
|
+ if (howmany == 0)
|
|
+ continue;
|
|
+ buf[strlen(buf) - 1] = '\0';
|
|
+ s_luser = strtok_r(buf, ":", &sptr);
|
|
+ s_uid = strtok_r(NULL, ":", &sptr);
|
|
+ s_npas = strtok_r(NULL, ":", &sptr);
|
|
+ s_pas = strtok_r(NULL, ":", &sptr);
|
|
+ npas = strtol(s_npas, NULL, 10) + 1;
|
|
+ while (npas > howmany) {
|
|
+ s_pas = strpbrk(s_pas, ",");
|
|
+ if (s_pas != NULL)
|
|
+ s_pas++;
|
|
+ npas--;
|
|
+ }
|
|
+ pass = crypt_md5_wrapper(oldpass);
|
|
+ if (s_pas == NULL)
|
|
+ snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
|
|
+ s_luser, s_uid, npas, pass);
|
|
+ else
|
|
+ snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
|
|
+ s_luser, s_uid, npas, s_pas, pass);
|
|
+ _pam_delete(pass);
|
|
+ if (fputs(nbuf, pwfile) < 0) {
|
|
+ err = 1;
|
|
+ break;
|
|
+ }
|
|
+ } else if (fputs(buf, pwfile) < 0) {
|
|
+ err = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ fclose(opwfile);
|
|
+
|
|
+ if (!found) {
|
|
+ pwd = getpwnam(forwho);
|
|
+ if (pwd == NULL) {
|
|
+ err = 1;
|
|
+ } else {
|
|
+ pass = crypt_md5_wrapper(oldpass);
|
|
+ snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
|
|
+ forwho, (unsigned long)pwd->pw_uid, pass);
|
|
+ _pam_delete(pass);
|
|
+ if (fputs(nbuf, pwfile) < 0) {
|
|
+ err = 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (fclose(pwfile)) {
|
|
+ D(("error writing entries to old passwords file: %m"));
|
|
+ err = 1;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ if (!err) {
|
|
+ if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
|
|
+ err = 1;
|
|
+ }
|
|
+#ifdef WITH_SELINUX
|
|
+ if (SELINUX_ENABLED) {
|
|
+ if (setfscreatecon(prev_context)) {
|
|
+ err = 1;
|
|
+ }
|
|
+ if (prev_context)
|
|
+ freecon(prev_context);
|
|
+ prev_context=NULL;
|
|
+ }
|
|
+#endif
|
|
+ if (!err) {
|
|
+ return PAM_SUCCESS;
|
|
+ } else {
|
|
+ unlink(OPW_TMPFILE);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+int
|
|
+unix_update_passwd(const char *forwho, const char *towhat)
|
|
+#else
|
|
+int
|
|
+unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
|
|
+#endif
|
|
+{
|
|
+ struct passwd *tmpent = NULL;
|
|
+ struct stat st;
|
|
+ FILE *pwfile, *opwfile;
|
|
+ int err = 1;
|
|
+ int oldmask;
|
|
+ security_context_t prev_context=NULL;
|
|
+
|
|
+ oldmask = umask(077);
|
|
+#ifdef WITH_SELINUX
|
|
+ if (SELINUX_ENABLED) {
|
|
+ security_context_t passwd_context=NULL;
|
|
+ if (getfilecon("/etc/passwd",&passwd_context)<0) {
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ };
|
|
+ if (getfscreatecon(&prev_context)<0) {
|
|
+ freecon(passwd_context);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+ if (setfscreatecon(passwd_context)) {
|
|
+ freecon(passwd_context);
|
|
+ freecon(prev_context);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+ freecon(passwd_context);
|
|
+ }
|
|
+#endif
|
|
+ pwfile = fopen(PW_TMPFILE, "w");
|
|
+ umask(oldmask);
|
|
+ if (pwfile == NULL) {
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ opwfile = fopen("/etc/passwd", "r");
|
|
+ if (opwfile == NULL) {
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (fstat(fileno(opwfile), &st) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ tmpent = fgetpwent(opwfile);
|
|
+ while (tmpent) {
|
|
+ if (!strcmp(tmpent->pw_name, forwho)) {
|
|
+ /* To shut gcc up */
|
|
+ union {
|
|
+ const char *const_charp;
|
|
+ char *charp;
|
|
+ } assigned_passwd;
|
|
+ assigned_passwd.const_charp = towhat;
|
|
+
|
|
+ tmpent->pw_passwd = assigned_passwd.charp;
|
|
+ err = 0;
|
|
+ }
|
|
+ if (putpwent(tmpent, pwfile)) {
|
|
+ D(("error writing entry to password file: %m"));
|
|
+ err = 1;
|
|
+ break;
|
|
+ }
|
|
+ tmpent = fgetpwent(opwfile);
|
|
+ }
|
|
+ fclose(opwfile);
|
|
+
|
|
+ if (fclose(pwfile)) {
|
|
+ D(("error writing entries to password file: %m"));
|
|
+ err = 1;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ if (!err) {
|
|
+ if (!rename(PW_TMPFILE, "/etc/passwd"))
|
|
+#ifdef HELPER_COMPILE
|
|
+ helper_log_err(
|
|
+#else
|
|
+ pam_syslog(pamh,
|
|
+#endif
|
|
+ LOG_NOTICE, "password changed for %s", forwho);
|
|
+ else
|
|
+ err = 1;
|
|
+ }
|
|
+#ifdef WITH_SELINUX
|
|
+ if (SELINUX_ENABLED) {
|
|
+ if (setfscreatecon(prev_context)) {
|
|
+ err = 1;
|
|
+ }
|
|
+ if (prev_context)
|
|
+ freecon(prev_context);
|
|
+ prev_context=NULL;
|
|
+ }
|
|
+#endif
|
|
+ if (!err) {
|
|
+ return PAM_SUCCESS;
|
|
+ } else {
|
|
+ unlink(PW_TMPFILE);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+int
|
|
+unix_update_shadow(const char *forwho, char *towhat)
|
|
+#else
|
|
+int
|
|
+unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
|
|
+#endif
|
|
+{
|
|
+ struct spwd *spwdent = NULL, *stmpent = NULL;
|
|
+ struct stat st;
|
|
+ FILE *pwfile, *opwfile;
|
|
+ int err = 1;
|
|
+ int oldmask;
|
|
+ security_context_t prev_context=NULL;
|
|
+
|
|
+ spwdent = getspnam(forwho);
|
|
+ if (spwdent == NULL) {
|
|
+ return PAM_USER_UNKNOWN;
|
|
+ }
|
|
+ oldmask = umask(077);
|
|
+
|
|
+#ifdef WITH_SELINUX
|
|
+ if (SELINUX_ENABLED) {
|
|
+ security_context_t shadow_context=NULL;
|
|
+ if (getfilecon("/etc/shadow",&shadow_context)<0) {
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ };
|
|
+ if (getfscreatecon(&prev_context)<0) {
|
|
+ freecon(shadow_context);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+ if (setfscreatecon(shadow_context)) {
|
|
+ freecon(shadow_context);
|
|
+ freecon(prev_context);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+ freecon(shadow_context);
|
|
+ }
|
|
+#endif
|
|
+ pwfile = fopen(SH_TMPFILE, "w");
|
|
+ umask(oldmask);
|
|
+ if (pwfile == NULL) {
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ opwfile = fopen("/etc/shadow", "r");
|
|
+ if (opwfile == NULL) {
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (fstat(fileno(opwfile), &st) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
|
|
+ fclose(opwfile);
|
|
+ fclose(pwfile);
|
|
+ err = 1;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ stmpent = fgetspent(opwfile);
|
|
+ while (stmpent) {
|
|
+
|
|
+ if (!strcmp(stmpent->sp_namp, forwho)) {
|
|
+ stmpent->sp_pwdp = towhat;
|
|
+ stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
|
|
+ err = 0;
|
|
+ D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
|
|
+ }
|
|
+
|
|
+ if (putspent(stmpent, pwfile)) {
|
|
+ D(("error writing entry to shadow file: %m"));
|
|
+ err = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ stmpent = fgetspent(opwfile);
|
|
+ }
|
|
+ fclose(opwfile);
|
|
+
|
|
+ if (fclose(pwfile)) {
|
|
+ D(("error writing entries to shadow file: %m"));
|
|
+ err = 1;
|
|
+ }
|
|
+
|
|
+ done:
|
|
+ if (!err) {
|
|
+ if (!rename(SH_TMPFILE, "/etc/shadow"))
|
|
+#ifdef HELPER_COMPILE
|
|
+ helper_log_err(
|
|
+#else
|
|
+ pam_syslog(pamh,
|
|
+#endif
|
|
+ LOG_NOTICE, "password changed for %s", forwho);
|
|
+ else
|
|
+ err = 1;
|
|
+ }
|
|
+
|
|
+#ifdef WITH_SELINUX
|
|
+ if (SELINUX_ENABLED) {
|
|
+ if (setfscreatecon(prev_context)) {
|
|
+ err = 1;
|
|
+ }
|
|
+ if (prev_context)
|
|
+ freecon(prev_context);
|
|
+ prev_context=NULL;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (!err) {
|
|
+ return PAM_SUCCESS;
|
|
+ } else {
|
|
+ unlink(SH_TMPFILE);
|
|
+ return PAM_AUTHTOK_ERR;
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef HELPER_COMPILE
|
|
+
|
|
+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;
|
|
+}
|
|
+
|
|
+void
|
|
+helper_log_err(int err, const char *format, ...)
|
|
+{
|
|
+ va_list args;
|
|
+
|
|
+ va_start(args, format);
|
|
+ openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
|
|
+ vsyslog(err, format, args);
|
|
+ va_end(args);
|
|
+ closelog();
|
|
+}
|
|
+
|
|
+static void
|
|
+su_sighandler(int sig)
|
|
+{
|
|
+#ifndef SA_RESETHAND
|
|
+ /* emulate the behaviour of the SA_RESETHAND flag */
|
|
+ if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
|
|
+ signal(sig, SIG_DFL);
|
|
+#endif
|
|
+ if (sig > 0) {
|
|
+ _exit(sig);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+setup_signals(void)
|
|
+{
|
|
+ struct sigaction action; /* posix signal structure */
|
|
+
|
|
+ /*
|
|
+ * Setup signal handlers
|
|
+ */
|
|
+ (void) memset((void *) &action, 0, sizeof(action));
|
|
+ action.sa_handler = su_sighandler;
|
|
+#ifdef SA_RESETHAND
|
|
+ action.sa_flags = SA_RESETHAND;
|
|
+#endif
|
|
+ (void) sigaction(SIGILL, &action, NULL);
|
|
+ (void) sigaction(SIGTRAP, &action, NULL);
|
|
+ (void) sigaction(SIGBUS, &action, NULL);
|
|
+ (void) sigaction(SIGSEGV, &action, NULL);
|
|
+ action.sa_handler = SIG_IGN;
|
|
+ action.sa_flags = 0;
|
|
+ (void) sigaction(SIGTERM, &action, NULL);
|
|
+ (void) sigaction(SIGHUP, &action, NULL);
|
|
+ (void) sigaction(SIGINT, &action, NULL);
|
|
+ (void) sigaction(SIGQUIT, &action, NULL);
|
|
+}
|
|
+
|
|
+char *
|
|
+getuidname(uid_t uid)
|
|
+{
|
|
+ struct passwd *pw;
|
|
+ static char username[256];
|
|
+
|
|
+ pw = getpwuid(uid);
|
|
+ if (pw == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ strncpy(username, pw->pw_name, sizeof(username));
|
|
+ username[sizeof(username) - 1] = '\0';
|
|
+
|
|
+ return username;
|
|
+}
|
|
+
|
|
+int
|
|
+read_passwords(int fd, int npass, char **passwords)
|
|
+{
|
|
+ int rbytes = 0;
|
|
+ int offset = 0;
|
|
+ int i = 0;
|
|
+ char *pptr;
|
|
+ while (npass > 0) {
|
|
+ rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
|
|
+
|
|
+ if (rbytes < 0) {
|
|
+ if (errno == EINTR) continue;
|
|
+ break;
|
|
+ }
|
|
+ if (rbytes == 0)
|
|
+ break;
|
|
+
|
|
+ while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
|
|
+ != NULL) {
|
|
+ rbytes -= pptr - (passwords[i]+offset) + 1;
|
|
+ i++;
|
|
+ offset = 0;
|
|
+ npass--;
|
|
+ if (rbytes > 0) {
|
|
+ if (npass > 0)
|
|
+ memcpy(passwords[i], pptr+1, rbytes);
|
|
+ memset(pptr+1, '\0', rbytes);
|
|
+ }
|
|
+ }
|
|
+ offset += rbytes;
|
|
+ }
|
|
+
|
|
+ /* clear up */
|
|
+ if (offset > 0 && npass > 0) {
|
|
+ memset(passwords[i], '\0', offset);
|
|
+ }
|
|
+
|
|
+ return i;
|
|
+}
|
|
+
|
|
+#endif
|
|
+/* ****************************************************************** *
|
|
+ * Copyright (c) Jan Rêkorajski 1999.
|
|
+ * Copyright (c) Andrew G. Morgan 1996-8.
|
|
+ * Copyright (c) Alex O. Yuriev, 1996.
|
|
+ * Copyright (c) Cristian Gafton 1996.
|
|
+ * Copyright (c) Red Hat, Inc. 1996, 2007, 2008.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, and the entire permission notice in its entirety,
|
|
+ * including the disclaimer of warranties.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. The name of the author may not be used to endorse or promote
|
|
+ * products derived from this software without specific prior
|
|
+ * written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
|
+ * the GNU Public License, in which case the provisions of the GPL are
|
|
+ * required INSTEAD OF the above restrictions. (This clause is
|
|
+ * necessary due to a potential bad interaction between the GPL and
|
|
+ * the restrictions contained in a BSD-style copyright.)
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/support.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/support.c
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/support.c.update-helper 2007-02-06 17:06:45.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/support.c 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -26,9 +26,8 @@
|
|
#include <security/pam_ext.h>
|
|
#include <security/pam_modutil.h>
|
|
|
|
-#include "md5.h"
|
|
#include "support.h"
|
|
-#include "bigcrypt.h"
|
|
+#include "passverify.h"
|
|
#ifdef WITH_SELINUX
|
|
#include <selinux/selinux.h>
|
|
#define SELINUX_ENABLED is_selinux_enabled()>0
|
|
@@ -53,8 +52,8 @@ int _make_remark(pam_handle_t * pamh, un
|
|
* set the control flags for the UNIX module.
|
|
*/
|
|
|
|
-int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
|
|
- const char **argv)
|
|
+int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
|
|
+ int argc, const char **argv)
|
|
{
|
|
unsigned int ctrl;
|
|
|
|
@@ -110,6 +109,16 @@ int _set_ctrl(pam_handle_t *pamh, int fl
|
|
*remember = 400;
|
|
}
|
|
}
|
|
+ if (rounds != NULL) {
|
|
+ if (j == UNIX_ALGO_ROUNDS) {
|
|
+ *rounds = strtol(*argv + 7, NULL, 10);
|
|
+ if ((*rounds < 1000) || (*rounds == INT_MAX))
|
|
+ /* don't care about bogus values */
|
|
+ unset(UNIX_ALGO_ROUNDS, ctrl);
|
|
+ if (*rounds >= 10000000)
|
|
+ *rounds = 9999999;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
++argv; /* step to next argument */
|
|
@@ -377,95 +386,6 @@ int _unix_comesfromsource(pam_handle_t *
|
|
}
|
|
|
|
/*
|
|
- * _unix_blankpasswd() is a quick check for a blank password
|
|
- *
|
|
- * returns TRUE if user does not have a password
|
|
- * - to avoid prompting for one in such cases (CG)
|
|
- */
|
|
-
|
|
-int
|
|
-_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
|
|
-{
|
|
- struct passwd *pwd = NULL;
|
|
- struct spwd *spwdent = NULL;
|
|
- char *salt = NULL;
|
|
- int retval;
|
|
-
|
|
- D(("called"));
|
|
-
|
|
- /*
|
|
- * This function does not have to be too smart if something goes
|
|
- * wrong, return FALSE and let this case to be treated somewhere
|
|
- * else (CG)
|
|
- */
|
|
-
|
|
- if (on(UNIX__NONULL, ctrl))
|
|
- return 0; /* will fail but don't let on yet */
|
|
-
|
|
- /* UNIX passwords area */
|
|
-
|
|
- /* Get password file entry... */
|
|
- pwd = pam_modutil_getpwnam (pamh, name);
|
|
-
|
|
- if (pwd != NULL) {
|
|
- if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
|
|
- { /* NIS+ */
|
|
- uid_t save_euid, save_uid;
|
|
-
|
|
- save_euid = geteuid();
|
|
- save_uid = getuid();
|
|
- if (save_uid == pwd->pw_uid)
|
|
- setreuid( save_euid, save_uid );
|
|
- else {
|
|
- setreuid( 0, -1 );
|
|
- if (setreuid( -1, pwd->pw_uid ) == -1) {
|
|
- setreuid( -1, 0 );
|
|
- setreuid( 0, -1 );
|
|
- if(setreuid( -1, pwd->pw_uid ) == -1)
|
|
- /* Will fail elsewhere. */
|
|
- return 0;
|
|
- }
|
|
- }
|
|
-
|
|
- spwdent = pam_modutil_getspnam (pamh, name);
|
|
- if (save_uid == pwd->pw_uid)
|
|
- setreuid( save_uid, save_euid );
|
|
- else {
|
|
- if (setreuid( -1, 0 ) == -1)
|
|
- setreuid( save_uid, -1 );
|
|
- setreuid( -1, save_euid );
|
|
- }
|
|
- } else if (_unix_shadowed(pwd)) {
|
|
- /*
|
|
- * ...and shadow password file entry for this user,
|
|
- * if shadowing is enabled
|
|
- */
|
|
- spwdent = pam_modutil_getspnam(pamh, name);
|
|
- }
|
|
- if (spwdent)
|
|
- salt = x_strdup(spwdent->sp_pwdp);
|
|
- else
|
|
- salt = x_strdup(pwd->pw_passwd);
|
|
- }
|
|
- /* Does this user have a password? */
|
|
- if (salt == NULL) {
|
|
- retval = 0;
|
|
- } else {
|
|
- if (strlen(salt) == 0)
|
|
- retval = 1;
|
|
- else
|
|
- retval = 0;
|
|
- }
|
|
-
|
|
- /* tidy up */
|
|
-
|
|
- if (salt)
|
|
- _pam_delete(salt);
|
|
-
|
|
- return retval;
|
|
-}
|
|
-
|
|
-/*
|
|
* verify the password of a user
|
|
*/
|
|
|
|
@@ -519,7 +439,7 @@ static int _unix_run_helper_binary(pam_h
|
|
}
|
|
}
|
|
|
|
- if (SELINUX_ENABLED && geteuid() == 0) {
|
|
+ if (geteuid() == 0) {
|
|
/* must set the real uid to 0 so the helper will not error
|
|
out if pam is called from setuid binary (su, sudo...) */
|
|
setuid(0);
|
|
@@ -573,13 +493,66 @@ static int _unix_run_helper_binary(pam_h
|
|
return retval;
|
|
}
|
|
|
|
+/*
|
|
+ * _unix_blankpasswd() is a quick check for a blank password
|
|
+ *
|
|
+ * returns TRUE if user does not have a password
|
|
+ * - to avoid prompting for one in such cases (CG)
|
|
+ */
|
|
+
|
|
+int
|
|
+_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
|
|
+{
|
|
+ struct passwd *pwd = NULL;
|
|
+ char *salt = NULL;
|
|
+ int retval;
|
|
+
|
|
+ D(("called"));
|
|
+
|
|
+ /*
|
|
+ * This function does not have to be too smart if something goes
|
|
+ * wrong, return FALSE and let this case to be treated somewhere
|
|
+ * else (CG)
|
|
+ */
|
|
+
|
|
+ if (on(UNIX__NONULL, ctrl))
|
|
+ return 0; /* will fail but don't let on yet */
|
|
+
|
|
+ /* UNIX passwords area */
|
|
+
|
|
+ retval = get_pwd_hash(pamh, name, &pwd, &salt);
|
|
+
|
|
+ if (retval == PAM_UNIX_RUN_HELPER) {
|
|
+ /* salt will not be set here so we can return immediately */
|
|
+ if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
|
|
+ return 1;
|
|
+ else
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Does this user have a password? */
|
|
+ if (salt == NULL) {
|
|
+ retval = 0;
|
|
+ } else {
|
|
+ if (strlen(salt) == 0)
|
|
+ retval = 1;
|
|
+ else
|
|
+ retval = 0;
|
|
+ }
|
|
+
|
|
+ /* tidy up */
|
|
+
|
|
+ if (salt)
|
|
+ _pam_delete(salt);
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
int _unix_verify_password(pam_handle_t * pamh, const char *name
|
|
,const char *p, unsigned int ctrl)
|
|
{
|
|
struct passwd *pwd = NULL;
|
|
- struct spwd *spwdent = NULL;
|
|
char *salt = NULL;
|
|
- char *pp = NULL;
|
|
char *data_name;
|
|
int retval;
|
|
|
|
@@ -597,48 +570,7 @@ int _unix_verify_password(pam_handle_t *
|
|
|
|
D(("locating user's record"));
|
|
|
|
- /* UNIX passwords area */
|
|
- pwd = pam_modutil_getpwnam (pamh, name); /* Get password file entry... */
|
|
-
|
|
- if (pwd != NULL) {
|
|
- if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
|
|
- { /* NIS+ */
|
|
- uid_t save_euid, save_uid;
|
|
-
|
|
- save_euid = geteuid();
|
|
- save_uid = getuid();
|
|
- if (save_uid == pwd->pw_uid)
|
|
- setreuid( save_euid, save_uid );
|
|
- else {
|
|
- setreuid( 0, -1 );
|
|
- if (setreuid( -1, pwd->pw_uid ) == -1) {
|
|
- setreuid( -1, 0 );
|
|
- setreuid( 0, -1 );
|
|
- if(setreuid( -1, pwd->pw_uid ) == -1)
|
|
- return PAM_CRED_INSUFFICIENT;
|
|
- }
|
|
- }
|
|
-
|
|
- spwdent = pam_modutil_getspnam (pamh, name);
|
|
- if (save_uid == pwd->pw_uid)
|
|
- setreuid( save_uid, save_euid );
|
|
- else {
|
|
- if (setreuid( -1, 0 ) == -1)
|
|
- setreuid( save_uid, -1 );
|
|
- setreuid( -1, save_euid );
|
|
- }
|
|
- } else if (_unix_shadowed(pwd)) {
|
|
- /*
|
|
- * ...and shadow password file entry for this user,
|
|
- * if shadowing is enabled
|
|
- */
|
|
- spwdent = pam_modutil_getspnam (pamh, name);
|
|
- }
|
|
- if (spwdent)
|
|
- salt = x_strdup(spwdent->sp_pwdp);
|
|
- else
|
|
- salt = x_strdup(pwd->pw_passwd);
|
|
- }
|
|
+ retval = get_pwd_hash(pamh, name, &pwd, &salt);
|
|
|
|
data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
|
|
if (data_name == NULL) {
|
|
@@ -648,29 +580,22 @@ int _unix_verify_password(pam_handle_t *
|
|
strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
|
|
}
|
|
|
|
- retval = PAM_SUCCESS;
|
|
- if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
|
|
-
|
|
- if (pwd != NULL && (geteuid() || SELINUX_ENABLED)) {
|
|
- /* we are not root perhaps this is the reason? Run helper */
|
|
+ if (retval != PAM_SUCCESS) {
|
|
+ if (retval == PAM_UNIX_RUN_HELPER) {
|
|
D(("running helper binary"));
|
|
retval = _unix_run_helper_binary(pamh, p, ctrl, name);
|
|
} else {
|
|
D(("user's record unavailable"));
|
|
p = NULL;
|
|
- if (pwd == NULL)
|
|
- retval = PAM_USER_UNKNOWN;
|
|
- else
|
|
- retval = PAM_AUTHINFO_UNAVAIL;
|
|
if (on(UNIX_AUDIT, ctrl)) {
|
|
/* this might be a typo and the user has given a password
|
|
instead of a username. Careful with this. */
|
|
- pam_syslog(pamh, LOG_ALERT,
|
|
+ pam_syslog(pamh, LOG_WARNING,
|
|
"check pass; user (%s) unknown", name);
|
|
} else {
|
|
name = NULL;
|
|
if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
|
|
- pam_syslog(pamh, LOG_ALERT,
|
|
+ pam_syslog(pamh, LOG_WARNING,
|
|
"check pass; user unknown");
|
|
} else {
|
|
/* don't log failure as another pam module can succeed */
|
|
@@ -679,48 +604,7 @@ int _unix_verify_password(pam_handle_t *
|
|
}
|
|
}
|
|
} else {
|
|
- size_t salt_len = strlen(salt);
|
|
- if (!salt_len) {
|
|
- /* the stored password is NULL */
|
|
- if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
|
|
- D(("user has empty password - access granted"));
|
|
- retval = PAM_SUCCESS;
|
|
- } else {
|
|
- D(("user has empty password - access denied"));
|
|
- retval = PAM_AUTH_ERR;
|
|
- }
|
|
- } else if (!p || *salt == '*' || *salt == '!') {
|
|
- retval = PAM_AUTH_ERR;
|
|
- } else {
|
|
- if (!strncmp(salt, "$1$", 3)) {
|
|
- pp = Goodcrypt_md5(p, salt);
|
|
- if (pp && strcmp(pp, salt) != 0) {
|
|
- _pam_delete(pp);
|
|
- pp = Brokencrypt_md5(p, salt);
|
|
- }
|
|
- } else if (*salt != '$' && salt_len >= 13) {
|
|
- pp = bigcrypt(p, salt);
|
|
- if (pp && salt_len == 13 && strlen(pp) > salt_len) {
|
|
- _pam_overwrite(pp + salt_len);
|
|
- }
|
|
- } else {
|
|
- /*
|
|
- * Ok, we don't know the crypt algorithm, but maybe
|
|
- * libcrypt nows about it? We should try it.
|
|
- */
|
|
- pp = x_strdup (crypt(p, salt));
|
|
- }
|
|
- p = NULL; /* no longer needed here */
|
|
-
|
|
- /* the moment of truth -- do we agree with the password? */
|
|
- D(("comparing state of pp[%s] and salt[%s]", pp, salt));
|
|
-
|
|
- if (pp && strcmp(pp, salt) == 0) {
|
|
- retval = PAM_SUCCESS;
|
|
- } else {
|
|
- retval = PAM_AUTH_ERR;
|
|
- }
|
|
- }
|
|
+ retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
|
|
}
|
|
|
|
if (retval == PAM_SUCCESS) {
|
|
@@ -809,8 +693,6 @@ cleanup:
|
|
_pam_delete(data_name);
|
|
if (salt)
|
|
_pam_delete(salt);
|
|
- if (pp)
|
|
- _pam_delete(pp);
|
|
|
|
D(("done [%d].", retval));
|
|
|
|
@@ -971,26 +853,12 @@ int _unix_read_password(pam_handle_t * p
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
-int _unix_shadowed(const struct passwd *pwd)
|
|
-{
|
|
- if (pwd != NULL) {
|
|
- if (strcmp(pwd->pw_passwd, "x") == 0) {
|
|
- return 1;
|
|
- }
|
|
- if ((pwd->pw_passwd[0] == '#') &&
|
|
- (pwd->pw_passwd[1] == '#') &&
|
|
- (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
|
|
- return 1;
|
|
- }
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
/* ****************************************************************** *
|
|
* Copyright (c) Jan Rêkorajski 1999.
|
|
* Copyright (c) Andrew G. Morgan 1996-8.
|
|
* Copyright (c) Alex O. Yuriev, 1996.
|
|
* Copyright (c) Cristian Gafton 1996.
|
|
+ * Copyright (c) Red Hat, Inc. 2007.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper 2006-12-18 19:50:50.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am 2008-01-08 16:17:32.000000000 +0100
|
|
@@ -16,7 +16,9 @@ securelibdir = $(SECUREDIR)
|
|
secureconfdir = $(SCONFIGDIR)
|
|
|
|
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
|
- -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\"
|
|
+ -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \
|
|
+ -DUPDATE_HELPER=\"$(sbindir)/unix_update\" \
|
|
+ -DPATH_RANDOMDEV=\"/dev/urandom\"
|
|
|
|
if HAVE_LIBSELINUX
|
|
AM_CFLAGS += -D"WITH_SELINUX"
|
|
@@ -25,33 +27,40 @@ if HAVE_LIBCRACK
|
|
AM_CFLAGS += -D"USE_CRACKLIB"
|
|
endif
|
|
|
|
-pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module \
|
|
- @LIBCRACK@ @LIBNSL@ -L$(top_builddir)/libpam -lpam \
|
|
- @LIBCRYPT@ @LIBSELINUX@
|
|
+pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module
|
|
if HAVE_VERSIONING
|
|
pam_unix_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
|
endif
|
|
+pam_unix_la_LIBADD = @LIBCRACK@ @LIBNSL@ -L$(top_builddir)/libpam -lpam \
|
|
+ @LIBCRYPT@ @LIBSELINUX@
|
|
|
|
securelib_LTLIBRARIES = pam_unix.la
|
|
|
|
-noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h
|
|
+noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h
|
|
|
|
-sbin_PROGRAMS = unix_chkpwd
|
|
+sbin_PROGRAMS = unix_chkpwd unix_update
|
|
|
|
noinst_PROGRAMS = bigcrypt
|
|
|
|
pam_unix_la_SOURCES = bigcrypt.c pam_unix_acct.c \
|
|
pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \
|
|
- yppasswd_xdr.c md5_good.c md5_broken.c
|
|
+ passverify.c yppasswd_xdr.c md5_good.c md5_broken.c
|
|
|
|
bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c
|
|
bigcrypt_CFLAGS = $(AM_CFLAGS)
|
|
-bigcrypt_LDFLAGS = @LIBCRYPT@
|
|
+bigcrypt_LDADD = @LIBCRYPT@
|
|
|
|
-unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c
|
|
-unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
|
|
-unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ -L$(top_builddir)/libpam -lpam \
|
|
- @LIBCRYPT@ @LIBSELINUX@
|
|
+unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \
|
|
+ passverify.c
|
|
+unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\"
|
|
+unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@
|
|
+unix_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@
|
|
+
|
|
+unix_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \
|
|
+ passverify.c
|
|
+unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_update\"
|
|
+unix_update_LDFLAGS = @PIE_LDFLAGS@
|
|
+unix_update_LDADD = @LIBCRYPT@ @LIBSELINUX@
|
|
|
|
if ENABLE_REGENERATE_MAN
|
|
noinst_DATA = README
|
|
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c
|
|
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c.update-helper 2006-12-20 15:52:55.000000000 +0100
|
|
+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c 2008-01-07 16:38:50.000000000 +0100
|
|
@@ -111,7 +111,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_h
|
|
|
|
D(("called."));
|
|
|
|
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
|
|
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
|
|
|
|
/* Get a few bytes so we can pass our return value to
|
|
pam_sm_setcred(). */
|