diff --git a/backport-Move-read_passwords-function-from-pam_unix-to-pam_inline.h.patch b/backport-Move-read_passwords-function-from-pam_unix-to-pam_inline.h.patch new file mode 100644 index 0000000..690926f --- /dev/null +++ b/backport-Move-read_passwords-function-from-pam_unix-to-pam_inline.h.patch @@ -0,0 +1,202 @@ +From 48f44125fac8873237ade9e94942f82a8e6d6e1d Mon Sep 17 00:00:00 2001 +From: ikerexxe +Date: Wed, 15 Jul 2020 09:45:12 +0200 +Subject: [PATCH] Move read_passwords function from pam_unix to pam_inline.h + +[ldv: rewrote commit message] + +* modules/pam_unix/passverify.h (read_passwords): Remove prototype. +* modules/pam_unix/passverify.c (read_passwords): Move ... +* libpam/include/pam_inline.h: ... here, rename to pam_read_passwords, +add static inline qualifiers. +Include and . +* modules/pam_unix/unix_chkpwd.c: Include "pam_inline.h". +(main): Replace read_passwords with pam_read_passwords. +* modules/pam_unix/unix_update.c: Include "pam_inline.h". +(set_password): Replace read_passwords with pam_read_passwords. +--- + libpam/include/pam_inline.h | 50 ++++++++++++++++++++++++++++++++++++++++++ + modules/pam_unix/passverify.c | 43 ------------------------------------ + modules/pam_unix/passverify.h | 2 -- + modules/pam_unix/unix_chkpwd.c | 3 ++- + modules/pam_unix/unix_update.c | 3 ++- + 5 files changed, 54 insertions(+), 47 deletions(-) + +diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h +index ec05fe4..8040b86 100644 +--- a/libpam/include/pam_inline.h ++++ b/libpam/include/pam_inline.h +@@ -10,6 +10,8 @@ + + #include "pam_cc_compat.h" + #include ++#include ++#include + + /* + * Evaluates to +@@ -64,4 +66,52 @@ pam_str_skip_icase_prefix_len(const char *str, const char *prefix, size_t prefix + #define pam_str_skip_icase_prefix(str_, prefix_) \ + pam_str_skip_icase_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_)) + ++static inline int ++pam_read_passwords(int fd, int npass, char **passwords) ++{ ++ /* ++ * The passwords array must contain npass preallocated ++ * buffers of length PAM_MAX_RESP_SIZE + 1. ++ */ ++ int rbytes = 0; ++ int offset = 0; ++ int i = 0; ++ char *pptr; ++ while (npass > 0) { ++ rbytes = read(fd, passwords[i]+offset, PAM_MAX_RESP_SIZE+1-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 /* PAM_INLINE_H */ +diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c +index a571b4f..4094b31 100644 +--- a/modules/pam_unix/passverify.c ++++ b/modules/pam_unix/passverify.c +@@ -1180,49 +1180,6 @@ getuidname(uid_t uid) + return username; + } + +-int +-read_passwords(int fd, int npass, char **passwords) +-{ +- /* The passwords array must contain npass preallocated +- * buffers of length MAXPASS + 1 +- */ +- int rbytes = 0; +- int offset = 0; +- int i = 0; +- char *pptr; +- while (npass > 0) { +- rbytes = read(fd, passwords[i]+offset, MAXPASS+1-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. +diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h +index e9a88fb..abd3f3e 100644 +--- a/modules/pam_unix/passverify.h ++++ b/modules/pam_unix/passverify.h +@@ -50,8 +50,6 @@ setup_signals(void); + char * + getuidname(uid_t uid); + +-int +-read_passwords(int fd, int npass, char **passwords); + #endif + + #ifdef HELPER_COMPILE +diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c +index 4384deb..7d0718c 100644 +--- a/modules/pam_unix/unix_chkpwd.c ++++ b/modules/pam_unix/unix_chkpwd.c +@@ -33,6 +33,7 @@ + #include + + #include "passverify.h" ++#include "pam_inline.h" + + static int _check_expiry(const char *uname) + { +@@ -162,7 +163,7 @@ int main(int argc, char *argv[]) + } + /* read the password from stdin (a pipe from the pam_unix module) */ + +- npass = read_passwords(STDIN_FILENO, 1, passwords); ++ npass = pam_read_passwords(STDIN_FILENO, 1, passwords); + + if (npass != 1) { /* is it a valid password? */ + helper_log_err(LOG_DEBUG, "no password supplied"); +diff --git a/modules/pam_unix/unix_update.c b/modules/pam_unix/unix_update.c +index 6ea7ea5..b6236cc 100644 +--- a/modules/pam_unix/unix_update.c ++++ b/modules/pam_unix/unix_update.c +@@ -32,6 +32,7 @@ + #include + + #include "passverify.h" ++#include "pam_inline.h" + + static int + set_password(const char *forwho, const char *shadow, const char *remember) +@@ -49,7 +50,7 @@ set_password(const char *forwho, const char *shadow, const char *remember) + + /* read the password from stdin (a pipe from the pam_unix module) */ + +- npass = read_passwords(STDIN_FILENO, 2, passwords); ++ npass = pam_read_passwords(STDIN_FILENO, 2, passwords); + + if (npass != 2) { /* is it a valid password? */ + if (npass == 1) { +-- +1.8.3.1 + diff --git a/backport-add-helper-to-handle-SELinux.patch b/backport-add-helper-to-handle-SELinux.patch new file mode 100644 index 0000000..13b166b --- /dev/null +++ b/backport-add-helper-to-handle-SELinux.patch @@ -0,0 +1,751 @@ +From f787845843da96cc29ea1f864e29fb17379b36b7 Mon Sep 17 00:00:00 2001 +From: ikerexxe +Date: Wed, 15 Jul 2020 09:54:45 +0200 +Subject: [PATCH] pam_pwhistory: add helper to handle SELinux + +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. + +Resolves: https://github.com/linux-pam/linux-pam/pull/247 +--- + modules/pam_pwhistory/.gitignore | 1 + + modules/pam_pwhistory/Makefile.am | 21 +- + modules/pam_pwhistory/opasswd.c | 67 +++++- + modules/pam_pwhistory/opasswd.h | 31 ++- + modules/pam_pwhistory/pam_pwhistory.c | 218 ++++++++++++++++--- + modules/pam_pwhistory/pwhistory_helper.8.xml | 68 ++++++ + modules/pam_pwhistory/pwhistory_helper.c | 119 ++++++++++ + 7 files changed, 478 insertions(+), 47 deletions(-) + create mode 100644 modules/pam_pwhistory/.gitignore + create mode 100644 modules/pam_pwhistory/pwhistory_helper.8.xml + create mode 100644 modules/pam_pwhistory/pwhistory_helper.c + +diff --git a/modules/pam_pwhistory/.gitignore b/modules/pam_pwhistory/.gitignore +new file mode 100644 +index 00000000..358515af +--- /dev/null ++++ b/modules/pam_pwhistory/.gitignore +@@ -0,0 +1 @@ ++pwhistory_helper +diff --git a/modules/pam_pwhistory/Makefile.am b/modules/pam_pwhistory/Makefile.am +index bd9f1ea9..32b9fcc4 100644 +--- a/modules/pam_pwhistory/Makefile.am ++++ b/modules/pam_pwhistory/Makefile.am +@@ -1,5 +1,6 @@ + # + # Copyright (c) 2008, 2009 Thorsten Kukuk ++# Copyright (c) 2013 Red Hat, Inc. + # + + CLEANFILES = *~ +@@ -8,9 +9,9 @@ MAINTAINERCLEANFILES = $(MANS) README + EXTRA_DIST = $(XMLS) + + if HAVE_DOC +-dist_man_MANS = pam_pwhistory.8 ++dist_man_MANS = pam_pwhistory.8 pwhistory_helper.8 + endif +-XMLS = README.xml pam_pwhistory.8.xml ++XMLS = README.xml pam_pwhistory.8.xml pwhistory_helper.8.xml + dist_check_SCRIPTS = tst-pam_pwhistory + TESTS = $(dist_check_SCRIPTS) + +@@ -18,18 +19,26 @@ securelibdir = $(SECUREDIR) + secureconfdir = $(SCONFIGDIR) + + AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \ +- $(WARN_CFLAGS) +-AM_LDFLAGS = -no-undefined -avoid-version -module ++ $(WARN_CFLAGS) -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\" @PIE_CFLAGS@ ++pwhistory_helper_SOURCES = pwhistory_helper.c opasswd.c ++pwhistory_helper_LDFLAGS = @PIE_LDFLAGS@ ++pwhistory_helper_LDADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@ ++ + if ENABLE_REGENERATE_MAN + dist_noinst_DATA = README + -include $(top_srcdir)/Make.xml.rules +diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c +index 77142f2c..ac10f691 100644 +--- a/modules/pam_pwhistory/opasswd.c ++++ b/modules/pam_pwhistory/opasswd.c +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2008 Thorsten Kukuk ++ * 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 ++#include + #include + #include + #include +@@ -47,6 +49,9 @@ + #include + #include + #include ++#ifdef HELPER_COMPILE ++#include ++#endif + #include + + #if defined HAVE_LIBXCRYPT +@@ -55,7 +60,14 @@ + #include + #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 + #include ++#endif + #include + + #include "opasswd.h" +@@ -76,6 +88,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) +@@ -117,9 +142,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, +- const char *newpass, int debug) ++PAMH_ARG_DECL(int ++check_old_pass, const char *user, const char *newpass, int debug) + { + int retval = PAM_SUCCESS; + FILE *oldpf; +@@ -128,6 +152,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) +@@ -213,9 +242,8 @@ 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; +@@ -226,10 +254,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; + +@@ -452,7 +505,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 db3e6568..3f257288 100644 +--- a/modules/pam_pwhistory/opasswd.h ++++ b/modules/pam_pwhistory/opasswd.h +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2008 Thorsten Kukuk ++ * 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,30 @@ + #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 ++#define SELINUX_ENABLED (is_selinux_enabled()>0) ++#else ++#define SELINUX_ENABLED 0 ++#endif ++ ++#ifdef HELPER_COMPILE ++#define PAMH_ARG_DECL(fname, ...) fname(__VA_ARGS__) ++#else ++#define PAMH_ARG_DECL(fname, ...) fname(pam_handle_t *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 cf4fc078..a16365f8 100644 +--- a/modules/pam_pwhistory/pam_pwhistory.c ++++ b/modules/pam_pwhistory/pam_pwhistory.c +@@ -3,6 +3,7 @@ + * + * Copyright (c) 2008, 2012 Thorsten Kukuk + * Author: Thorsten Kukuk ++ * 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 + #include + #include +-#include + #include + #include + #include ++#include ++#include ++#include ++#include ++#include + + #include + #include +@@ -59,8 +64,6 @@ + #include "opasswd.h" + #include "pam_inline.h" + +-#define DEFAULT_BUFLEN 2048 +- + struct options_t { + int debug; + int enforce_for_root; +@@ -105,6 +108,179 @@ 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) ++ { ++ static char *envp[] = { NULL }; ++ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; ++ ++ if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, ++ PAM_MODUTIL_PIPE_FD, ++ PAM_MODUTIL_PIPE_FD) < 0) ++ { ++ _exit(PAM_SYSTEM_ERR); ++ } ++ ++ /* exec binary helper */ ++ DIAG_PUSH_IGNORE_CAST_QUAL; ++ args[0] = (char *)PWHISTORY_HELPER; ++ args[1] = (char *)"save"; ++ args[2] = (char *)user; ++ DIAG_POP_IGNORE_CAST_QUAL; ++ if (asprintf(&args[3], "%d", howmany) < 0 || ++ asprintf(&args[4], "%d", debug) < 0) ++ { ++ pam_syslog(pamh, LOG_ERR, "asprintf: %m"); ++ _exit(PAM_SYSTEM_ERR); ++ } ++ ++ execve(args[0], args, envp); ++ ++ pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %s: %m", args[0]); ++ ++ _exit(PAM_SYSTEM_ERR); ++ } ++ else if (child > 0) ++ { ++ /* wait for child */ ++ int rc = 0; ++ while ((rc = waitpid (child, &retval, 0)) == -1 && ++ errno == EINTR); ++ if (rc < 0) ++ { ++ pam_syslog(pamh, LOG_ERR, "pwhistory_helper save: waitpid: %m"); ++ 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 ++ { ++ pam_syslog(pamh, LOG_ERR, "fork failed: %m"); ++ 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) ++ { ++ static char *envp[] = { NULL }; ++ char *args[] = { NULL, NULL, NULL, NULL, NULL }; ++ ++ /* reopen stdin as pipe */ ++ if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) ++ { ++ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); ++ _exit(PAM_SYSTEM_ERR); ++ } ++ ++ if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, ++ PAM_MODUTIL_PIPE_FD, ++ PAM_MODUTIL_PIPE_FD) < 0) ++ { ++ _exit(PAM_SYSTEM_ERR); ++ } ++ ++ /* exec binary helper */ ++ DIAG_PUSH_IGNORE_CAST_QUAL; ++ args[0] = (char *)PWHISTORY_HELPER; ++ args[1] = (char *)"check"; ++ args[2] = (char *)user; ++ DIAG_POP_IGNORE_CAST_QUAL; ++ if (asprintf(&args[3], "%d", debug) < 0) ++ { ++ pam_syslog(pamh, LOG_ERR, "asprintf: %m"); ++ _exit(PAM_SYSTEM_ERR); ++ } ++ ++ execve(args[0], args, envp); ++ ++ pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %s: %m", args[0]); ++ ++ _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]); ++ while ((rc = waitpid (child, &retval, 0)) == -1 && ++ errno == EINTR); ++ if (rc < 0) ++ { ++ pam_syslog(pamh, LOG_ERR, "pwhistory_helper check: waitpid: %m"); ++ 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 ++ { ++ pam_syslog(pamh, LOG_ERR, "fork failed: %m"); ++ 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. */ +@@ -112,7 +288,6 @@ parse_option (pam_handle_t *pamh, const char *argv, options_t *options) + 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; +@@ -148,31 +323,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; +@@ -201,8 +358,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 00000000..a0301764 +--- /dev/null ++++ b/modules/pam_pwhistory/pwhistory_helper.8.xml +@@ -0,0 +1,68 @@ ++ ++ ++ ++ ++ ++ ++ pwhistory_helper ++ 8 ++ Linux-PAM Manual ++ ++ ++ ++ pwhistory_helper ++ Helper binary that transfers password hashes from passwd or shadow to opasswd ++ ++ ++ ++ ++ pwhistory_helper ++ ++ ... ++ ++ ++ ++ ++ ++ ++ DESCRIPTION ++ ++ ++ pwhistory_helper is a helper program for the ++ pam_pwhistory 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. ++ ++ ++ ++ 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. ++ ++ ++ ++ The interface of the helper - command line options, and input/output ++ data format are internal to the pam_pwhistory ++ module and it should not be called directly from applications. ++ ++ ++ ++ ++ SEE ALSO ++ ++ ++ pam_pwhistory8 ++ ++ ++ ++ ++ ++ AUTHOR ++ ++ Written by Tomas Mraz based on the code originally in ++ pam_pwhistory and pam_unix modules. ++ ++ ++ ++ +diff --git a/modules/pam_pwhistory/pwhistory_helper.c b/modules/pam_pwhistory/pwhistory_helper.c +new file mode 100644 +index 00000000..b08a14a7 +--- /dev/null ++++ b/modules/pam_pwhistory/pwhistory_helper.c +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) 2013 Red Hat, Inc. ++ * Author: Tomas Mraz ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "opasswd.h" ++#include "pam_inline.h" ++ ++ ++static int ++check_history(const char *user, const char *debug) ++{ ++ char pass[PAM_MAX_RESP_SIZE + 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 = pam_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', PAM_MAX_RESP_SIZE); /* 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; ++ ++ /* ++ * 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"); ++ 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]); ++ ++ fprintf(stderr, "This binary is not designed for running in this way.\n"); ++ ++ return PAM_SYSTEM_ERR; ++} diff --git a/change-ndbm-to-gdbm.patch b/change-ndbm-to-gdbm.patch index 40e3d91..4293b9e 100644 --- a/change-ndbm-to-gdbm.patch +++ b/change-ndbm-to-gdbm.patch @@ -16,7 +16,7 @@ index d537907..ecbaa2d 100644 fi if test -z "$LIBDB" ; then - AC_CHECK_LIB([ndbm],[dbm_store], LIBDB="-lndbm", LIBDB="") -+ AC_CHECK_LIB([gdbm_compat],[dbm_stroe], LIBDB="-lgdbm_compat", LIBDB="") ++ AC_CHECK_LIB([gdbm_compat],[dbm_store], LIBDB="-lgdbm_compat", LIBDB="") if test -n "$LIBDB" ; then AC_CHECK_HEADERS(ndbm.h) fi diff --git a/pam.spec b/pam.spec index ac83a2a..ddf23ed 100644 --- a/pam.spec +++ b/pam.spec @@ -4,7 +4,7 @@ %define _pamconfdir %{_sysconfdir}/pam.d Name: pam Version: 1.4.0 -Release: 4 +Release: 5 Summary: Pluggable Authentication Modules for Linux License: BSD and GPLv2+ URL: http://www.linux-pam.org/ @@ -26,6 +26,8 @@ Patch4: pam_faillock-fix-build-on-musl.patch Patch5: pam_modutil_check_user_in_passwd-avoid-timing-attack.patch Patch6: Prevent-SEGFAULT-for-unknown-UID.patch Patch7: change-ndbm-to-gdbm.patch +Patch6000: backport-Move-read_passwords-function-from-pam_unix-to-pam_inline.h.patch +Patch6001: backport-add-helper-to-handle-SELinux.patch BuildRequires: autoconf automake libtool bison flex sed cracklib-devel gdbm-devel BuildRequires: perl-interpreter pkgconfig gettext-devel libtirpc-devel libnsl2-devel @@ -140,6 +142,7 @@ fi %{_sbindir}/faillock %{_sbindir}/mkhomedir_helper %{_sbindir}/pam_namespace_helper +%attr(0755,root,root) %{_sbindir}/pwhistory_helper %dir %{_moduledir} %{_moduledir}/pam*.so %{_moduledir}/pam_filter/ @@ -177,6 +180,9 @@ fi %changelog +* Mon May 17 2021 shixuantong - 1.4.0-5 +- fix error in change-ndbm-to-gdbm.patch and sync community patch + * Fri Jan 8 2021 wangchen - 1.4.0-4 - Use gdbm