You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
812 lines
23 KiB
812 lines
23 KiB
diff --git a/modules/pam_pwhistory/Makefile.am b/modules/pam_pwhistory/Makefile.am |
|
index 4bb4d6d..9157b91 100644 |
|
--- a/modules/pam_pwhistory/Makefile.am |
|
+++ b/modules/pam_pwhistory/Makefile.am |
|
@@ -1,5 +1,6 @@ |
|
# |
|
# Copyright (c) 2008, 2009 Thorsten Kukuk <kukuk@suse.de> |
|
+# Copyright (c) 2013 Red Hat, Inc. |
|
# |
|
|
|
CLEANFILES = *~ |
|
@@ -9,25 +10,33 @@ EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_pwhistory |
|
|
|
TESTS = tst-pam_pwhistory |
|
|
|
-man_MANS = pam_pwhistory.8 |
|
+man_MANS = pam_pwhistory.8 pwhistory_helper.8 |
|
|
|
-XMLS = README.xml pam_pwhistory.8.xml |
|
+XMLS = README.xml pam_pwhistory.8.xml pwhistory_helper.8.xml |
|
|
|
securelibdir = $(SECUREDIR) |
|
secureconfdir = $(SCONFIGDIR) |
|
|
|
-AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include |
|
-AM_LDFLAGS = -no-undefined -avoid-version -module |
|
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \ |
|
+ -DPWHISTORY_HELPER=\"$(sbindir)/pwhistory_helper\" |
|
+ |
|
+pam_pwhistory_la_LDFLAGS = -no-undefined -avoid-version -module |
|
if HAVE_VERSIONING |
|
- AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map |
|
+ pam_pwhistory_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map |
|
endif |
|
|
|
noinst_HEADERS = opasswd.h |
|
|
|
securelib_LTLIBRARIES = pam_pwhistory.la |
|
-pam_pwhistory_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@ |
|
+pam_pwhistory_la_CFLAGS = $(AM_CFLAGS) |
|
+pam_pwhistory_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@ @LIBSELINUX@ |
|
pam_pwhistory_la_SOURCES = pam_pwhistory.c opasswd.c |
|
|
|
+sbin_PROGRAMS = pwhistory_helper |
|
+pwhistory_helper_CFLAGS = $(AM_CFLAGS) -DHELPER_COMPILE=\"pwhistory_helper\" |
|
+pwhistory_helper_SOURCES = pwhistory_helper.c opasswd.c |
|
+pwhistory_helper_LDADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@ |
|
+ |
|
if ENABLE_REGENERATE_MAN |
|
noinst_DATA = README |
|
README: pam_pwhistory.8.xml |
|
diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c |
|
index 836d713..e319ff3 100644 |
|
--- a/modules/pam_pwhistory/opasswd.c |
|
+++ b/modules/pam_pwhistory/opasswd.c |
|
@@ -1,5 +1,6 @@ |
|
/* |
|
* Copyright (c) 2008 Thorsten Kukuk <kukuk@suse.de> |
|
+ * Copyright (c) 2013 Red Hat, Inc. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
@@ -38,6 +39,7 @@ |
|
#endif |
|
|
|
#include <pwd.h> |
|
+#include <shadow.h> |
|
#include <time.h> |
|
#include <ctype.h> |
|
#include <errno.h> |
|
@@ -47,6 +49,7 @@ |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <syslog.h> |
|
+#include <stdarg.h> |
|
#include <sys/stat.h> |
|
|
|
#if defined (HAVE_XCRYPT_H) |
|
@@ -55,7 +58,14 @@ |
|
#include <crypt.h> |
|
#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,...) helper_log_err(a,__VA_ARGS__) |
|
+#else |
|
+#include <security/pam_modutil.h> |
|
#include <security/pam_ext.h> |
|
+#endif |
|
#include <security/pam_modules.h> |
|
|
|
#include "opasswd.h" |
|
@@ -76,6 +86,19 @@ typedef struct { |
|
char *old_passwords; |
|
} opwd; |
|
|
|
+#ifdef HELPER_COMPILE |
|
+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(); |
|
+} |
|
+#endif |
|
|
|
static int |
|
parse_entry (char *line, opwd *data) |
|
@@ -112,8 +135,8 @@ compare_password(const char *newpass, const char *oldpass) |
|
} |
|
|
|
/* Check, if the new password is already in the opasswd file. */ |
|
-int |
|
-check_old_pass (pam_handle_t *pamh, const char *user, |
|
+PAMH_ARG_DECL(int |
|
+check_old_pass, const char *user, |
|
const char *newpass, int debug) |
|
{ |
|
int retval = PAM_SUCCESS; |
|
@@ -123,6 +146,11 @@ check_old_pass (pam_handle_t *pamh, const char *user, |
|
opwd entry; |
|
int found = 0; |
|
|
|
+#ifndef HELPER_COMPILE |
|
+ if (SELINUX_ENABLED) |
|
+ return PAM_PWHISTORY_RUN_HELPER; |
|
+#endif |
|
+ |
|
if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL) |
|
{ |
|
if (errno != ENOENT) |
|
@@ -208,9 +236,9 @@ check_old_pass (pam_handle_t *pamh, const char *user, |
|
return retval; |
|
} |
|
|
|
-int |
|
-save_old_pass (pam_handle_t *pamh, const char *user, uid_t uid, |
|
- const char *oldpass, int howmany, int debug UNUSED) |
|
+PAMH_ARG_DECL(int |
|
+save_old_pass, const char *user, |
|
+ int howmany, int debug UNUSED) |
|
{ |
|
char opasswd_tmp[] = TMP_PASSWORDS_FILE; |
|
struct stat opasswd_stat; |
|
@@ -221,10 +249,35 @@ save_old_pass (pam_handle_t *pamh, const char *user, uid_t uid, |
|
char *buf = NULL; |
|
size_t buflen = 0; |
|
int found = 0; |
|
+ struct passwd *pwd; |
|
+ const char *oldpass; |
|
+ |
|
+ pwd = pam_modutil_getpwnam (pamh, user); |
|
+ if (pwd == NULL) |
|
+ return PAM_USER_UNKNOWN; |
|
|
|
if (howmany <= 0) |
|
return PAM_SUCCESS; |
|
|
|
+#ifndef HELPER_COMPILE |
|
+ if (SELINUX_ENABLED) |
|
+ return PAM_PWHISTORY_RUN_HELPER; |
|
+#endif |
|
+ |
|
+ if ((strcmp(pwd->pw_passwd, "x") == 0) || |
|
+ ((pwd->pw_passwd[0] == '#') && |
|
+ (pwd->pw_passwd[1] == '#') && |
|
+ (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0))) |
|
+ { |
|
+ struct spwd *spw = pam_modutil_getspnam (pamh, user); |
|
+ |
|
+ if (spw == NULL) |
|
+ return PAM_USER_UNKNOWN; |
|
+ oldpass = spw->sp_pwdp; |
|
+ } |
|
+ else |
|
+ oldpass = pwd->pw_passwd; |
|
+ |
|
if (oldpass == NULL || *oldpass == '\0') |
|
return PAM_SUCCESS; |
|
|
|
@@ -447,7 +500,7 @@ save_old_pass (pam_handle_t *pamh, const char *user, uid_t uid, |
|
{ |
|
char *out; |
|
|
|
- if (asprintf (&out, "%s:%d:1:%s\n", user, uid, oldpass) < 0) |
|
+ if (asprintf (&out, "%s:%d:1:%s\n", user, pwd->pw_uid, oldpass) < 0) |
|
{ |
|
retval = PAM_AUTHTOK_ERR; |
|
if (oldpf) |
|
diff --git a/modules/pam_pwhistory/opasswd.h b/modules/pam_pwhistory/opasswd.h |
|
index db3e656..1b08699 100644 |
|
--- a/modules/pam_pwhistory/opasswd.h |
|
+++ b/modules/pam_pwhistory/opasswd.h |
|
@@ -1,5 +1,6 @@ |
|
/* |
|
* Copyright (c) 2008 Thorsten Kukuk <kukuk@suse.de> |
|
+ * Copyright (c) 2013 Red Hat, Inc. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
@@ -36,10 +37,32 @@ |
|
#ifndef __OPASSWD_H__ |
|
#define __OPASSWD_H__ |
|
|
|
-extern int check_old_pass (pam_handle_t *pamh, const char *user, |
|
- const char *newpass, int debug); |
|
-extern int save_old_pass (pam_handle_t *pamh, const char *user, |
|
- uid_t uid, const char *oldpass, |
|
- int howmany, int debug); |
|
+#define PAM_PWHISTORY_RUN_HELPER PAM_CRED_INSUFFICIENT |
|
+ |
|
+#ifdef WITH_SELINUX |
|
+#include <selinux/selinux.h> |
|
+#define SELINUX_ENABLED is_selinux_enabled()>0 |
|
+#else |
|
+#define SELINUX_ENABLED 0 |
|
+#endif |
|
+ |
|
+#ifdef HELPER_COMPILE |
|
+#define PAMH_ARG_DECL(fname, ...) fname(__VA_ARGS__) |
|
+#define PAMH_ARG(...) __VA_ARGS__ |
|
+#else |
|
+#define PAMH_ARG_DECL(fname, ...) fname(pam_handle_t *pamh, __VA_ARGS__) |
|
+#define PAMH_ARG(...) pamh, __VA_ARGS__ |
|
+#endif |
|
+ |
|
+#ifdef HELPER_COMPILE |
|
+void |
|
+helper_log_err(int err, const char *format, ...); |
|
+#endif |
|
+ |
|
+PAMH_ARG_DECL(int |
|
+check_old_pass, const char *user, const char *newpass, int debug); |
|
+ |
|
+PAMH_ARG_DECL(int |
|
+save_old_pass, const char *user, int howmany, int debug); |
|
|
|
#endif /* __OPASSWD_H__ */ |
|
diff --git a/modules/pam_pwhistory/pam_pwhistory.c b/modules/pam_pwhistory/pam_pwhistory.c |
|
index 654edd3..d6c5c47 100644 |
|
--- a/modules/pam_pwhistory/pam_pwhistory.c |
|
+++ b/modules/pam_pwhistory/pam_pwhistory.c |
|
@@ -1,6 +1,7 @@ |
|
/* |
|
* Copyright (c) 2008, 2012 Thorsten Kukuk |
|
* Author: Thorsten Kukuk <kukuk@thkukuk.de> |
|
+ * Copyright (c) 2013 Red Hat, Inc. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
@@ -46,10 +47,14 @@ |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
-#include <shadow.h> |
|
#include <syslog.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
+#include <sys/time.h> |
|
+#include <sys/resource.h> |
|
+#include <sys/wait.h> |
|
+#include <signal.h> |
|
+#include <fcntl.h> |
|
|
|
#include <security/pam_modules.h> |
|
#include <security/pam_modutil.h> |
|
@@ -59,6 +64,7 @@ |
|
#include "opasswd.h" |
|
|
|
#define DEFAULT_BUFLEN 2048 |
|
+#define MAX_FD_NO 20000 |
|
|
|
struct options_t { |
|
int debug; |
|
@@ -102,6 +108,184 @@ parse_option (pam_handle_t *pamh, const char *argv, options_t *options) |
|
pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv); |
|
} |
|
|
|
+static int |
|
+run_save_helper(pam_handle_t *pamh, const char *user, |
|
+ int howmany, int debug) |
|
+{ |
|
+ int retval, child; |
|
+ struct sigaction newsa, oldsa; |
|
+ |
|
+ memset(&newsa, '\0', sizeof(newsa)); |
|
+ newsa.sa_handler = SIG_DFL; |
|
+ sigaction(SIGCHLD, &newsa, &oldsa); |
|
+ |
|
+ child = fork(); |
|
+ if (child == 0) |
|
+ { |
|
+ int i = 0; |
|
+ struct rlimit rlim; |
|
+ int dummyfds[2]; |
|
+ static char *envp[] = { NULL }; |
|
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; |
|
+ |
|
+ /* replace std file descriptors with a dummy pipe */ |
|
+ if (pipe2(dummyfds, O_NONBLOCK) == 0) |
|
+ { |
|
+ dup2(dummyfds[0], STDIN_FILENO); |
|
+ dup2(dummyfds[1], STDOUT_FILENO); |
|
+ dup2(dummyfds[1], STDERR_FILENO); |
|
+ } |
|
+ |
|
+ if (getrlimit(RLIMIT_NOFILE,&rlim) == 0) |
|
+ { |
|
+ if (rlim.rlim_max >= MAX_FD_NO) |
|
+ rlim.rlim_max = MAX_FD_NO; |
|
+ for (i = STDERR_FILENO + 1; i < (int)rlim.rlim_max; i++) |
|
+ { |
|
+ if (i != dummyfds[0]) |
|
+ close(i); |
|
+ } |
|
+ } |
|
+ |
|
+ /* exec binary helper */ |
|
+ args[0] = strdup(PWHISTORY_HELPER); |
|
+ args[1] = strdup("save"); |
|
+ args[2] = x_strdup(user); |
|
+ asprintf(&args[3], "%d", howmany); |
|
+ asprintf(&args[4], "%d", debug); |
|
+ |
|
+ execve(args[0], args, envp); |
|
+ |
|
+ _exit(PAM_SYSTEM_ERR); |
|
+ } |
|
+ else if (child > 0) |
|
+ { |
|
+ /* wait for child */ |
|
+ int rc = 0; |
|
+ rc = waitpid(child, &retval, 0); /* wait for helper to complete */ |
|
+ if (rc < 0) |
|
+ { |
|
+ pam_syslog(pamh, LOG_ERR, "pwhistory_helper save waitpid returned %d: %m", rc); |
|
+ retval = PAM_SYSTEM_ERR; |
|
+ } |
|
+ else if (!WIFEXITED(retval)) |
|
+ { |
|
+ pam_syslog(pamh, LOG_ERR, "pwhistory_helper save abnormal exit: %d", retval); |
|
+ retval = PAM_SYSTEM_ERR; |
|
+ } |
|
+ else |
|
+ { |
|
+ retval = WEXITSTATUS(retval); |
|
+ } |
|
+ } |
|
+ else |
|
+ { |
|
+ retval = PAM_SYSTEM_ERR; |
|
+ } |
|
+ |
|
+ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ |
|
+ |
|
+ return retval; |
|
+} |
|
+ |
|
+static int |
|
+run_check_helper(pam_handle_t *pamh, const char *user, |
|
+ const char *newpass, int debug) |
|
+{ |
|
+ int retval, child, fds[2]; |
|
+ struct sigaction newsa, oldsa; |
|
+ |
|
+ /* create a pipe for the password */ |
|
+ if (pipe(fds) != 0) |
|
+ return PAM_SYSTEM_ERR; |
|
+ |
|
+ memset(&newsa, '\0', sizeof(newsa)); |
|
+ newsa.sa_handler = SIG_DFL; |
|
+ sigaction(SIGCHLD, &newsa, &oldsa); |
|
+ |
|
+ child = fork(); |
|
+ if (child == 0) |
|
+ { |
|
+ int i = 0; |
|
+ struct rlimit rlim; |
|
+ int dummyfds[2]; |
|
+ static char *envp[] = { NULL }; |
|
+ char *args[] = { NULL, NULL, NULL, NULL, NULL }; |
|
+ |
|
+ /* reopen stdin as pipe */ |
|
+ dup2(fds[0], STDIN_FILENO); |
|
+ |
|
+ /* replace std file descriptors with a dummy pipe */ |
|
+ if (pipe2(dummyfds, O_NONBLOCK) == 0) |
|
+ { |
|
+ dup2(dummyfds[1], STDOUT_FILENO); |
|
+ dup2(dummyfds[1], STDERR_FILENO); |
|
+ } |
|
+ |
|
+ if (getrlimit(RLIMIT_NOFILE,&rlim) == 0) |
|
+ { |
|
+ if (rlim.rlim_max >= MAX_FD_NO) |
|
+ rlim.rlim_max = MAX_FD_NO; |
|
+ for (i = STDERR_FILENO + 1; i < (int)rlim.rlim_max; i++) |
|
+ { |
|
+ if (i != dummyfds[0]) |
|
+ close(i); |
|
+ } |
|
+ } |
|
+ |
|
+ /* exec binary helper */ |
|
+ args[0] = strdup(PWHISTORY_HELPER); |
|
+ args[1] = strdup("check"); |
|
+ args[2] = x_strdup(user); |
|
+ asprintf(&args[3], "%d", debug); |
|
+ |
|
+ execve(args[0], args, envp); |
|
+ |
|
+ _exit(PAM_SYSTEM_ERR); |
|
+ } |
|
+ else if (child > 0) |
|
+ { |
|
+ /* wait for child */ |
|
+ int rc = 0; |
|
+ if (newpass == NULL) |
|
+ newpass = ""; |
|
+ |
|
+ /* send the password to the child */ |
|
+ if (write(fds[1], newpass, strlen(newpass)+1) == -1) |
|
+ { |
|
+ pam_syslog(pamh, LOG_ERR, "Cannot send password to helper: %m"); |
|
+ retval = PAM_SYSTEM_ERR; |
|
+ } |
|
+ newpass = NULL; |
|
+ close(fds[0]); /* close here to avoid possible SIGPIPE above */ |
|
+ close(fds[1]); |
|
+ rc = waitpid(child, &retval, 0); /* wait for helper to complete */ |
|
+ if (rc < 0) |
|
+ { |
|
+ pam_syslog(pamh, LOG_ERR, "pwhistory_helper check waitpid returned %d: %m", rc); |
|
+ retval = PAM_SYSTEM_ERR; |
|
+ } |
|
+ else if (!WIFEXITED(retval)) |
|
+ { |
|
+ pam_syslog(pamh, LOG_ERR, "pwhistory_helper check abnormal exit: %d", retval); |
|
+ retval = PAM_SYSTEM_ERR; |
|
+ } |
|
+ else |
|
+ { |
|
+ retval = WEXITSTATUS(retval); |
|
+ } |
|
+ } |
|
+ else |
|
+ { |
|
+ close(fds[0]); |
|
+ close(fds[1]); |
|
+ retval = PAM_SYSTEM_ERR; |
|
+ } |
|
+ |
|
+ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ |
|
+ |
|
+ return retval; |
|
+} |
|
|
|
/* This module saves the current crypted password in /etc/security/opasswd |
|
and then compares the new password with all entries in this file. */ |
|
@@ -109,7 +293,6 @@ parse_option (pam_handle_t *pamh, const char *argv, options_t *options) |
|
PAM_EXTERN int |
|
pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv) |
|
{ |
|
- struct passwd *pwd; |
|
const char *newpass; |
|
const char *user; |
|
int retval, tries; |
|
@@ -154,31 +337,13 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv) |
|
return PAM_SUCCESS; |
|
} |
|
|
|
- pwd = pam_modutil_getpwnam (pamh, user); |
|
- if (pwd == NULL) |
|
- return PAM_USER_UNKNOWN; |
|
+ retval = save_old_pass (pamh, user, options.remember, options.debug); |
|
|
|
- if ((strcmp(pwd->pw_passwd, "x") == 0) || |
|
- ((pwd->pw_passwd[0] == '#') && |
|
- (pwd->pw_passwd[1] == '#') && |
|
- (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0))) |
|
- { |
|
- struct spwd *spw = pam_modutil_getspnam (pamh, user); |
|
- if (spw == NULL) |
|
- return PAM_USER_UNKNOWN; |
|
+ if (retval == PAM_PWHISTORY_RUN_HELPER) |
|
+ retval = run_save_helper(pamh, user, options.remember, options.debug); |
|
|
|
- retval = save_old_pass (pamh, user, pwd->pw_uid, spw->sp_pwdp, |
|
- options.remember, options.debug); |
|
- if (retval != PAM_SUCCESS) |
|
- return retval; |
|
- } |
|
- else |
|
- { |
|
- retval = save_old_pass (pamh, user, pwd->pw_uid, pwd->pw_passwd, |
|
- options.remember, options.debug); |
|
- if (retval != PAM_SUCCESS) |
|
- return retval; |
|
- } |
|
+ if (retval != PAM_SUCCESS) |
|
+ return retval; |
|
|
|
newpass = NULL; |
|
tries = 0; |
|
@@ -207,8 +372,11 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv) |
|
if (options.debug) |
|
pam_syslog (pamh, LOG_DEBUG, "check against old password file"); |
|
|
|
- if (check_old_pass (pamh, user, newpass, |
|
- options.debug) != PAM_SUCCESS) |
|
+ retval = check_old_pass (pamh, user, newpass, options.debug); |
|
+ if (retval == PAM_PWHISTORY_RUN_HELPER) |
|
+ retval = run_check_helper(pamh, user, newpass, options.debug); |
|
+ |
|
+ if (retval != PAM_SUCCESS) |
|
{ |
|
if (getuid() || options.enforce_for_root || |
|
(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) |
|
diff --git a/modules/pam_pwhistory/pwhistory_helper.8.xml b/modules/pam_pwhistory/pwhistory_helper.8.xml |
|
new file mode 100644 |
|
index 0000000..a030176 |
|
--- /dev/null |
|
+++ b/modules/pam_pwhistory/pwhistory_helper.8.xml |
|
@@ -0,0 +1,68 @@ |
|
+<?xml version="1.0" encoding='UTF-8'?> |
|
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" |
|
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"> |
|
+ |
|
+<refentry id="pwhistory_helper"> |
|
+ |
|
+ <refmeta> |
|
+ <refentrytitle>pwhistory_helper</refentrytitle> |
|
+ <manvolnum>8</manvolnum> |
|
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo> |
|
+ </refmeta> |
|
+ |
|
+ <refnamediv id="pwhistory_helper-name"> |
|
+ <refname>pwhistory_helper</refname> |
|
+ <refpurpose>Helper binary that transfers password hashes from passwd or shadow to opasswd</refpurpose> |
|
+ </refnamediv> |
|
+ |
|
+ <refsynopsisdiv> |
|
+ <cmdsynopsis id="pwhistory_helper-cmdsynopsis"> |
|
+ <command>pwhistory_helper</command> |
|
+ <arg choice="opt"> |
|
+ ... |
|
+ </arg> |
|
+ </cmdsynopsis> |
|
+ </refsynopsisdiv> |
|
+ |
|
+ <refsect1 id="pwhistory_helper-description"> |
|
+ |
|
+ <title>DESCRIPTION</title> |
|
+ |
|
+ <para> |
|
+ <emphasis>pwhistory_helper</emphasis> is a helper program for the |
|
+ <emphasis>pam_pwhistory</emphasis> module that transfers password hashes |
|
+ from passwd or shadow file to the opasswd file and checks a password |
|
+ supplied by user against the existing hashes in the opasswd file. |
|
+ </para> |
|
+ |
|
+ <para> |
|
+ The purpose of the helper is to enable tighter confinement of |
|
+ login and password changing services. The helper is thus called only |
|
+ when SELinux is enabled on the system. |
|
+ </para> |
|
+ |
|
+ <para> |
|
+ The interface of the helper - command line options, and input/output |
|
+ data format are internal to the <emphasis>pam_pwhistory</emphasis> |
|
+ module and it should not be called directly from applications. |
|
+ </para> |
|
+ </refsect1> |
|
+ |
|
+ <refsect1 id='pwhistory_helper-see_also'> |
|
+ <title>SEE ALSO</title> |
|
+ <para> |
|
+ <citerefentry> |
|
+ <refentrytitle>pam_pwhistory</refentrytitle><manvolnum>8</manvolnum> |
|
+ </citerefentry> |
|
+ </para> |
|
+ </refsect1> |
|
+ |
|
+ <refsect1 id='pwhistory_helper-author'> |
|
+ <title>AUTHOR</title> |
|
+ <para> |
|
+ Written by Tomas Mraz based on the code originally in |
|
+ <emphasis>pam_pwhistory and pam_unix</emphasis> modules. |
|
+ </para> |
|
+ </refsect1> |
|
+ |
|
+</refentry> |
|
diff --git a/modules/pam_pwhistory/pwhistory_helper.c b/modules/pam_pwhistory/pwhistory_helper.c |
|
new file mode 100644 |
|
index 0000000..b07ab81 |
|
--- /dev/null |
|
+++ b/modules/pam_pwhistory/pwhistory_helper.c |
|
@@ -0,0 +1,209 @@ |
|
+/* |
|
+ * Copyright (c) 2013 Red Hat, Inc. |
|
+ * Author: Tomas Mraz <tmraz@redhat.com> |
|
+ * |
|
+ * 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. |
|
+ */ |
|
+ |
|
+#include "config.h" |
|
+ |
|
+#include <stdio.h> |
|
+#include <stdlib.h> |
|
+#include <string.h> |
|
+#include <syslog.h> |
|
+#include <errno.h> |
|
+#include <unistd.h> |
|
+#include <signal.h> |
|
+#include <security/_pam_types.h> |
|
+#include <security/_pam_macros.h> |
|
+#include "opasswd.h" |
|
+ |
|
+#define MAXPASS 200 |
|
+ |
|
+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 ) { |
|
+ struct sigaction sa; |
|
+ memset(&sa, '\0', sizeof(sa)); |
|
+ sa.sa_handler = SIG_DFL; |
|
+ sigaction(sig, &sa, NULL); |
|
+ } |
|
+#endif |
|
+ if (sig > 0) { |
|
+ _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); |
|
+} |
|
+ |
|
+static 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; |
|
+} |
|
+ |
|
+ |
|
+static int |
|
+check_history(const char *user, const char *debug) |
|
+{ |
|
+ char pass[MAXPASS + 1]; |
|
+ char *passwords[] = { pass }; |
|
+ int npass; |
|
+ int dbg = atoi(debug); /* no need to be too fancy here */ |
|
+ int retval; |
|
+ |
|
+ /* read the password from stdin (a pipe from the pam_pwhistory module) */ |
|
+ npass = read_passwords(STDIN_FILENO, 1, passwords); |
|
+ |
|
+ if (npass != 1) |
|
+ { /* is it a valid password? */ |
|
+ helper_log_err(LOG_DEBUG, "no password supplied"); |
|
+ return PAM_AUTHTOK_ERR; |
|
+ } |
|
+ |
|
+ retval = check_old_pass(user, pass, dbg); |
|
+ |
|
+ memset(pass, '\0', MAXPASS); /* clear memory of the password */ |
|
+ |
|
+ return retval; |
|
+} |
|
+ |
|
+static int |
|
+save_history(const char *user, const char *howmany, const char *debug) |
|
+{ |
|
+ int num = atoi(howmany); |
|
+ int dbg = atoi(debug); /* no need to be too fancy here */ |
|
+ int retval; |
|
+ |
|
+ retval = save_old_pass(user, num, dbg); |
|
+ |
|
+ return retval; |
|
+} |
|
+ |
|
+int |
|
+main(int argc, char *argv[]) |
|
+{ |
|
+ const char *option; |
|
+ const char *user; |
|
+ |
|
+ /* |
|
+ * 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. |
|
+ */ |
|
+ |
|
+ if (isatty(STDIN_FILENO) || argc < 4) |
|
+ { |
|
+ fprintf(stderr, |
|
+ "This binary is not designed for running in this way.\n"); |
|
+ sleep(10); /* this should discourage/annoy the user */ |
|
+ return PAM_SYSTEM_ERR; |
|
+ } |
|
+ |
|
+ option = argv[1]; |
|
+ user = argv[2]; |
|
+ |
|
+ if (strcmp(option, "check") == 0 && argc == 4) |
|
+ return check_history(user, argv[3]); |
|
+ else if (strcmp(option, "save") == 0 && argc == 5) |
|
+ return save_history(user, argv[3], argv[4]); |
|
+ |
|
+ return PAM_SYSTEM_ERR; |
|
+} |
|
+
|
|
|