summaryrefslogtreecommitdiff
path: root/loginutils/su.c
blob: 1273831ff64fca5bb483cebd829e3219dd1bda20 (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: */
/*
   Licensed under the GPL v2, see the file LICENSE in this tarball.
*/

#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/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.  */
#ifndef DEFAULT_SHELL
#define DEFAULT_SHELL "/bin/sh"
#endif

/* Default user.  */
#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;
	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);
#define SU_OPT_m (3)
#define SU_OPT_p (3)
#define SU_OPT_l (4)

	if (optind < argc  && argv[optind][0] == '-' && argv[optind][1] == 0) {
		flags |= SU_OPT_l;
		++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_UTMP
	/* 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 && (flags & SU_OPT_p))
		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, flags & SU_OPT_l, !(flags & SU_OPT_p), pw);
#if ENABLE_SELINUX
       set_current_security_context(NULL);
#endif
	run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)opt_args);

	return EXIT_FAILURE;
}