/*
+ * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+/*-
* Copyright (c) 1988, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
-#ifndef lint
#if 0
+#ifndef lint
static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
-#endif
-static const char rcsid[] =
- "$FreeBSD: src/usr.bin/su/su.c,v 1.48 2002/01/24 16:20:17 des Exp $";
#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/su/su.c,v 1.91 2009/12/13 03:14:06 delphij Exp $");
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
+#ifdef USE_BSM_AUDIT
+#include <bsm/libbsm.h>
+#include <bsm/audit_uevents.h>
+#endif
+
#include <err.h>
#include <errno.h>
#include <grp.h>
+#ifndef __APPLE__
+#include <login_cap.h>
+#endif /* !__APPLE__ */
#include <paths.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
-
-#include <pam/pam_appl.h>
-#include <pam/pam_misc.h>
-
-#define PAM_END() do { \
- int local_ret; \
- if (pamh != NULL && creds_set) { \
- local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
- if (local_ret != PAM_SUCCESS) \
- syslog(LOG_ERR, "pam_setcred: %s", \
- pam_strerror(pamh, local_ret)); \
- local_ret = pam_close_session(pamh, 0); \
- local_ret = pam_end(pamh, local_ret); \
- if (local_ret != PAM_SUCCESS) \
- syslog(LOG_ERR, "pam_end: %s", \
- pam_strerror(pamh, local_ret)); \
- } \
+#include <stdarg.h>
+
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+
+#ifdef __APPLE__
+#include <bsm/audit_session.h>
+#endif /* __APPLE__ */
+
+#define PAM_END() do { \
+ int local_ret; \
+ if (pamh != NULL) { \
+ local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_setcred: %s", \
+ pam_strerror(pamh, local_ret)); \
+ if (asthem) { \
+ local_ret = pam_close_session(pamh, 0); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_close_session: %s",\
+ pam_strerror(pamh, local_ret)); \
+ } \
+ local_ret = pam_end(pamh, local_ret); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_end: %s", \
+ pam_strerror(pamh, local_ret)); \
+ } \
} while (0)
-#define PAM_SET_ITEM(what, item) do { \
- int local_ret; \
- local_ret = pam_set_item(pamh, what, item); \
- if (local_ret != PAM_SUCCESS) { \
- syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
- pam_strerror(pamh, local_ret)); \
- errx(1, "pam_set_item(" #what "): %s", \
- pam_strerror(pamh, local_ret)); \
- } \
+#define PAM_SET_ITEM(what, item) do { \
+ int local_ret; \
+ local_ret = pam_set_item(pamh, what, item); \
+ if (local_ret != PAM_SUCCESS) { \
+ syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
+ pam_strerror(pamh, local_ret)); \
+ errx(1, "pam_set_item(" #what "): %s", \
+ pam_strerror(pamh, local_ret)); \
+ /* NOTREACHED */ \
+ } \
} while (0)
enum tristate { UNSET, YES, NO };
static pam_handle_t *pamh = NULL;
-static int creds_set = 0;
static char **environ_pam;
static char *ontty(void);
-static int chshell(char *);
-static void usage(void);
-static int export_pam_environment(void);
+static int chshell(const char *);
+static void usage(void) __dead2;
+static void export_pam_environment(void);
static int ok_to_export(const char *);
extern char **environ;
int
main(int argc, char *argv[])
{
+ static char *cleanenv;
struct passwd *pwd;
- struct pam_conv conv = {misc_conv, NULL};
+ struct pam_conv conv = { openpam_ttyconv, NULL };
enum tristate iscsh;
+#ifndef __APPLE__
+ login_cap_t *lc;
+#endif /* !__APPLE__ */
union {
const char **a;
char * const *b;
- } np;
+ } np;
uid_t ruid;
- gid_t gid;
- int asme, ch, asthem, fastlogin, prio, i, setwhat, retcode,
- statusp, child_pid, child_pgrp, ret_pid;
- char *username, *cleanenv, *class, shellbuf[MAXPATHLEN];
+ pid_t child_pid, child_pgrp, pid;
+ int asme, ch, asthem, fastlogin, prio, i, retcode,
+ statusp, setmaclabel;
+#ifndef __APPLE__
+ u_int setwhat;
+#endif /* !__APPLE__ */
+ char *username, *class, shellbuf[MAXPATHLEN];
const char *p, *user, *shell, *mytty, **nargv;
+ const void *v;
+ struct sigaction sa, sa_int, sa_quit, sa_pipe;
+ int temp, fds[2];
+#ifdef USE_BSM_AUDIT
+ const char *aerr;
+ au_id_t auid;
+#endif
+#ifdef __APPLE__
+ /* 4043304 */
+ const char *avshell;
+ char avshellbuf[MAXPATHLEN];
+#endif /* __APPLE__ */
shell = class = cleanenv = NULL;
asme = asthem = fastlogin = statusp = 0;
user = "root";
iscsh = UNSET;
+ setmaclabel = 0;
- while ((ch = getopt(argc, argv, "-flmc:")) != -1)
+#ifdef __APPLE__
+ while ((ch = getopt(argc, argv, "-flm")) != -1)
+#else
+ while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
+#endif /* __APPLE__ */
switch ((char)ch) {
case 'f':
fastlogin = 1;
asme = 1;
asthem = 0;
break;
+#ifndef __APPLE__
+ case 's':
+ setmaclabel = 1;
+ break;
case 'c':
class = optarg;
break;
+#endif /* !__APPLE__ */
case '?':
default:
usage();
+ /* NOTREACHED */
}
if (optind < argc)
if (user == NULL)
usage();
+ /* NOTREACHED */
+
+ /*
+ * Try to provide more helpful debugging output if su(1) is running
+ * non-setuid, or was run from a file system not mounted setuid.
+ */
+ if (geteuid() != 0)
+ errx(1, "not running setuid");
- if (strlen(user) > MAXLOGNAME - 1)
+#ifdef USE_BSM_AUDIT
+ if (getauid(&auid) < 0 && errno != ENOSYS) {
+ syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
+ errx(1, "Permission denied");
+ }
+#endif
+ if (strlen(user) > MAXLOGNAME - 1) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid,
+ EPERM, 1, "username too long: '%s'", user))
+ errx(1, "Permission denied");
+#endif
errx(1, "username too long");
+ }
- nargv = malloc(sizeof(char *) * (argc + 4));
+ nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
if (nargv == NULL)
errx(1, "malloc failure");
pwd = getpwnam(username);
if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
pwd = getpwuid(ruid);
- if (pwd == NULL)
+ if (pwd == NULL) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "unable to determine invoking subject: '%s'", username))
+ errx(1, "Permission denied");
+#endif
errx(1, "who are you?");
- gid = pwd->pw_gid;
+ }
username = strdup(pwd->pw_name);
if (username == NULL)
errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
}
- PAM_SET_ITEM(PAM_RUSER, getlogin());
+ PAM_SET_ITEM(PAM_RUSER, username);
mytty = ttyname(STDERR_FILENO);
if (!mytty)
retcode = pam_authenticate(pamh, 0);
if (retcode != PAM_SUCCESS) {
- syslog(LOG_ERR, "pam_authenticate: %s",
- pam_strerror(pamh, retcode));
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
+ username, user, mytty))
+ errx(1, "Permission denied");
+#endif
+ syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
+ username, user, mytty);
errx(1, "Sorry");
}
- retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
+ errx(1, "Permission denied");
+#endif
+ retcode = pam_get_item(pamh, PAM_USER, &v);
if (retcode == PAM_SUCCESS)
- user = p;
+ user = v;
else
syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
pam_strerror(pamh, retcode));
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "unknown subject: %s", user))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "unknown login: %s", user);
+ }
retcode = pam_acct_mgmt(pamh, 0);
if (retcode == PAM_NEW_AUTHTOK_REQD) {
retcode = pam_chauthtok(pamh,
PAM_CHANGE_EXPIRED_AUTHTOK);
if (retcode != PAM_SUCCESS) {
+#ifdef USE_BSM_AUDIT
+ aerr = pam_strerror(pamh, retcode);
+ if (aerr == NULL)
+ aerr = "Unknown PAM error";
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "pam_chauthtok: %s", aerr))
+ errx(1, "Permission denied");
+#endif
syslog(LOG_ERR, "pam_chauthtok: %s",
pam_strerror(pamh, retcode));
errx(1, "Sorry");
}
}
if (retcode != PAM_SUCCESS) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
+ pam_strerror(pamh, retcode)))
+ errx(1, "Permission denied");
+#endif
syslog(LOG_ERR, "pam_acct_mgmt: %s",
pam_strerror(pamh, retcode));
errx(1, "Sorry");
}
- /* get target login information, default to root */
- pwd = getpwnam(user);
- if (pwd == NULL)
- errx(1, "unknown login: %s", user);
+#ifndef __APPLE__
+ /* get target login information */
+ if (class == NULL)
+ lc = login_getpwclass(pwd);
+ else {
+ if (ruid != 0) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "only root may use -c"))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "only root may use -c");
+ }
+ lc = login_getclass(class);
+ if (lc == NULL)
+ errx(1, "unknown class: %s", class);
+ }
+#endif /* !__APPLE__ */
/* if asme and non-standard target shell, must be root */
if (asme) {
if (ruid != 0 && !chshell(pwd->pw_shell))
- errx(1, "permission denied (shell).");
+ errx(1, "permission denied (shell)");
}
else if (pwd->pw_shell && *pwd->pw_shell) {
+#ifdef __APPLE__
+ /* 3825554 */
+ shell = strncpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
+ shellbuf[sizeof(shellbuf) - 1] = '\0';
+#else
shell = pwd->pw_shell;
+#endif /* __APPLE__ */
iscsh = UNSET;
}
else {
* PAM modules might add supplementary groups in pam_setcred(), so
* initialize them first.
*/
- if( initgroups(user, pwd->pw_gid) )
- err(1, "initgroups failed");
-
- retcode = pam_open_session(pamh, 0);
- if( retcode != PAM_SUCCESS ) {
- syslog(LOG_ERR, "pam_open_session(pamh, 0): %s",
- pam_strerror(pamh, retcode));
+#ifdef __APPLE__
+ if (initgroups(user, pwd->pw_gid))
+ err(1, "initgroups");
+#else
+ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
+ err(1, "setusercontext");
+#endif /* __APPLE__ */
+
+#ifdef __APPLE__
+ /* 8530846 */
+ if (asthem) {
+ auditinfo_addr_t auinfo = {
+ .ai_termid = { .at_type = AU_IPv4 },
+ .ai_asid = AU_ASSIGN_ASID,
+ .ai_auid = getuid(),
+ .ai_flags = 0,
+ };
+ if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
+ char session[16];
+ snprintf(session, sizeof(session), "%x", auinfo.ai_asid);
+ setenv("SECURITYSESSIONID", session, 1);
+ } else {
+ errx(1, "failed to create session.");
+ }
}
+#endif /* __APPLE__ */
retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
- if (retcode != PAM_SUCCESS)
- syslog(LOG_ERR, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s",
+ if (retcode != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_setcred: %s",
pam_strerror(pamh, retcode));
- else
- creds_set = 1;
+ errx(1, "failed to establish credentials.");
+ }
+ if (asthem) {
+ retcode = pam_open_session(pamh, 0);
+ if (retcode != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_open_session: %s",
+ pam_strerror(pamh, retcode));
+ errx(1, "failed to open session.");
+ }
+ }
/*
* We must fork() before setuid() because we need to call
* pam_setcred(pamh, PAM_DELETE_CRED) as root.
*/
-
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGINT, &sa, &sa_int);
+ sigaction(SIGQUIT, &sa, &sa_quit);
+ sigaction(SIGPIPE, &sa, &sa_pipe);
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTSTP, &sa, NULL);
statusp = 1;
+ if (pipe(fds) == -1) {
+ PAM_END();
+ err(1, "pipe");
+ }
child_pid = fork();
switch (child_pid) {
default:
- while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGTTOU, &sa, NULL);
+ close(fds[0]);
+ setpgid(child_pid, child_pid);
+ if (tcgetpgrp(STDERR_FILENO) == getpgrp())
+ tcsetpgrp(STDERR_FILENO, child_pid);
+ close(fds[1]);
+ sigaction(SIGPIPE, &sa_pipe, NULL);
+ while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
if (WIFSTOPPED(statusp)) {
- child_pgrp = tcgetpgrp(1);
+ child_pgrp = getpgid(child_pid);
+ if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
+ tcsetpgrp(STDERR_FILENO, getpgrp());
kill(getpid(), SIGSTOP);
- tcsetpgrp(1, child_pgrp);
- kill(child_pid, SIGCONT);
+ if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
+ child_pgrp = getpgid(child_pid);
+ tcsetpgrp(STDERR_FILENO, child_pgrp);
+ }
+ kill(child_pid, SIGCONT);
statusp = 1;
continue;
}
break;
}
- if (ret_pid == -1)
+ tcsetpgrp(STDERR_FILENO, getpgrp());
+ if (pid == -1)
err(1, "waitpid");
PAM_END();
exit(WEXITSTATUS(statusp));
case -1:
- err(1, "fork");
PAM_END();
- exit(1);
+ err(1, "fork");
case 0:
- if( setgid(pwd->pw_gid) )
+ close(fds[1]);
+ read(fds[0], &temp, 1);
+ close(fds[0]);
+ sigaction(SIGPIPE, &sa_pipe, NULL);
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+
+#ifdef __APPLE__
+ if (setgid(pwd->pw_gid))
err(1, "setgid");
- if( setuid(pwd->pw_uid) )
+ /* Call initgroups(2) after setgid(2) to re-establish memberd */
+ if (initgroups(user, pwd->pw_gid))
+ err(1, "initgroups");
+ if (setuid(pwd->pw_uid))
err(1, "setuid");
+#else
+ /*
+ * Set all user context except for: Environmental variables
+ * Umask Login records (wtmp, etc) Path
+ */
+ setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
+ LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
+ LOGIN_SETMAC);
+ /*
+ * If -s is present, also set the MAC label.
+ */
+ if (setmaclabel)
+ setwhat |= LOGIN_SETMAC;
+ /*
+ * Don't touch resource/priority settings if -m has been used
+ * or -l and -c hasn't, and we're not su'ing to root.
+ */
+ if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
+ setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
+ if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
+ err(1, "setusercontext");
+#endif /* __APPLE__ */
if (!asme) {
if (asthem) {
p = getenv("TERM");
- *environ = NULL;
+ environ = &cleanenv;
+ }
+ if (asthem || pwd->pw_uid)
+ setenv("USER", pwd->pw_name, 1);
+ setenv("HOME", pwd->pw_dir, 1);
+ setenv("SHELL", shell, 1);
+#ifdef __APPLE__
+ unsetenv("TMPDIR");
+#endif /* __APPLE__ */
+
+ if (asthem) {
/*
* Add any environmental variables that the
* PAM modules may have set.
if (environ_pam)
export_pam_environment();
+#ifdef __APPLE__
+ /* 5276965: As documented, set $PATH. */
+ setenv("PATH", "/bin:/usr/bin", 1);
+#else
+ /* set the su'd user's environment & umask */
+ setusercontext(lc, pwd, pwd->pw_uid,
+ LOGIN_SETPATH | LOGIN_SETUMASK |
+ LOGIN_SETENV);
+#endif /* __APPLE__ */
if (p)
setenv("TERM", p, 1);
- if (chdir(pwd->pw_dir) < 0)
+
+ p = pam_getenv(pamh, "HOME");
+ if (chdir(p ? p : pwd->pw_dir) < 0)
errx(1, "no directory");
}
- if (asthem || pwd->pw_uid)
- setenv("USER", pwd->pw_name, 1);
- setenv("HOME", pwd->pw_dir, 1);
- setenv("SHELL", shell, 1);
}
+#ifndef __APPLE__
+ login_close(lc);
+#endif /* !__APPLE__ */
if (iscsh == YES) {
if (fastlogin)
if (asme)
*np.a-- = "-m";
}
+#ifdef __APPLE__
+ /* 4043304 */
+ if ((p = strrchr(shell, '/')) != NULL)
+ avshell = p + 1;
+ else
+ avshell = shell;
+
+ if (asthem) {
+ avshellbuf[0] = '-';
+ strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
+ avshell = avshellbuf;
+ }
+
/* csh *no longer* strips the first character... */
- *np.a = asthem ? "-su" : "su";
+ *np.a = avshell;
+#else
+ /* csh strips the first character... */
+ *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
+#endif /* __APPLE__ */
if (ruid != 0)
syslog(LOG_NOTICE, "%s to %s%s", username, user,
}
}
-static int
+static void
export_pam_environment(void)
{
char **pp;
+ char *p;
for (pp = environ_pam; *pp != NULL; pp++) {
- if (ok_to_export(*pp))
- putenv(*pp);
+ if (ok_to_export(*pp)) {
+ p = strchr(*pp, '=');
+ *p = '\0';
+ setenv(*pp, p + 1, 1);
+ }
free(*pp);
}
- return PAM_SUCCESS;
}
/*
* - Make sure the string doesn't run on too long.
* - Do not export certain variables. This list was taken from the
* Solaris pam_putenv(3) man page.
+ * Note that if the user is chrooted, PAM may have a better idea than we
+ * do of where her home directory is.
*/
static int
ok_to_export(const char *s)
{
static const char *noexport[] = {
- "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
+ "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
"IFS", "PATH", NULL
};
const char **pp;
usage(void)
{
- fprintf(stderr, "usage: su [-] [-flm] [-c class] [login [args]]\n");
+#ifdef __APPLE__
+ fprintf(stderr, "usage: su [-] [-flm] [login [args]]\n");
+#else
+ fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
+#endif /* __APPLE__ */
exit(1);
+ /* NOTREACHED */
}
static int
-chshell(char *sh)
+chshell(const char *sh)
{
int r;
char *cp;
r = 0;
setusershell();
- do {
- cp = getusershell();
- r = strcmp(cp, sh);
- } while (!r && cp != NULL);
+ while ((cp = getusershell()) != NULL && !r)
+ r = (strcmp(cp, sh) == 0);
endusershell();
return r;
}