The branch main has been updated by bapt: URL: https://cgit.FreeBSD.org/src/commit/?id=92e8a76b87862aa2ee7e6c3f637034e60ffbc2e6
commit 92e8a76b87862aa2ee7e6c3f637034e60ffbc2e6 Author: Baptiste Daroussin <[email protected]> AuthorDate: 2026-09-04 10:17:02 +0000 Commit: Baptiste Daroussin <[email protected]> CommitDate: 2026-09-04 10:42:29 +0000 pw: use _PWDASH pseudo-fd for "-" in pw_checkfd pw_checkfd() returned the character "-" (45) for the "-" argument, which was ambiguous with a real file descriptor. MFC After: 1 week --- usr.sbin/pw/pw.h | 1 + usr.sbin/pw/pw_group.c | 6 +- usr.sbin/pw/pw_user.c | 6 +- usr.sbin/pw/pw_utils.c | 2 +- usr.sbin/pw/tests/Makefile | 10 +++- usr.sbin/pw/tests/pw_unit_test.c | 107 +++++++++++++++++++++++++++++++++++ usr.sbin/pw/tests/pw_useradd_test.sh | 35 ++++++++++++ 7 files changed, 159 insertions(+), 8 deletions(-) diff --git a/usr.sbin/pw/pw.h b/usr.sbin/pw/pw.h index 6be667f166fb..fcb5214a10e5 100644 --- a/usr.sbin/pw/pw.h +++ b/usr.sbin/pw/pw.h @@ -63,6 +63,7 @@ enum _which #define _DEF_DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO) #define _PW_CONF "pw.conf" +#define _PWDASH (-2) /* pseudo-fd returned by pw_checkfd for '-' */ #define _UC_MAXLINE 1024 #define _UC_MAXSHELLS 32 diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c index 2840e972af59..2df22e014b67 100644 --- a/usr.sbin/pw/pw_group.c +++ b/usr.sbin/pw/pw_group.c @@ -56,7 +56,7 @@ grp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) if (fd == -1) return; - if (fd == '-') { + if (fd == _PWDASH) { grp->gr_passwd = "*"; /* No access */ return; } @@ -538,7 +538,7 @@ pw_group_add(int argc, char **argv, char *arg1) "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; - if (fd == '-') + if (fd == _PWDASH) errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': @@ -636,7 +636,7 @@ pw_group_mod(int argc, char **argv, char *arg1) "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; - if (fd == '-') + if (fd == _PWDASH) errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c index abb8a09ce468..e2b84b2a83d3 100644 --- a/usr.sbin/pw/pw_user.c +++ b/usr.sbin/pw/pw_user.c @@ -171,7 +171,7 @@ pw_set_passwd(struct passwd *pwd, int fd, bool precrypted, bool update) char line[_PASSWORD_LEN+1]; char *p; - if (fd == '-') { + if (fd == _PWDASH) { if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { pwd->pw_passwd = "*"; /* No access */ return (1); @@ -1306,7 +1306,7 @@ pw_user_add(int argc, char **argv, char *arg1) "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; - if (fd == '-') + if (fd == _PWDASH) errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': @@ -1629,7 +1629,7 @@ pw_user_mod(int argc, char **argv, char *arg1) "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; - if (fd == '-') + if (fd == _PWDASH) errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': diff --git a/usr.sbin/pw/pw_utils.c b/usr.sbin/pw/pw_utils.c index 87dd421ca8a3..363cceafec39 100644 --- a/usr.sbin/pw/pw_utils.c +++ b/usr.sbin/pw/pw_utils.c @@ -42,7 +42,7 @@ pw_checkfd(char *nptr) int fd = -1; if (strcmp(nptr, "-") == 0) - return '-'; + return (_PWDASH); fd = strtonum(nptr, 0, INT_MAX, &errstr); if (errstr != NULL) errx(EX_USAGE, "Bad file descriptor '%s': %s", diff --git a/usr.sbin/pw/tests/Makefile b/usr.sbin/pw/tests/Makefile index 910a563d680d..b92897ab9208 100644 --- a/usr.sbin/pw/tests/Makefile +++ b/usr.sbin/pw/tests/Makefile @@ -2,8 +2,16 @@ PACKAGE= tests BINDIR= ${TESTSDIR} +.PATH: ${SRCTOP}/usr.sbin/pw + PROGS+= crypt -LIBADD+= crypt +LIBADD.crypt+= crypt + +ATF_TESTS_C= pw_unit_test +SRCS.pw_unit_test= pw_unit_test.c \ + pw_utils.c strtounum.c +LIBADD.pw_unit_test= util +CFLAGS.pw_unit_test= -I${SRCTOP}/usr.sbin/pw ATF_TESTS_SH= pw_etcdir_test \ pw_lock_test \ diff --git a/usr.sbin/pw/tests/pw_unit_test.c b/usr.sbin/pw/tests/pw_unit_test.c new file mode 100644 index 000000000000..f335f8e100a5 --- /dev/null +++ b/usr.sbin/pw/tests/pw_unit_test.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2026 Baptiste Daroussin <[email protected]> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/types.h> + +#include <atf-c.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include "pw.h" +#include "pwupd.h" + +struct pwconf conf; + +/* stub */ +struct userconf * +read_userconfig(char const *file __unused) +{ + + return (NULL); +} + +ATF_TC_WITHOUT_HEAD(checkfd_dash); +ATF_TC_BODY(checkfd_dash, tc) +{ + char dash[] = "-"; + + ATF_CHECK_EQ(pw_checkfd(dash), _PWDASH); +} + +ATF_TC_WITHOUT_HEAD(checkfd_zero); +ATF_TC_BODY(checkfd_zero, tc) +{ + char zero[] = "0"; + + ATF_CHECK_EQ(pw_checkfd(zero), 0); +} + +ATF_TC_WITHOUT_HEAD(checkfd_valid); +ATF_TC_BODY(checkfd_valid, tc) +{ + char five[] = "5"; + + ATF_CHECK_EQ(pw_checkfd(five), 5); +} + +ATF_TC_WITHOUT_HEAD(checkfd_max); +ATF_TC_BODY(checkfd_max, tc) +{ + char max[] = "2147483647"; + + ATF_CHECK_EQ(pw_checkfd(max), INT_MAX); +} + +static void +checkfd_invalid(const char *value, const char *errstr) +{ + pid_t pid; + char buf[32]; + char experr[128]; + + strlcpy(buf, value, sizeof(buf)); + snprintf(experr, sizeof(experr), + "pw_unit_test: Bad file descriptor '%s': %s\n", value, errstr); + pid = atf_utils_fork(); + if (pid == 0) { + pw_checkfd(buf); + exit(1); + } + atf_utils_wait(pid, EX_USAGE, "", experr); +} + +ATF_TC_WITHOUT_HEAD(checkfd_invalid); +ATF_TC_BODY(checkfd_invalid, tc) +{ + checkfd_invalid("abc", "invalid"); +} + +ATF_TC_WITHOUT_HEAD(checkfd_negative); +ATF_TC_BODY(checkfd_negative, tc) +{ + checkfd_invalid("-1", "too small"); +} + +ATF_TC_WITHOUT_HEAD(checkfd_overflow); +ATF_TC_BODY(checkfd_overflow, tc) +{ + checkfd_invalid("999999999999", "too large"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, checkfd_dash); + ATF_TP_ADD_TC(tp, checkfd_zero); + ATF_TP_ADD_TC(tp, checkfd_valid); + ATF_TP_ADD_TC(tp, checkfd_max); + ATF_TP_ADD_TC(tp, checkfd_invalid); + ATF_TP_ADD_TC(tp, checkfd_negative); + ATF_TP_ADD_TC(tp, checkfd_overflow); + + return (atf_no_error()); +} diff --git a/usr.sbin/pw/tests/pw_useradd_test.sh b/usr.sbin/pw/tests/pw_useradd_test.sh index 4b0778759203..82c8d8b8524f 100755 --- a/usr.sbin/pw/tests/pw_useradd_test.sh +++ b/usr.sbin/pw/tests/pw_useradd_test.sh @@ -528,6 +528,38 @@ user_add_already_in_group_body() grep testuser ${HOME}/group } +atf_test_case user_add_fd_dash +user_add_fd_dash_body() +{ + populate_etc_skel + + atf_check -s exit:0 ${PW} useradd fdtest -h - + atf_check -s exit:0 -o match:"^fdtest:\*:" \ + grep "^fdtest:" ${HOME}/master.passwd +} + +atf_test_case user_add_fd_numeric +user_add_fd_numeric_body() +{ + populate_etc_skel + + echo "testpass123" > ${HOME}/pwfile + fd=$(echo ${HOME}/pwfile) + echo "stdinpass" | atf_check -s exit:0 \ + ${PW} useradd fdstdin -h 0 + atf_check -s exit:0 -o not-match:"^fdstdin:\*:" \ + grep "^fdstdin:" ${HOME}/master.passwd +} + +atf_test_case user_add_H_dash_rejected +user_add_H_dash_rejected_body() +{ + populate_etc_skel + + atf_check -s exit:64 -e inline:"pw: -H expects a file descriptor\n" \ + ${PW} useradd htest -H - +} + atf_init_test_cases() { atf_add_test_case user_add atf_add_test_case user_add_noupdate @@ -572,4 +604,7 @@ atf_init_test_cases() { atf_add_test_case user_add_conf_defaultpasswd atf_add_test_case user_add_existing_login_group atf_add_test_case user_add_already_in_group + atf_add_test_case user_add_fd_dash + atf_add_test_case user_add_fd_numeric + atf_add_test_case user_add_H_dash_rejected }
