From a950a390a1c49a7df0bbe67c92dc2480f7025846 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Thu, 12 Apr 2018 11:57:15 +0200 Subject: [PATCH 1/3] nsswitch: Add try_authok option to pam_winbind Same as the use_authtok option, except that if the new password is not valid, PAM will prompt for a password. Bug-Debian: https://bugs.debian.org/858923 Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/samba/+bug/570944 Signed-off-by: Mathieu Parent --- docs-xml/manpages/pam_winbind.8.xml | 8 ++++++++ nsswitch/pam_winbind.c | 5 +++++ nsswitch/pam_winbind.h | 1 + 3 files changed, 14 insertions(+) diff --git a/docs-xml/manpages/pam_winbind.8.xml b/docs-xml/manpages/pam_winbind.8.xml index f57a9286a6c..b8af5b54c58 100644 --- a/docs-xml/manpages/pam_winbind.8.xml +++ b/docs-xml/manpages/pam_winbind.8.xml @@ -122,6 +122,14 @@ + + try_authtok + + Same as the use_authtok option (previous item), except that if the new password is not + valid, PAM will prompt for a password. + + + krb5_auth diff --git a/nsswitch/pam_winbind.c b/nsswitch/pam_winbind.c index 7ac5bb08181..1a58ba49c48 100644 --- a/nsswitch/pam_winbind.c +++ b/nsswitch/pam_winbind.c @@ -492,6 +492,8 @@ static int _pam_parse(const pam_handle_t *pamh, ctrl |= WINBIND_SILENT; else if (!strcasecmp(*v, "use_authtok")) ctrl |= WINBIND_USE_AUTHTOK_ARG; + else if (!strcasecmp(*v, "try_authtok")) + ctrl |= WINBIND_TRY_AUTHTOK_ARG; else if (!strcasecmp(*v, "use_first_pass")) ctrl |= WINBIND_USE_FIRST_PASS_ARG; else if (!strcasecmp(*v, "try_first_pass")) @@ -3181,6 +3183,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) { lctrl |= WINBIND_USE_FIRST_PASS_ARG; } + if (on(WINBIND_TRY_AUTHTOK_ARG, lctrl)) { + lctrl |= WINBIND_TRY_FIRST_PASS_ARG; + } retry = 0; ret = PAM_AUTHTOK_ERR; while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) { diff --git a/nsswitch/pam_winbind.h b/nsswitch/pam_winbind.h index d468efbb56a..c6786d65a4d 100644 --- a/nsswitch/pam_winbind.h +++ b/nsswitch/pam_winbind.h @@ -156,6 +156,7 @@ do { \ #define WINBIND_DEBUG_STATE 0x00001000 #define WINBIND_WARN_PWD_EXPIRE 0x00002000 #define WINBIND_MKHOMEDIR 0x00004000 +#define WINBIND_TRY_AUTHTOK_ARG 0x00008000 #if defined(HAVE_GETTEXT) && !defined(__LCLINT__) #define _(string) dgettext(MODULE_NAME, string) From 37499e31ea1786e99617de36ec0c49d0e895e2ba Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Sat, 19 May 2018 14:57:01 +0200 Subject: [PATCH 2/3] Add pam_set_items.so from pam-wrapper Signed-off-by: Mathieu Parent --- third_party/pam_wrapper/modules/pam_set_items.c | 164 ++++++++++++++++++++++++ third_party/pam_wrapper/wscript | 6 + 2 files changed, 170 insertions(+) create mode 100644 third_party/pam_wrapper/modules/pam_set_items.c diff --git a/third_party/pam_wrapper/modules/pam_set_items.c b/third_party/pam_wrapper/modules/pam_set_items.c new file mode 100644 index 00000000000..22c2c565cb4 --- /dev/null +++ b/third_party/pam_wrapper/modules/pam_set_items.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2015 Andreas Schneider + * Copyright (c) 2015 Jakub Hrozek + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_SECURITY_PAM_APPL_H +#include +#endif +#ifdef HAVE_SECURITY_PAM_MODULES_H +#include +#endif + +#include "config.h" + +#define ITEM_FILE_KEY "item_file=" + +static const char *envs[] = { +#ifndef HAVE_OPENPAM + "PAM_SERVICE", +#endif + "PAM_USER", + "PAM_USER_PROMPT", + "PAM_TTY", + "PAM_RUSER", + "PAM_RHOST", + "PAM_AUTHTOK", + "PAM_OLDAUTHTOK", +#ifdef PAM_XDISPLAY + "PAM_XDISPLAY", +#endif +#ifdef PAM_AUTHTOK_TYPE + "PAM_AUTHTOK_TYPE", +#endif + NULL +}; + +static const int items[] = { +#ifndef HAVE_OPENPAM + PAM_SERVICE, +#endif + PAM_USER, + PAM_USER_PROMPT, + PAM_TTY, + PAM_RUSER, + PAM_RHOST, + PAM_AUTHTOK, + PAM_OLDAUTHTOK, +#ifdef PAM_XDISPLAY + PAM_XDISPLAY, +#endif +#ifdef PAM_AUTHTOK_TYPE + PAM_AUTHTOK_TYPE, +#endif +}; + +static void pam_setitem_env(pam_handle_t *pamh) +{ + int i; + int rv; + const char *v; + + for (i = 0; envs[i] != NULL; i++) { + v = getenv(envs[i]); + if (v == NULL) { + continue; + } + + rv = pam_set_item(pamh, items[i], v); + if (rv != PAM_SUCCESS) { + continue; + } + } +} + +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + (void) flags; /* unused */ + (void) argc; /* unused */ + (void) argv; /* unused */ + + pam_setitem_env(pamh); + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + (void) flags; /* unused */ + (void) argc; /* unused */ + (void) argv; /* unused */ + + pam_setitem_env(pamh); + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + (void) flags; /* unused */ + (void) argc; /* unused */ + (void) argv; /* unused */ + + pam_setitem_env(pamh); + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + (void) flags; /* unused */ + (void) argc; /* unused */ + (void) argv; /* unused */ + + pam_setitem_env(pamh); + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + (void) flags; /* unused */ + (void) argc; /* unused */ + (void) argv; /* unused */ + + pam_setitem_env(pamh); + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + (void) flags; /* unused */ + (void) argc; /* unused */ + (void) argv; /* unused */ + + pam_setitem_env(pamh); + return PAM_SUCCESS; +} + diff --git a/third_party/pam_wrapper/wscript b/third_party/pam_wrapper/wscript index f70fc66351a..e3fb26f5547 100644 --- a/third_party/pam_wrapper/wscript +++ b/third_party/pam_wrapper/wscript @@ -113,6 +113,12 @@ def build(bld): source='libpamtest.c', deps='dl pam') + bld.SAMBA_LIBRARY('pam_set_items', + source='modules/pam_set_items.c', + deps='pam', + install=False, + realname='pam_set_items.so') + # Can be used to write pam tests in python for env in bld.gen_python_environments(): bld.SAMBA_PYTHON('pypamtest', From ed8d16e7f5414c98c098dbe385daa8b88f49f4a6 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Thu, 31 May 2018 21:16:31 +0200 Subject: [PATCH 3/3] (WIP not working) Test some pam_winbind options Signed-off-by: Mathieu Parent --- python/samba/tests/pam_winbind_options.py | 44 ++++++++++++++++++++++++ python/samba/tests/test_pam_winbind_options.sh | 47 ++++++++++++++++++++++++++ selftest/tests.py | 14 ++++++++ 3 files changed, 105 insertions(+) create mode 100644 python/samba/tests/pam_winbind_options.py create mode 100755 python/samba/tests/test_pam_winbind_options.sh diff --git a/python/samba/tests/pam_winbind_options.py b/python/samba/tests/pam_winbind_options.py new file mode 100644 index 00000000000..58dcaf408b5 --- /dev/null +++ b/python/samba/tests/pam_winbind_options.py @@ -0,0 +1,44 @@ +# Unix SMB/CIFS implementation. +# +# Copyright (C) 2017 Andreas Schneider +# Copyright (C) 2018 Mathieu Parent +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import samba.tests +import pypamtest +import os + +class PamOptionsTests(samba.tests.TestCase): + def test_chauthtok(self): + domain = os.environ["DOMAIN"] + username = os.environ["USERNAME"] + password = os.environ["PASSWORD"] + newpassword = os.environ["NEWPASSWORD"] + unix_username = "%s/%s" % (domain, username) + expected_rc = 0 # PAM_SUCCESS + + tc = pypamtest.TestCase(pypamtest.PAMTEST_CHAUTHTOK, expected_rc) + res = pypamtest.run_pamtest(unix_username, "samba", [tc], [password, newpassword], ["aaa"]) + + self.assertTrue(res != None) + +# # Change password back +# expected_rc = 0 # PAM_SUCCESS + +# tc = pypamtest.TestCase(pypamtest.PAMTEST_CHAUTHTOK, expected_rc) +# res = pypamtest.run_pamtest(unix_username, "samba", [tc], [newpassword, password]) + +# self.assertTrue(res != None) diff --git a/python/samba/tests/test_pam_winbind_options.sh b/python/samba/tests/test_pam_winbind_options.sh new file mode 100755 index 00000000000..5702b15fef0 --- /dev/null +++ b/python/samba/tests/test_pam_winbind_options.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +PYTHON="$1" +PAM_WRAPPER_SO_PATH="$2" +shift 2 + +DOMAIN="$1" +export DOMAIN +USERNAME="$2" +export USERNAME +PASSWORD="$3" +export PASSWORD +NEWPASSWORD="$4" +export NEWPASSWORD +PAM_OPTIONS="$5" +export PAM_OPTIONS +shift 5 + +PAM_WRAPPER_PATH="$BINDIR/default/third_party/pam_wrapper" + +pam_winbind="$BINDIR/shared/pam_winbind.so" +service_dir="$SELFTEST_TMPDIR/pam_services" +service_file="$service_dir/samba" + +mkdir $service_dir +echo "auth required $pam_winbind debug debug_state $PAM_OPTIONS" > $service_file +echo "account required $pam_winbind debug debug_state $PAM_OPTIONS" >> $service_file +echo "password required pam_set_items.so" >> $service_file +echo "password required $pam_winbind debug debug_state $PAM_OPTIONS" >> $service_file +echo "session required $pam_winbind debug debug_state $PAM_OPTIONS" >> $service_file + +PAM_WRAPPER="1" +export PAM_WRAPPER +PAM_WRAPPER_SERVICE_DIR="$service_dir" +export PAM_WRAPPER_SERVICE_DIR +LD_PRELOAD="$LD_PRELOAD:$PAM_WRAPPER_SO_PATH" +export LD_PRELOAD + +PAM_WRAPPER_DEBUGLEVEL=${PAM_WRAPPER_DEBUGLEVEL:="2"} +export PAM_WRAPPER_DEBUGLEVEL + +PYTHONPATH="$PYTHONPATH:$PAM_WRAPPER_PATH:$(dirname $0)" $PYTHON -m samba.subunit.run samba.tests.pam_winbind_options +exit_code=$? + +rm -rf $service_dir + +exit $exit_code diff --git a/selftest/tests.py b/selftest/tests.py index f354bb57ef5..222e7780c83 100644 --- a/selftest/tests.py +++ b/selftest/tests.py @@ -167,6 +167,20 @@ [os.path.join(srcdir(), "python/samba/tests/test_pam_winbind.sh"), valgrindify(python), pam_wrapper_so_path, "$DOMAIN", "$DC_USERNAME", "$DC_PASSWORD"]) + + #for pam_options in ["''", "'use_authtok'", "'try_authtok'"]: + for pam_options in ["'try_authtok'"]: + plantestsuite("samba.tests.pam_winbind with options %s(local)" % pam_options, "ad_member", + [os.path.join(srcdir(), "python/samba/tests/test_pam_winbind_options.sh"), + valgrindify(python), pam_wrapper_so_path, + "$SERVER", "$USERNAME", "$PASSWORD", "newpassword0", + pam_options]) + plantestsuite("samba.tests.pam_winbind with options %s(domain)" % pam_options, "ad_member", + [os.path.join(srcdir(), "python/samba/tests/test_pam_winbind_options.sh"), + valgrindify(python), pam_wrapper_so_path, + "$DOMAIN", "$DC_USERNAME", "$DC_PASSWORD", "newpassword0", + pam_options]) + plantestsuite("samba.tests.pam_winbind_warn_pwd_expire(domain)", "ad_member", [os.path.join(srcdir(), "python/samba/tests/test_pam_winbind_warn_pwd_expire.sh"), valgrindify(python), pam_wrapper_so_path,