Subject: bin/21693: Bug in su when using 'su -m', Hesiod, and root shell /bin/sh
To: None <>
From: None <>
List: netbsd-bugs
Date: 05/27/2003 01:56:03
>Number:         21693
>Category:       bin
>Synopsis:       su needs to call setusershell() before using getusershell()
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue May 27 05:58:00 UTC 2003
>Originator:     Geoff Adams
>Release:        NetBSD 1.6.1_STABLE, source from May 24, 2003
System: NetBSD 1.6.1_RC2 NetBSD 1.6.1_RC2 (UNNAMED) #0: Mon Dec 9 20:43:49 EST 2002 sparc
Architecture: sparc
Machine: sparc
When using the -m flag to su, su checks to make sure the target shell is
listed in the shells database (usually /etc/shells).  It does this,
naturally, by cycling through the list of shells using getusershell().
However, it does not call setusershell() first, to reset the scan to
the top of the file.  This is fine in most circumstances, but in certain
situations, the getpwnam() function will itself call getusershell(), and
since su calls getpwnam() before doing the target shell check, it will
miss some entries.

In particular, when the target user is root, and the passwd database is
in Hesiod or NIS, and root's shell is /bin/sh, getpwnam() will call
getusershell() once (assuming /bin/sh is the first entry in /etc/shells),
leaving the pointer at the second shell in /etc/shells.  Then, when
'su -m' checks the target (root) shell, it will compare /bin/sh against
all the entries *after* /bin/sh.  It will then emit the error:

su: permission denied (shell).
Set the nsswitch.conf entry for shells to "files".
Set the nsswitch.conf entry for passwd to lookup in Hesiod or NIS, such as:
    passwd:         dns [notfound=return] files
Set root's shell to /bin/sh.
Make sure /bin/sh is the first shell listed in /etc/shells.
Log in as a non-root user.
Type 'su -m' and enter the correct password for your local auth scheme.
Notice the error:
    su: permission denied (shell).
It seems that getpwnam() shouldn't be calling functions that have
significant side effects, such as getusershell().  However, since it
does, and since changing the libc functions to avoid the side effects
seems very difficult, the most straightforward fix is to call
setusershell() in su.c to rewind the shells database before looping
over getusershell().  The patch below does that.

Index: su.c
RCS file: /cvsroot/src/usr.bin/su/su.c,v
retrieving revision 1.48
diff -u -r1.48 su.c
--- su.c	2001/04/23 06:52:22	1.48
+++ su.c	2003/05/27 05:33:01
@@ -414,6 +414,7 @@
 	const char *cp;
+	setusershell();
 	while ((cp = getusershell()) != NULL)
 		if (!strcmp(cp, sh))
 			return (1);