aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_unix/pam_unix.c~')
-rw-r--r--modules/pam_unix/pam_unix.c~150
1 files changed, 146 insertions, 4 deletions
diff --git a/modules/pam_unix/pam_unix.c~ b/modules/pam_unix/pam_unix.c~
index 72dbac0..06f335c 100644
--- a/modules/pam_unix/pam_unix.c~
+++ b/modules/pam_unix/pam_unix.c~
@@ -23,6 +23,7 @@
#define PASSWORD_HASH "md5"
+#define MAX_RETRIES 3
#define DEFAULT_WARN (2L * 7L * 86400L) /* two weeks */
#define SALTSIZE 32
@@ -264,8 +265,11 @@ pam_sm_chautok(pam_handle_t *pamh, int flags,
*/
struct passwd *new_pwd, *old_pwd;
const char *user, *old_pass, *new_pass;
- char *hashedpwd;
- int pam_err;
+ char *hashedpwd ;
+#ifndef __linux__
+ login_cap_t * lc;
+#endif
+ int pam_err, retries;
/* identify user */
@@ -307,7 +311,7 @@ pam_sm_chautok(pam_handle_t *pamh, int flags,
if (flags & PAM_PRELIM_CHECK) {
- PAM_LOG("PRELIM round");
+ PAM_LOG("Doing preliminary actions.");
if (getuid() == 0 ) {
/* root doesn't need old passwd */
@@ -322,15 +326,153 @@ pam_sm_chautok(pam_handle_t *pamh, int flags,
* ask for a password.
*/
old_pass = "";
+ } else {
+ pam_err = pam_get_authtok(pamh,PAM_OLDAUTHTOK,
+ &old_pass, NULL);
+ if (pam_err != PAM_SUCCESS )
+ return (pam_err);
+
+ }
+
+ PAM_LOG("Got old token for user [%s].",user);
+
+ hashedpwd = crypt(old_pass, old_pwd->pw_passwd);
+
+ if (old_pass[0] == '\0' && !openpam_get_option(pamh, PAM_OPT_NULLOK))
+ return (PAM_PERM_DENIED);
+
+ if (strcmp(hashedpwd, old_pwd->pw_passwd) != 0)
+ return (PAM_PERM_DENIED);
+
+ } else if ( flags & PAM_UPDATE_AUTHTOK ) {
+
+ PAM_LOG("Doing actual update.");
+
+ pam_err= pam_get_authtok(pamh, PAM_OLDAUTHTOK ,&old_pass, NULL);
+
+ if (pam_err != PAM_SUCCESS)
+ return (pam_err);
+
+ PAM_LOG("Got old password");
+
+ retries = 0;
+ pam_err = PAM_AUTHTOK_ERR;
+
+ while ((pam_err != PAM_SUCCESS) && ( retries++ < MAX_RETRIES)) {
+
+ pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
+ &new_pass, NULL);
+
+ pam_error(pamh, "Unable to get new passwd. Please \
+ try again");
+
+ }
+
+ if (pam_err != PAM_SUCCESS) {
+ PAM_ERROR("Unable to get new password!");
+ return (pam_err);
}
+ PAM_LOG("Got new password");
+
+ /*
+ * checking has to be done (?) for the new passwd to
+ * verify it's not weak.
+ */
+
+ if (getuid() != 0 && new_pass[0] == '\0' &&
+ !openpam_get_option(pamh, PAM_OPT_NULLOK))
+ return (PAM_PERM_DENIED);
+
+
+
+
+#ifndef __linux__
+
+ /*
+ * The BSD way to update the passwd entry. Taken as is
+ * from the freebsd-lib module pam_unix. Unfortunately,
+ * the following won't work under Linux.
+ */
+
+ if ((new_pwd = pw_dup(old_pwd)) == NULL)
+ return (PAM_BUF_ERR);
+
+ new_pwd->pw_change = 0;
+ lc = login_getclass(new_pwd->pw_class);
+ if (login_setcryptfmt(lc, password_hash, NULL) == NULL)
+ openpam_log(PAM_LOG_ERROR,
+ "can't set password cipher, relying on default");
+
+ login_close(lc);
+ makesalt(salt);
+ new_pwd->pw_passwd = crypt(new_pass, salt);
+
+
+ pam_err = PAM_SERVICE_ERR;
+ if (pw_init(NULL, NULL))
+ openpam_log(PAM_LOG_ERROR, "pw_init() failed");
+ else if ((pfd = pw_lock()) == -1)
+ openpam_log(PAM_LOG_ERROR, "pw_lock() failed");
+ else if ((tfd = pw_tmp(-1)) == -1)
+ openpam_log(PAM_LOG_ERROR, "pw_tmp() failed");
+ else if (pw_copy(pfd, tfd, new_pwd, old_pwd) == -1)
+ openpam_log(PAM_LOG_ERROR, "pw_copy() failed");
+ else if (pw_mkdb(new_pwd->pw_name) == -1)
+ openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed");
+ else
+ pam_err = PAM_SUCCESS;
+ pw_fini();
+
+ free(old_pwd);
+#endif
+
+ /* Update shadow/passwd entries for Linux */
+
+
+ } else {
+ pam_err = PAM_ABORT;
+ PAM_ERROR("Unrecognized flags.");
+ return (pam_err);
+ }
+
-
return (PAM_SUCCESS);
}
+/*
+ * Mostly stolen from freebsd-lib's pam_unix module which was mostly
+ * stolen from passwd(1)'s local_passwd.c
+ *
+ * Good ideas are meant to be reused ;)
+ */
+
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *s, long v, int n) {
+ while (--n >= 0) {
+ *s++ = itoa64[v&0x3f];
+ v >>= 6;
+ }
+}
+
+/* Salt suitable for traditional DES and MD5 */
+void
+makesalt(char salt[SALTSIZE]) {
+ int i;
+ /* These are not really random numbers, they are just
+ * numbers that change to thwart construction of a
+ * dictionary. This is exposed to the public.
+ */
+
+ for (i = 0; i < SALTSIZE; i += 4)
+ to64(&salt[i], arc4random(), 4);
+ salt[SALTSIZE] = '\0';
+}
+
PAM_MODULE_ENTRY("pam_unix")