From 80285e3e194993731e573e5ffa1eba819e283ddd Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 13 Jul 2016 17:09:20 +0000 Subject: pw should sanitize the argument of -w. Otherwise, it will silently disable the login for the selected account if the argument is unrecognizable. usr.sbin/pw/pw.h usr.sbin/pw/pw_conf.c usr.sbin/pw/pw_user.c Use separate rules to validate boolean parameters and passwd parameters. Error out if a password parameter cannot be parsed. usr.sbin/pw/tests/Makefile usr.sbin/pw/tests/crypt.c usr.sbin/pw/tests/pw_useradd.sh usr.sbin/pw/tests/pw_usermod.sh Add tests for the validation. Also, enhance existing password-related tests to actually validate that the correct hash is written to master.passwd. Reviewed by: bapt MFC after: 4 weeks Sponsored by: Spectra Logic Corp Differential Revision: https://reviews.freebsd.org/D6840 --- pw/pw.h | 1 + pw/pw_conf.c | 20 ++++++++++++++++- pw/pw_user.c | 4 ++-- pw/tests/Makefile | 5 +++++ pw/tests/crypt.c | 45 +++++++++++++++++++++++++++++++++++++ pw/tests/pw_useradd.sh | 57 ++++++++++++++++++++++++++++++++++++++--------- pw/tests/pw_usermod.sh | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 pw/tests/crypt.c diff --git a/pw/pw.h b/pw/pw.h index b389f12..05a51c2 100644 --- a/pw/pw.h +++ b/pw/pw.h @@ -93,6 +93,7 @@ int groupadd(struct userconf *, char *name, gid_t id, char *members, int fd, int nis_update(void); int boolean_val(char const * str, int dflt); +int passwd_val(char const * str, int dflt); char const *boolean_str(int val); char *newstr(char const * p); diff --git a/pw/pw_conf.c b/pw/pw_conf.c index d30c80e..a3bd0bd 100644 --- a/pw/pw_conf.c +++ b/pw/pw_conf.c @@ -180,6 +180,22 @@ boolean_val(char const * str, int dflt) if ((str = unquote(str)) != NULL) { int i; + for (i = 0; booltrue[i]; i++) + if (strcmp(str, booltrue[i]) == 0) + return 1; + for (i = 0; boolfalse[i]; i++) + if (strcmp(str, boolfalse[i]) == 0) + return 0; + } + return dflt; +} + +int +passwd_val(char const * str, int dflt) +{ + if ((str = unquote(str)) != NULL) { + int i; + for (i = 0; booltrue[i]; i++) if (strcmp(str, booltrue[i]) == 0) return 1; @@ -194,6 +210,8 @@ boolean_val(char const * str, int dflt) return -1; if (strcmp(str, "none") == 0) return -2; + + errx(1, "Invalid value for default password"); } return dflt; } @@ -258,7 +276,7 @@ read_userconfig(char const * file) #endif switch (i) { case _UC_DEFAULTPWD: - config.default_password = boolean_val(q, 1); + config.default_password = passwd_val(q, 1); break; case _UC_REUSEUID: config.reuse_uids = boolean_val(q, 0); diff --git a/pw/pw_user.c b/pw/pw_user.c index 67186c3..75e7fb6 100644 --- a/pw/pw_user.c +++ b/pw/pw_user.c @@ -1315,7 +1315,7 @@ pw_user_add(int argc, char **argv, char *arg1) mix_config(cmdcnf, cnf); if (default_passwd) - cmdcnf->default_password = boolean_val(default_passwd, + cmdcnf->default_password = passwd_val(default_passwd, cnf->default_password); if (genconf) { if (name != NULL) @@ -1717,7 +1717,7 @@ pw_user_mod(int argc, char **argv, char *arg1) if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); - cnf->default_password = boolean_val(passwd, + cnf->default_password = passwd_val(passwd, cnf->default_password); pwd->pw_passwd = pw_password(cnf, pwd->pw_name, dryrun); edited = true; diff --git a/pw/tests/Makefile b/pw/tests/Makefile index 1c69d50..1ae14f3 100644 --- a/pw/tests/Makefile +++ b/pw/tests/Makefile @@ -2,6 +2,11 @@ PACKAGE= tests +BINDIR= ${TESTSDIR} + +PROGS+= crypt +LIBADD+= crypt + ATF_TESTS_SH= pw_etcdir \ pw_lock \ pw_config \ diff --git a/pw/tests/crypt.c b/pw/tests/crypt.c new file mode 100644 index 0000000..586fccd --- /dev/null +++ b/pw/tests/crypt.c @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2016 Spectra Logic Corporation + * 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, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``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 AUTHORS OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include + +int main(int argc, char** argv) +{ + char *salt, *pass, *hash; + + if (argc < 3) + errx(1, "Usage: crypt "); + salt = argv[1]; + pass = argv[2]; + + hash = crypt(pass, salt); + printf("%s", hash); + return (hash == NULL ? 1 : 0); +} diff --git a/pw/tests/pw_useradd.sh b/pw/tests/pw_useradd.sh index cb62944..b1ff8d4 100755 --- a/pw/tests/pw_useradd.sh +++ b/pw/tests/pw_useradd.sh @@ -235,9 +235,12 @@ atf_test_case user_add_password_from_h user_add_password_from_h_body() { populate_etc_skel - atf_check -s exit:0 ${PW} useradd test -h 0 <<-EOF - $(echo test) + atf_check -s exit:0 ${PW} useradd foo -h 0 <<-EOF + $(echo mypassword) EOF + passhash=`awk -F ':' '/^foo:/ {print $2}' $HOME/master.passwd` + atf_check -s exit:0 -o inline:$passhash \ + $(atf_get_srcdir)/crypt $passhash "mypassword" } atf_test_case user_add_R @@ -325,17 +328,47 @@ user_add_already_exists_body() { ${PW} useradd foo } +atf_test_case user_add_w_error +user_add_w_error_body() { + populate_etc_skel + + atf_check -s exit:1 -e match:"pw: Invalid value for default password" \ + ${PW} useradd foo -w invalid_value +} + +atf_test_case user_add_w_no +user_add_w_no_body() { + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd foo -w no + atf_check -s exit:0 -o match:"^foo:\*" grep "^foo:" $HOME/master.passwd +} + +atf_test_case user_add_w_none +user_add_w_none_body() { + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd foo -w none + atf_check -s exit:0 -o match:"^foo::" grep "^foo:" $HOME/master.passwd +} + +atf_test_case user_add_w_random +user_add_w_random_body() { + populate_etc_skel + + password=`${PW} useradd foo -w random | cat` + passhash=`awk -F ':' '/^foo:/ {print $2}' $HOME/master.passwd` + atf_check -s exit:0 -o inline:$passhash \ + $(atf_get_srcdir)/crypt $passhash "$password" +} + atf_test_case user_add_w_yes user_add_w_yes_body() { populate_etc_skel - atf_check -s exit:0 ${PW} useradd foo -w yes - atf_check -s exit:0 \ - -o match:'^foo:\$.*' \ - grep "^foo" ${HOME}/master.passwd - atf_check -s exit:0 ${PW} usermod foo -w yes - atf_check -s exit:0 \ - -o match:'^foo:\$.*' \ - grep "^foo" ${HOME}/master.passwd + password=`${PW} useradd foo -w random | cat` + passhash=`awk -F ':' '/^foo:/ {print $2}' $HOME/master.passwd` + atf_check -s exit:0 -o inline:$passhash \ + $(atf_get_srcdir)/crypt $passhash "$password" } atf_test_case user_add_with_pw_conf @@ -380,6 +413,10 @@ atf_init_test_cases() { atf_add_test_case user_add_uid_too_large atf_add_test_case user_add_bad_shell atf_add_test_case user_add_already_exists + atf_add_test_case user_add_w_error + atf_add_test_case user_add_w_no + atf_add_test_case user_add_w_none + atf_add_test_case user_add_w_random atf_add_test_case user_add_w_yes atf_add_test_case user_add_with_pw_conf } diff --git a/pw/tests/pw_usermod.sh b/pw/tests/pw_usermod.sh index 236fd27..df056e0 100755 --- a/pw/tests/pw_usermod.sh +++ b/pw/tests/pw_usermod.sh @@ -157,8 +157,9 @@ user_mod_h_body() { atf_check -s exit:0 ${PW} usermod foo -h 0 <<- EOF $(echo a) EOF - atf_check -s exit:0 -o not-match:"^foo:\*:.*" \ - grep "^foo" ${HOME}/master.passwd + passhash=`awk -F ':' '/^foo:/ {print $2}' $HOME/master.passwd` + atf_check -s exit:0 -o inline:$passhash \ + $(atf_get_srcdir)/crypt $passhash "a" atf_check -s exit:0 ${PW} usermod foo -h - <<- EOF $(echo b) EOF @@ -203,6 +204,56 @@ user_mod_uid_body() { atf_check -s exit:0 ${PW} usermod foo -u 5000 } +atf_test_case user_mod_w_error +user_mod_w_error_body() { + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd foo + atf_check -s exit:1 -e match:"pw: Invalid value for default password" \ + ${PW} usermod foo -w invalid_value +} + +atf_test_case user_mod_w_no +user_mod_w_no_body() { + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd foo + atf_check -s exit:0 ${PW} usermod foo -w no + atf_check -s exit:0 -o match:"^foo:\*" grep "^foo:" $HOME/master.passwd +} + +atf_test_case user_mod_w_none +user_mod_w_none_body() { + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd foo + atf_check -s exit:0 ${PW} usermod foo -w none + atf_check -s exit:0 -o match:"^foo::" grep "^foo:" $HOME/master.passwd +} + +atf_test_case user_mod_w_random +user_mod_w_random_body() { + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd foo + password=`${PW} usermod foo -w random | cat` + passhash=`awk -F ':' '/^foo:/ {print $2}' $HOME/master.passwd` + atf_check -s exit:0 -o inline:$passhash \ + $(atf_get_srcdir)/crypt $passhash "$password" +} + +atf_test_case user_mod_w_yes +user_mod_w_yes_body() { + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd foo + atf_check -s exit:0 ${PW} usermod foo -w yes + passhash=`awk -F ':' '/^foo:/ {print $2}' $HOME/master.passwd` + atf_check -s exit:0 -o inline:$passhash \ + $(atf_get_srcdir)/crypt $passhash "foo" +} + + atf_init_test_cases() { atf_add_test_case user_mod atf_add_test_case user_mod_noupdate @@ -219,4 +270,9 @@ atf_init_test_cases() { atf_add_test_case user_mod_H atf_add_test_case user_mod_renamehome atf_add_test_case user_mod_uid + atf_add_test_case user_mod_w_error + atf_add_test_case user_mod_w_no + atf_add_test_case user_mod_w_none + atf_add_test_case user_mod_w_random + atf_add_test_case user_mod_w_yes } -- cgit v1.2.3-56-ge451