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