summaryrefslogtreecommitdiff
path: root/su
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2015-03-17 21:19:25 -0700
committerElliott Hughes <enh@google.com>2015-03-17 21:24:18 -0700
commit48049dde30bc1170670045517a79c80c81a5a0f0 (patch)
treebfd5347afc72f36e6861c12a2bb9b93ff8333faa /su
parentf1380e5ba41393dd2fd605692a0cd207782418b6 (diff)
downloadextras-48049dde30bc1170670045517a79c80c81a5a0f0.tar.gz
su should reset PATH, IFS, LOGNAME, and USER.
Also add -h and --help while we're here (since all the toybox commands carry their own documentation, and our su isn't much like the real su). Bug: 19647373 Bug: 19564110 Change-Id: If2e6117b6701dcac03b5831f251e60a272bf0d61
Diffstat (limited to 'su')
-rw-r--r--su/su.c59
1 files changed, 38 insertions, 21 deletions
diff --git a/su/su.c b/su/su.c
index 9e3144de..af971737 100644
--- a/su/su.c
+++ b/su/su.c
@@ -16,7 +16,10 @@
#include <errno.h>
#include <error.h>
+#include <getopt.h>
+#include <paths.h>
#include <pwd.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -77,35 +80,46 @@ void extract_uidgids(const char* uidgids, uid_t* uid, gid_t* gid, gid_t* gids, i
free(clobberablegids);
}
-/*
- * SU can be given a specific command to exec. UID _must_ be
- * specified for this.
- *
- * Usage:
- * su 1000
- * su 1000 ls -l
- * or
- * su [uid[,gid[,group1]...] [cmd]]
- * E.g.
- * su 1000,shell,net_bw_acct,net_bw_stats id
- * will return
- * uid=1000(system) gid=2000(shell) groups=3006(net_bw_stats),3007(net_bw_acct)
- */
int main(int argc, char** argv) {
uid_t current_uid = getuid();
if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed");
+ // Handle -h and --help.
+ while (true) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { 0, 0, 0, 0 },
+ };
+
+ int c = getopt_long(argc, argv, "h", long_options, &option_index);
+ if (c == -1) break;
+ switch (c) {
+ case 'h':
+ default:
+ fprintf(stderr,
+ "usage: su [UID[,GID[,GID2]...]] [COMMAND [ARG...]]\n"
+ "\n"
+ "Switch to WHO (default 'root') and run the given command (default sh).\n"
+ "\n"
+ "where WHO is a comma-separated list of user, group,\n"
+ "and supplementary groups in that order.\n"
+ "\n");
+ return 0;
+ }
+ }
+ // Bump argv to the first non-option argument.
+ argv += optind;
+
// The default user is root.
uid_t uid = 0;
gid_t gid = 0;
- // TODO: use getopt and support at least -- and --help.
-
// If there are any arguments, the first argument is the uid/gid/supplementary groups.
- if (argc >= 2) {
+ if (*argv) {
gid_t gids[10];
int gids_count = sizeof(gids)/sizeof(gids[0]);
- extract_uidgids(argv[1], &uid, &gid, gids, &gids_count);
+ extract_uidgids(*argv, &uid, &gid, gids, &gids_count);
if (gids_count) {
if (setgroups(gids_count, gids)) {
error(1, errno, "setgroups failed");
@@ -117,12 +131,15 @@ int main(int argc, char** argv) {
if (setgid(gid)) error(1, errno, "setgid failed");
if (setuid(uid)) error(1, errno, "setuid failed");
- // TODO: reset $PATH.
+ // Reset parts of the environment.
+ setenv("PATH", _PATH_DEFPATH, 1);
+ unsetenv("IFS");
+ struct passwd* pw = getpwuid(uid);
+ setenv("LOGNAME", pw->pw_name, 1);
+ setenv("USER", pw->pw_name, 1);
// Set up the arguments for exec.
char* exec_args[argc + 1]; // Having too much space is fine.
- // Skip "su" and copy any other args. We already skipped the optional uid above.
- ++argv;
size_t i = 0;
for (; *argv != NULL; ++i) {
exec_args[i] = *argv++;