libbb/login/su: do not sanitize shell name twice
[oweals/busybox.git] / loginutils / su.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini su implementation for busybox
4  *
5  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6  */
7
8 #include "libbb.h"
9 #include <syslog.h>
10
11 //usage:#define su_trivial_usage
12 //usage:       "[OPTIONS] [-] [USER]"
13 //usage:#define su_full_usage "\n\n"
14 //usage:       "Run shell under USER (by default, root)\n"
15 //usage:     "\nOptions:"
16 //usage:     "\n        -,-l    Clear environment, run shell as login shell"
17 //usage:     "\n        -p,-m   Do not set new $HOME, $SHELL, $USER, $LOGNAME"
18 //usage:     "\n        -c CMD  Command to pass to 'sh -c'"
19 //usage:     "\n        -s SH   Shell to use instead of user's default"
20
21 #if ENABLE_FEATURE_SU_CHECKS_SHELLS
22 /* Return 1 if SHELL is a restricted shell (one not returned by
23  * getusershell), else 0, meaning it is a standard shell.  */
24 static int restricted_shell(const char *shell)
25 {
26         char *line;
27         int result = 1;
28
29         /*setusershell(); - getusershell does it itself*/
30         while ((line = getusershell()) != NULL) {
31                 if (/* *line != '#' && */ strcmp(line, shell) == 0) {
32                         result = 0;
33                         break;
34                 }
35         }
36         if (ENABLE_FEATURE_CLEAN_UP)
37                 endusershell();
38         return result;
39 }
40 #endif
41
42 #define SU_OPT_mp (3)
43 #define SU_OPT_l  (4)
44
45 int su_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
46 int su_main(int argc UNUSED_PARAM, char **argv)
47 {
48         unsigned flags;
49         char *opt_shell = NULL;
50         char *opt_command = NULL;
51         const char *opt_username = "root";
52         struct passwd *pw;
53         uid_t cur_uid = getuid();
54         const char *tty;
55 #if ENABLE_FEATURE_UTMP
56         char user_buf[64];
57 #endif
58         const char *old_user;
59
60         flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
61         //argc -= optind;
62         argv += optind;
63
64         if (argv[0] && LONE_DASH(argv[0])) {
65                 flags |= SU_OPT_l;
66                 argv++;
67         }
68
69         /* get user if specified */
70         if (argv[0]) {
71                 opt_username = argv[0];
72                 argv++;
73         }
74
75         if (ENABLE_FEATURE_SU_SYSLOG) {
76                 /* The utmp entry (via getlogin) is probably the best way to
77                  * identify the user, especially if someone su's from a su-shell.
78                  * But getlogin can fail -- usually due to lack of utmp entry.
79                  * in this case resort to getpwuid.  */
80 #if ENABLE_FEATURE_UTMP
81                 old_user = user_buf;
82                 if (getlogin_r(user_buf, sizeof(user_buf)) != 0)
83 #endif
84                 {
85                         pw = getpwuid(cur_uid);
86                         old_user = pw ? xstrdup(pw->pw_name) : "";
87                 }
88                 tty = xmalloc_ttyname(2);
89                 if (!tty) {
90                         tty = "none";
91                 }
92                 openlog(applet_name, 0, LOG_AUTH);
93         }
94
95         pw = xgetpwnam(opt_username);
96
97         if (cur_uid == 0 || correct_password(pw)) {
98                 if (ENABLE_FEATURE_SU_SYSLOG)
99                         syslog(LOG_NOTICE, "%c %s %s:%s",
100                                 '+', tty, old_user, opt_username);
101         } else {
102                 if (ENABLE_FEATURE_SU_SYSLOG)
103                         syslog(LOG_NOTICE, "%c %s %s:%s",
104                                 '-', tty, old_user, opt_username);
105                 bb_error_msg_and_die("incorrect password");
106         }
107
108         if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) {
109                 closelog();
110         }
111
112         if (!opt_shell && (flags & SU_OPT_mp)) {
113                 /* -s SHELL is not given, but "preserve env" opt is */
114                 opt_shell = getenv("SHELL");
115         }
116
117 #if ENABLE_FEATURE_SU_CHECKS_SHELLS
118         if (opt_shell && cur_uid != 0 && pw->pw_shell && restricted_shell(pw->pw_shell)) {
119                 /* The user being su'd to has a nonstandard shell, and so is
120                  * probably a uucp account or has restricted access.  Don't
121                  * compromise the account by allowing access with a standard
122                  * shell.  */
123                 bb_error_msg("using restricted shell");
124                 opt_shell = NULL; /* ignore -s PROG */
125         }
126         /* else: user can run whatever he wants via "su -s PROG USER".
127          * This is safe since PROG is run under user's uid/gid. */
128 #endif
129         if (!opt_shell)
130                 opt_shell = pw->pw_shell;
131
132         change_identity(pw);
133         setup_environment(opt_shell,
134                         ((flags & SU_OPT_l) / SU_OPT_l * SETUP_ENV_CLEARENV)
135                         + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV),
136                         pw);
137         IF_SELINUX(set_current_security_context(NULL);)
138
139         /* Never returns */
140         run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
141
142         /* return EXIT_FAILURE; - not reached */
143 }