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