Patch from Russell Coker:
[oweals/busybox.git] / loginutils / su.c
1 /* vi: set sw=4 ts=4: */
2
3 #include <fcntl.h>
4 #include <signal.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <syslog.h>
9 #include <termios.h>
10 #include <unistd.h>
11 #include <utmp.h>
12 #include <sys/resource.h>
13 #include <sys/stat.h>
14 #include <sys/time.h>
15 #include <sys/types.h>
16 #include <ctype.h>
17 #include <time.h>
18
19 #include "busybox.h"
20
21
22
23 /* The shell to run if none is given in the user's passwd entry.  */
24 #define DEFAULT_SHELL "/bin/sh"
25 #define DEFAULT_USER  "root"
26
27 //#define SYSLOG_SUCCESS
28 #define SYSLOG_FAILURE
29
30
31 #if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
32 /* Log the fact that someone has run su to the user given by PW;
33    if SUCCESSFUL is nonzero, they gave the correct password, etc.  */
34
35 static void log_su ( const struct passwd *pw, int successful )
36 {
37         const char *old_user, *tty;
38
39 #if !defined( SYSLOG_SUCESS )
40         if ( successful )
41                 return;
42 #endif
43 #if !defined( SYSLOG_FAILURE )
44         if ( !successful )
45                 return;
46 #endif
47
48         if ( pw-> pw_uid ) // not to root -> ignored
49                 return;
50
51         /* The utmp entry (via getlogin) is probably the best way to identify
52            the user, especially if someone su's from a su-shell.  */
53         old_user = getlogin ( );
54         if ( !old_user ) {
55                 /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid.  */
56                 struct passwd *pwd = getpwuid ( getuid ( ));
57                 old_user = ( pwd ? pwd-> pw_name : "" );
58         }
59         
60         tty = ttyname ( 2 );
61
62         openlog ( "su", 0, LOG_AUTH );
63         syslog ( LOG_NOTICE, "%s%s on %s", successful ? "" : "FAILED SU ", old_user, tty ? tty : "none" );
64 }
65 #endif
66
67
68
69 int su_main ( int argc, char **argv )
70 {
71         int flag;
72         int opt_preserve = 0;
73         int opt_loginshell = 0;
74         char *opt_shell = 0;
75         char *opt_command = 0;
76         char *opt_username = DEFAULT_USER;
77         char **opt_args = 0;
78         struct passwd *pw, pw_copy;
79
80
81         while (( flag = getopt ( argc, argv, "c:lmps:" )) != -1 ) {
82                 switch ( flag ) {
83                 case 'c':
84                         opt_command = optarg;
85                         break;
86                 case 'm':
87                 case 'p':
88                         opt_preserve = 1;
89                         break;
90                 case 's':
91                         opt_shell = optarg;
92                         break;
93                 case 'l':
94                         opt_loginshell = 1;
95                         break;
96                 default:
97                         bb_show_usage( );
98                         break;
99                 }
100         }
101
102         if (( optind < argc ) && ( argv [optind][0] == '-' ) && ( argv [optind][1] == 0 )) {
103                 opt_loginshell = 1;
104                 ++optind;
105     }
106
107         /* get user if specified */
108         if ( optind < argc ) 
109                 opt_username = argv [optind++];
110
111         if ( optind < argc )
112                 opt_args = argv + optind;
113                 
114                 
115         pw = getpwnam ( opt_username );
116         if ( !pw )
117                 bb_error_msg_and_die ( "user %s does not exist", opt_username );
118                 
119         /* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
120            is a username that is retrieved via NIS (YP), but that doesn't have
121            a default shell listed.  */
122         if ( !pw-> pw_shell || !pw->pw_shell [0] )
123                 pw-> pw_shell = (char *) DEFAULT_SHELL;
124
125         /* Make a copy of the password information and point pw at the local
126            copy instead.  Otherwise, some systems (e.g. Linux) would clobber
127            the static data through the getlogin call from log_su.  */
128         pw_copy = *pw;
129         pw = &pw_copy;
130         pw-> pw_name  = bb_xstrdup ( pw-> pw_name );
131         pw-> pw_dir   = bb_xstrdup ( pw-> pw_dir );
132         pw-> pw_shell = bb_xstrdup ( pw-> pw_shell );
133
134         if (( getuid ( ) == 0 ) || correct_password ( pw )) 
135                 log_su ( pw, 1 );
136         else {
137                 log_su ( pw, 0 );
138                 bb_error_msg_and_die ( "incorrect password" );
139         }
140
141         if ( !opt_shell && opt_preserve )
142                 opt_shell = getenv ( "SHELL" );
143
144         if ( opt_shell && getuid ( ) && restricted_shell ( pw-> pw_shell ))
145         {
146                 /* The user being su'd to has a nonstandard shell, and so is
147                    probably a uucp account or has restricted access.  Don't
148                    compromise the account by allowing access with a standard
149                    shell.  */
150                 fputs ( "using restricted shell\n", stderr );
151                 opt_shell = 0;
152         }
153
154         if ( !opt_shell )
155                 opt_shell = bb_xstrdup ( pw-> pw_shell );
156
157         change_identity ( pw ); 
158         setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw );
159         run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args
160 #ifdef CONFIG_SELINUX
161         , 0
162 #endif
163         );
164         
165         return EXIT_FAILURE;
166 }