Hi,

this patch fixes error paths and an undefined behaviour:

In getnewpasswd we increment "tries" every time we try to enter a new
password. The code allows this to be repeated endlessly by defining
passwordtries to be 0 in /etc/login.conf. But unfortunately we even
increment the int "tries" if pwd_tries is 0, which eventually would lead
to a signed integer overflow (and we would stop asking for passwords
after billion times).

In pwd_check we should close pipe file descriptors if fork fails. Also
it might be possible that waitpid fails if the root user tries to
sabotage the own passwd call. Let's just handle the error case here
as well to avoid accessing undefined content of "res".

Okay?


Tobias

Index: local_passwd.c
===================================================================
RCS file: /cvs/src/usr.bin/passwd/local_passwd.c,v
retrieving revision 1.63
diff -u -p -u -p -r1.63 local_passwd.c
--- local_passwd.c      10 Feb 2022 13:06:46 -0000      1.63
+++ local_passwd.c      5 May 2023 16:35:58 -0000
@@ -217,7 +217,7 @@ getnewpasswd(struct passwd *pw, login_ca
                        continue;
                }
 
-               if ((tries++ < pwd_tries || pwd_tries == 0) &&
+               if ((pwd_tries == 0 || tries++ < pwd_tries) &&
                    pwd_check(lc, p) == 0)
                        continue;
                p = readpassphrase("Retype new password:", repeat, 
sizeof(repeat),
Index: pwd_check.c
===================================================================
RCS file: /cvs/src/usr.bin/passwd/pwd_check.c,v
retrieving revision 1.17
diff -u -p -u -p -r1.17 pwd_check.c
--- pwd_check.c 28 Aug 2021 06:46:49 -0000      1.17
+++ pwd_check.c 5 May 2023 16:35:58 -0000
@@ -91,7 +91,7 @@ pwd_check(login_cap_t *lc, char *passwor
        char *checker;
        char *argp[] = { "sh", "-c", NULL, NULL};
        int pipefds[2];
-       pid_t child;
+       pid_t child, c;
        uid_t uid;
        gid_t gid;
 
@@ -114,6 +114,8 @@ pwd_check(login_cap_t *lc, char *passwor
        switch (child = fork()) {
        case -1:
                warn("fork");
+               close(pipefds[0]);
+               close(pipefds[1]);
                goto out;
        case 0:
                (void)signal(SIGINT, SIG_DFL);
@@ -183,11 +185,11 @@ pwd_check(login_cap_t *lc, char *passwor
        }
 
        /* get the return value from the child */
-       while (waitpid(child, &res, 0) == -1) {
+       while ((c = waitpid(child, &res, 0)) == -1) {
                if (errno != EINTR)
                        break;
        }
-       if (WIFEXITED(res) && WEXITSTATUS(res) == 0) {
+       if (c == child && WIFEXITED(res) && WEXITSTATUS(res) == 0) {
                free(checker);
                return (1);
        }

Reply via email to