aboutsummaryrefslogtreecommitdiff
path: root/loginutils/su.c
blob: 5e40cf2e42d748bc53409244d0c92df0546897df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* vi: set sw=4 ts=4: */

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>
#include <utmp.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <ctype.h>
#include <time.h>

#include "busybox.h"



/* The shell to run if none is given in the user's passwd entry.  */
#define DEFAULT_SHELL "/bin/sh"
#define DEFAULT_USER  "root"

//#define SYSLOG_SUCCESS
#define SYSLOG_FAILURE


#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
/* Log the fact that someone has run su to the user given by PW;
   if SUCCESSFUL is nonzero, they gave the correct password, etc.  */

static void log_su ( const struct passwd *pw, int successful )
{
	const char *old_user, *tty;

#if !defined( SYSLOG_SUCESS )
	if ( successful )
		return;
#endif
#if !defined( SYSLOG_FAILURE )
	if ( !successful )
		return;
#endif

	if ( pw-> pw_uid ) // not to root -> ignored
		return;

	/* The utmp entry (via getlogin) is probably the best way to identify
	   the user, especially if someone su's from a su-shell.  */
	old_user = getlogin ( );
	if ( !old_user ) {
		/* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid.  */
		struct passwd *pwd = getpwuid ( getuid ( ));
		old_user = ( pwd ? pwd-> pw_name : "" );
	}
	
	tty = ttyname ( 2 );

	openlog ( "su", 0, LOG_AUTH );
	syslog ( LOG_NOTICE, "%s%s on %s", successful ? "" : "FAILED SU ", old_user, tty ? tty : "none" );
}
#endif



int su_main ( int argc, char **argv )
{
	int flag;
	int opt_preserve = 0;
	int opt_loginshell = 0;
	char *opt_shell = 0;
	char *opt_command = 0;
	char *opt_username = DEFAULT_USER;
	char **opt_args = 0;
	struct passwd *pw, pw_copy;


	while (( flag = getopt ( argc, argv, "c:lmps:" )) != -1 ) {
		switch ( flag ) {
		case 'c':
			opt_command = optarg;
			break;
		case 'm':
		case 'p':
			opt_preserve = 1;
			break;
		case 's':
			opt_shell = optarg;
			break;
		case 'l':
			opt_loginshell = 1;
			break;
		default:
			bb_show_usage( );
			break;
		}
	}

	if (( optind < argc ) && ( argv [optind][0] == '-' ) && ( argv [optind][1] == 0 )) {
		opt_loginshell = 1;
		++optind;
    }

	/* get user if specified */
	if ( optind < argc ) 
		opt_username = argv [optind++];

	if ( optind < argc )
		opt_args = argv + optind;
		
		
	pw = getpwnam ( opt_username );
	if ( !pw )
		bb_error_msg_and_die ( "user %s does not exist", opt_username );
		
	/* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
	   is a username that is retrieved via NIS (YP), but that doesn't have
	   a default shell listed.  */
	if ( !pw-> pw_shell || !pw->pw_shell [0] )
		pw-> pw_shell = (char *) DEFAULT_SHELL;

	/* Make a copy of the password information and point pw at the local
	   copy instead.  Otherwise, some systems (e.g. Linux) would clobber
	   the static data through the getlogin call from log_su.  */
	pw_copy = *pw;
	pw = &pw_copy;
	pw-> pw_name  = bb_xstrdup ( pw-> pw_name );
	pw-> pw_dir   = bb_xstrdup ( pw-> pw_dir );
	pw-> pw_shell = bb_xstrdup ( pw-> pw_shell );

	if (( getuid ( ) == 0 ) || correct_password ( pw )) 
		log_su ( pw, 1 );
	else {
		log_su ( pw, 0 );
		bb_error_msg_and_die ( "incorrect password" );
	}

	if ( !opt_shell && opt_preserve )
		opt_shell = getenv ( "SHELL" );

	if ( opt_shell && getuid ( ) && restricted_shell ( pw-> pw_shell ))
	{
		/* The user being su'd to has a nonstandard shell, and so is
		   probably a uucp account or has restricted access.  Don't
		   compromise the account by allowing access with a standard
		   shell.  */
		fputs ( "using restricted shell\n", stderr );
		opt_shell = 0;
	}

	if ( !opt_shell )
		opt_shell = bb_xstrdup ( pw-> pw_shell );

	change_identity ( pw );	
	setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw );
	run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args );
	
	return EXIT_FAILURE;
}