summaryrefslogtreecommitdiffstats
path: root/pw
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2014-11-19 01:07:58 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2014-11-19 01:07:58 +0000
commita697e7c7ae3f57207a6ad6d016fafb4bd4edbed0 (patch)
tree50af2908e381b03974977649fd0dedb542c63475 /pw
parente4061ea7d682a38b6ce4da1d45e20f840d367b19 (diff)
parent04c1a8669ab01c8bb76f4cbf48aa2f8c56fa80e9 (diff)
downloadpw-darwin-a697e7c7ae3f57207a6ad6d016fafb4bd4edbed0.tar.gz
pw-darwin-a697e7c7ae3f57207a6ad6d016fafb4bd4edbed0.tar.zst
pw-darwin-a697e7c7ae3f57207a6ad6d016fafb4bd4edbed0.zip
Merge from head@274682
Diffstat (limited to 'pw')
-rw-r--r--pw/Makefile6
-rw-r--r--pw/pw.c8
-rw-r--r--pw/pw_group.c14
-rw-r--r--pw/pw_user.c27
-rw-r--r--pw/tests/Makefile24
-rw-r--r--pw/tests/group3
-rwxr-xr-xpw/tests/helper_functions.shin15
-rw-r--r--pw/tests/master.passwd4
-rwxr-xr-xpw/tests/pw_delete.sh47
-rwxr-xr-xpw/tests/pw_etcdir.sh18
-rwxr-xr-xpw/tests/pw_lock.sh22
-rwxr-xr-xpw/tests/pw_modify.sh80
12 files changed, 262 insertions, 6 deletions
diff --git a/pw/Makefile b/pw/Makefile
index eae0b87..8c5acf9 100644
--- a/pw/Makefile
+++ b/pw/Makefile
@@ -11,4 +11,10 @@ WARNS?= 2
DPADD= ${LIBCRYPT} ${LIBUTIL}
LDADD= -lcrypt -lutil
+.include <src.opts.mk>
+
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.prog.mk>
diff --git a/pw/pw.c b/pw/pw.c
index b0ac728..ff48db7 100644
--- a/pw/pw.c
+++ b/pw/pw.c
@@ -98,6 +98,7 @@ main(int argc, char *argv[])
int which = -1;
char *config = NULL;
struct userconf *cnf;
+ struct stat st;
static const char *opts[W_NUM][M_NUM] =
{
@@ -143,6 +144,13 @@ main(int argc, char *argv[])
if (argv[1][1] == 'V') {
optarg = &argv[1][2];
if (*optarg == '\0') {
+ if (stat(argv[2], &st) != 0)
+ errx(EX_OSFILE, \
+ "no such directory `%s'",
+ argv[2]);
+ if (!S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "`%s' not a "
+ "directory", argv[2]);
optarg = argv[2];
++argv;
--argc;
diff --git a/pw/pw_group.c b/pw/pw_group.c
index 391e477..b20ce88 100644
--- a/pw/pw_group.c
+++ b/pw/pw_group.c
@@ -51,6 +51,7 @@ int
pw_group(struct userconf * cnf, int mode, struct cargs * args)
{
int rc;
+ struct carg *a_newname = getarg(args, 'l');
struct carg *a_name = getarg(args, 'n');
struct carg *a_gid = getarg(args, 'g');
struct carg *arg;
@@ -66,6 +67,11 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args)
NULL
};
+ if (a_gid != NULL) {
+ if (strspn(a_gid->val, "0123456789") != strlen(a_gid->val))
+ errx(EX_USAGE, "-g expects a number");
+ }
+
if (mode == M_LOCK || mode == M_UNLOCK)
errx(EX_USAGE, "'lock' command is not available for groups");
@@ -140,8 +146,8 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args)
if (a_gid)
grp->gr_gid = (gid_t) atoi(a_gid->val);
- if ((arg = getarg(args, 'l')) != NULL)
- grp->gr_name = pw_checkname((u_char *)arg->val, 0);
+ if (a_newname != NULL)
+ grp->gr_name = pw_checkname((u_char *)a_newname->val, 0);
} else {
if (a_name == NULL) /* Required */
errx(EX_DATAERR, "group name required");
@@ -270,8 +276,10 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args)
warn("group update");
return EX_IOERR;
}
+
+ arg = a_newname != NULL ? a_newname : a_name;
/* grp may have been invalidated */
- if ((grp = GETGRNAM(a_name->val)) == NULL)
+ if ((grp = GETGRNAM(arg->val)) == NULL)
errx(EX_SOFTWARE, "group disappeared during update");
pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
diff --git a/pw/pw_user.c b/pw/pw_user.c
index 36c5d9d..483148a 100644
--- a/pw/pw_user.c
+++ b/pw/pw_user.c
@@ -321,6 +321,9 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
(a_uid = a_name)->ch = 'u';
a_name = NULL;
}
+ } else {
+ if (strspn(a_uid->val, "0123456789") != strlen(a_uid->val))
+ errx(EX_USAGE, "-u expects a number");
}
/*
@@ -615,7 +618,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name);
pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
lc = login_getpwclass(pwd);
- if (lc == NULL || login_setcryptfmt(lc, "md5", NULL) == NULL)
+ if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
warn("setting crypt(3) format");
login_close(lc);
pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
@@ -690,7 +693,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
} else {
lc = login_getpwclass(pwd);
if (lc == NULL ||
- login_setcryptfmt(lc, "md5", NULL) == NULL)
+ login_setcryptfmt(lc, "sha512", NULL) == NULL)
warn("setting crypt(3) format");
login_close(lc);
pwd->pw_passwd = pw_pwcrypt(line);
@@ -751,7 +754,25 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
*/
if (mode == M_ADD || getarg(args, 'G') != NULL) {
- int i;
+ int i, j;
+ /* First remove the user from all group */
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL) {
+ char group[MAXLOGNAME];
+ if (grp->gr_mem == NULL)
+ continue;
+ for (i = 0; grp->gr_mem[i] != NULL; i++) {
+ if (strcmp(grp->gr_mem[i] , pwd->pw_name) != 0)
+ continue;
+ for (j = i; grp->gr_mem[j] != NULL ; j++)
+ grp->gr_mem[j] = grp->gr_mem[j+1];
+ strlcpy(group, grp->gr_name, MAXLOGNAME);
+ chggrent(group, grp);
+ }
+ }
+ ENDGRENT();
+
+ /* now add to group where needed */
for (i = 0; cnf->groups[i] != NULL; i++) {
grp = GETGRNAM(cnf->groups[i]);
grp = gr_add(grp, pwd->pw_name);
diff --git a/pw/tests/Makefile b/pw/tests/Makefile
new file mode 100644
index 0000000..6bc9433
--- /dev/null
+++ b/pw/tests/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+TESTSRC= ${.CURDIR}/../../../contrib/netbsd-tests/usr.sbin/useradd
+.PATH: ${TESTSRC}
+
+TESTSDIR= ${TESTSBASE}/usr.sbin/pw
+
+ATF_TESTS_SH= pw_delete pw_lock pw_modify pw_etcdir
+
+TEST_METADATA.pw_delete+= required_user="root"
+TEST_METADATA.pw_modify+= required_user="root"
+
+FILES= group helper_functions.shin master.passwd
+FILESDIR= ${TESTSDIR}
+
+ATF_TESTS_SH+= pw_test
+# - user{add,del} does not exist on FreeBSD; use pw user{add,del} instead
+# - The command passes on FreeBSD
+ATF_TESTS_SH_SED_pw_test= -e 's/useradd /pw useradd /'
+ATF_TESTS_SH_SED_pw_test+= -e 's/userdel /pw userdel /'
+ATF_TESTS_SH_SED_pw_test+= -e '/atf_expect_fail "PR bin\/39546"/d'
+ATF_TESTS_SH_SRC_pw_test= t_useradd.sh
+
+.include <bsd.test.mk>
diff --git a/pw/tests/group b/pw/tests/group
new file mode 100644
index 0000000..620c588
--- /dev/null
+++ b/pw/tests/group
@@ -0,0 +1,3 @@
+# $FreeBSD$
+#
+wheel:*:0:root
diff --git a/pw/tests/helper_functions.shin b/pw/tests/helper_functions.shin
new file mode 100755
index 0000000..f87b1e7
--- /dev/null
+++ b/pw/tests/helper_functions.shin
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+# Workdir to run tests in
+TESTDIR=$(atf_get_srcdir)
+
+# Populate the files pw needs to use into $HOME/etc
+populate_etc_skel() {
+ cp ${TESTDIR}/master.passwd ${HOME} || \
+ atf_fail "Populating master.passwd in ${HOME}"
+ cp ${TESTDIR}/group ${HOME} || atf_fail "Populating group in ${HOME}"
+
+ # Generate the passwd file
+ pwd_mkdb -p -d ${HOME} ${HOME}/master.passwd || \
+ atf_fail "generate passwd from master.passwd"
+}
diff --git a/pw/tests/master.passwd b/pw/tests/master.passwd
new file mode 100644
index 0000000..f7dc837
--- /dev/null
+++ b/pw/tests/master.passwd
@@ -0,0 +1,4 @@
+# $FreeBSD$
+#
+root:*:0:0::0:0:Charlie &:/root:/bin/csh
+toor:*:0:0::0:0:Bourne-again Superuser:/root:
diff --git a/pw/tests/pw_delete.sh b/pw/tests/pw_delete.sh
new file mode 100755
index 0000000..02a9ade
--- /dev/null
+++ b/pw/tests/pw_delete.sh
@@ -0,0 +1,47 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+# Test that a user can be deleted when another user is part of this
+# user's default group and does not go into an infinate loop.
+# PR: 191427
+atf_test_case rmuser_seperate_group cleanup
+rmuser_seperate_group_head() {
+ atf_set "timeout" "30"
+}
+rmuser_seperate_group_body() {
+ populate_etc_skel
+ pw -V ${HOME} useradd test || atf_fail "Creating test user"
+ pw -V ${HOME} groupmod test -M 'test,root' || \
+ atf_fail "Modifying the group"
+ pw -V ${HOME} userdel test || atf_fail "Delete the test user"
+}
+
+atf_test_case group_do_not_delete_wheel_if_group_unknown
+group_do_not_delete_wheel_if_group_unknown_head() {
+ atf_set "descr" "Make sure we do not consider gid 0 an unknown group"
+}
+
+group_do_not_delete_wheel_if_group_unknown_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -o inline:"wheel:*:0:root\n" -x pw -V ${HOME} groupshow wheel
+ atf_check -e inline:"pw: -g expects a number\n" -s exit:64 -x pw -V ${HOME} groupdel -g I_do_not_exist
+ atf_check -s exit:0 -o inline:"wheel:*:0:root\n" -x pw -V ${HOME} groupshow wheel
+}
+
+atf_test_case user_do_not_try_to_delete_root_if_user_unknown
+user_do_not_try_to_delete_root_if_user_unknown_head() {
+ atf_set "descr" "Make sure not to try to remove root if deleting an unknown user"
+}
+
+user_do_not_try_to_delete_root_if_user_unknown_body() {
+ populate_etc_skel
+ atf_check -e inline:"pw: -u expects a number\n" -s exit:64 -x pw -V ${HOME} userdel -u plop
+}
+
+atf_init_test_cases() {
+ atf_add_test_case rmuser_seperate_group
+ atf_add_test_case group_do_not_delete_wheel_if_group_unknown
+ atf_add_test_case user_do_not_try_to_delete_root_if_user_unknown
+}
diff --git a/pw/tests/pw_etcdir.sh b/pw/tests/pw_etcdir.sh
new file mode 100755
index 0000000..b237789
--- /dev/null
+++ b/pw/tests/pw_etcdir.sh
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+# When the '-V directory' option is provided, the directory must exist
+atf_test_case etcdir_must_exist
+etcdir_must_exist_head() {
+ atf_set "descr" "When the '-V directory' option is provided, the directory must exist"
+}
+
+etcdir_must_exist_body() {
+ local fakedir="/this_directory_does_not_exist"
+ atf_check -e inline:"pw: no such directory \`$fakedir'\n" \
+ -s exit:72 -x pw -V ${fakedir} usershow root
+}
+
+atf_init_test_cases() {
+ atf_add_test_case etcdir_must_exist
+}
+
diff --git a/pw/tests/pw_lock.sh b/pw/tests/pw_lock.sh
new file mode 100755
index 0000000..070a6f9
--- /dev/null
+++ b/pw/tests/pw_lock.sh
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+# Test locking and unlocking a user account
+atf_test_case user_locking cleanup
+user_locking_body() {
+ populate_etc_skel
+ pw -V ${HOME} useradd test || atf_fail "Creating test user"
+ pw -V ${HOME} lock test || atf_fail "Locking the user"
+ atf_check -s exit:0 -o match:"^test:\*LOCKED\*\*:1001:" \
+ grep "^test:\*LOCKED\*\*:1001:" $HOME/master.passwd
+ pw -V ${HOME} unlock test || atf_fail "Locking the user"
+ atf_check -s exit:0 -o match:"^test:\*:1001:" \
+ grep "^test:\*:1001:" $HOME/master.passwd
+}
+
+
+atf_init_test_cases() {
+ atf_add_test_case user_locking
+}
diff --git a/pw/tests/pw_modify.sh b/pw/tests/pw_modify.sh
new file mode 100755
index 0000000..b81f105
--- /dev/null
+++ b/pw/tests/pw_modify.sh
@@ -0,0 +1,80 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+
+# Test adding & removing a user from a group
+atf_test_case groupmod_user
+groupmod_user_body() {
+ populate_etc_skel
+ atf_check -s exit:0 pw -V ${HOME} addgroup test
+ atf_check -s exit:0 pw -V ${HOME} groupmod test -m root
+ atf_check -s exit:0 -o match:"^test:\*:1001:root$" \
+ grep "^test:\*:.*:root$" $HOME/group
+ atf_check -s exit:0 pw -V ${HOME} groupmod test -d root
+ atf_check -s exit:0 -o match:"^test:\*:1001:$" \
+ grep "^test:\*:.*:$" $HOME/group
+}
+
+
+# Test adding and removing a user that does not exist
+atf_test_case groupmod_invalid_user
+groupmod_invalid_user_body() {
+ populate_etc_skel
+ atf_check -s exit:0 pw -V ${HOME} addgroup test
+ atf_check -s exit:67 -e match:"does not exist" pw -V ${HOME} groupmod test -m foo
+ atf_check -s exit:0 pw -V ${HOME} groupmod test -d foo
+}
+
+atf_test_case groupmod_bug_193704
+groupmod_bug_193704_head() {
+ atf_set "descr" "Regression test for the #193704 bug"
+}
+groupmod_bug_193704_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -x pw -V ${HOME} groupadd test
+ atf_check -s exit:0 -x pw -V ${HOME} groupmod test -l newgroupname
+ atf_check -s exit:65 -e match:"^pw: unknown group" -x pw -V ${HOME} groupshow test
+}
+
+atf_test_case usermod_bug_185666
+usermod_bug_185666_head() {
+ atf_set "descr" "Regression test for the #185666 bug"
+}
+
+usermod_bug_185666_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -x pw -V ${HOME} useradd testuser
+ atf_check -s exit:0 -x pw -V ${HOME} groupadd testgroup
+ atf_check -s exit:0 -x pw -V ${HOME} groupadd testgroup2
+ atf_check -s exit:0 -x pw -V ${HOME} usermod testuser -G testgroup
+ atf_check -o inline:"testuser:*:1001:\n" -x pw -V${HOME} groupshow testuser
+ atf_check -o inline:"testgroup:*:1002:testuser\n" -x pw -V ${HOME} groupshow testgroup
+ atf_check -o inline:"testgroup2:*:1003:\n" -x pw -V${HOME} groupshow testgroup2
+ atf_check -s exit:0 -x pw -V ${HOME} usermod testuser -G testgroup2
+ atf_check -o inline:"testuser:*:1001:\n" -x pw -V ${HOME} groupshow testuser
+ atf_check -o inline:"testgroup:*:1002:\n" -x pw -V ${HOME} groupshow testgroup
+ atf_check -o inline:"testgroup2:*:1003:testuser\n" -x pw -V ${HOME} groupshow testgroup2
+}
+
+atf_test_case do_not_duplicate_group_on_gid_change
+do_not_duplicate_group_on_gid_change_head() {
+ atf_set "descr" "Do not duplicate group on gid change"
+}
+
+do_not_duplicate_group_on_gid_change_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -x pw -V ${HOME} groupadd testgroup
+ atf_check -s exit:0 -x pw -V ${HOME} groupmod testgroup -g 12345
+ # use grep to see if the entry has not be duplicated
+ atf_check -o inline:"testgroup:*:12345:\n" -s exit:0 -x grep "^testgroup" ${HOME}/group
+}
+
+atf_init_test_cases() {
+ atf_add_test_case groupmod_user
+ atf_add_test_case groupmod_invalid_user
+ atf_add_test_case groupmod_bug_193704
+ atf_add_test_case usermod_bug_185666
+ atf_add_test_case do_not_duplicate_group_on_gid_change
+}