summaryrefslogtreecommitdiff
path: root/loginutils/su.c
blob: ec0c16c7dc726177e9200a66e41e0c85c5086dea (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
/* 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_USER  "root"

//#define SYSLOG_SUCCESS
#define SYSLOG_FAILURE


#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
/* Log the fact that someone has run su */

# if defined( SYSLOG_SUCCESS ) && defined( SYSLOG_FAILURE )
static void log_su (const char *successful, const char *old_user, const char *tty)
{
	syslog ( LOG_NOTICE, "%s%s on %s", successful, old_user, tty);
}
#  define log_su_successful(cu, u, tty) if(!cu) log_su("", u, tty)
#  define log_su_failure(cu, u, tty)    if(!cu) log_su("FAILED SU ", u, tty)
# else
	/* partial logging */
#  if !defined( SYSLOG_SUCESS )
#   define log_su_successful(cu, u, tty)
#   define log_su_failure(cu, u, t) if(!cu) \
			syslog(LOG_NOTICE, "FAILED SU %s on %s", u, t)
#  else
#   define log_su_successful(cu, u, t) if(!cu) \
			syslog(LOG_NOTICE, "%s on %s", u, t)
#   define log_su_failure(cu, u, tty)
#  endif
# endif
#else
	/* logging not used */
# define log_su_successful(cu, u, tty)
# define log_su_failure(cu, u, tty)
#endif


int su_main ( int argc, char **argv )
{
	unsigned long flags;
	int opt_preserve;
	int opt_loginshell;
	char *opt_shell = 0;
	char *opt_command = 0;
	char *opt_username = DEFAULT_USER;
	char **opt_args = 0;
	struct passwd *pw;
	uid_t cur_uid = getuid();

#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
	const char *tty;
	const char *old_user;
#endif

	flags = bb_getopt_ulflags(argc, argv, "mplc:s:",
						  &opt_command, &opt_shell);
	opt_preserve = flags & 3;
	opt_loginshell = (flags & 4 ? 1 : 0);

	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;

#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
#ifdef CONFIG_FEATURE_U_W_TMP
	/* 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 )
#endif
		{
		/* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid.  */
		pw = getpwuid ( cur_uid );
		old_user = ( pw ? pw->pw_name : "" );
	}
	tty = ttyname ( 2 );
	if(!tty)
		tty = "none";

	openlog ( bb_applet_name, 0, LOG_AUTH );
#endif

	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;

	if ((( cur_uid == 0 ) || correct_password ( pw ))) {
		log_su_successful(pw->pw_uid, old_user, tty );
	} else {
		log_su_failure (pw->pw_uid, old_user, tty );
		bb_error_msg_and_die ( "incorrect password" );
	}

#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
	closelog();
#endif

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

	if ( opt_shell && cur_uid && 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 = 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
#ifdef CONFIG_SELINUX
	, 0
#endif
	);

	return EXIT_FAILURE;
}