diff options
author | JP Abgrall <jpa@google.com> | 2013-01-10 17:04:42 -0800 |
---|---|---|
committer | JP Abgrall <jpa@google.com> | 2013-01-10 17:04:42 -0800 |
commit | d198c429f50e1cad98e989d6212508adb45d7919 (patch) | |
tree | df20b2137607427c449c65df7a12400613f35bf8 /su | |
parent | ba120713397023ab9ddee07eb581f4d1083dbae8 (diff) | |
download | extras-d198c429f50e1cad98e989d6212508adb45d7919.tar.gz |
su: support fine grained setting of gid and groups
To allow some tests to quickly check if some group membership handling
are correctly done, the tests need to run as a proc belonging to multiple
groups.
su would normally only allow setting uid=gid=something.
This changes adds setting the gid separately from the uid and setting the
groups:
su [uid[,gid[,group1]...]] [cmd]]
E.g.
su
su shell ls /data
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)
Change-Id: I713454bf14c3bc3ece93aec3729bda102542d1b1
Diffstat (limited to 'su')
-rw-r--r-- | su/su.c | 87 |
1 files changed, 75 insertions, 12 deletions
@@ -31,18 +31,79 @@ #include <private/android_filesystem_config.h> + +void pwtoid(const char *tok, uid_t *uid, gid_t *gid) +{ + struct passwd *pw; + pw = getpwnam(tok); + if (pw) { + if (uid) *uid = pw->pw_uid; + if (gid) *gid = pw->pw_gid; + } else { + uid_t tmpid = atoi(tok); + if (uid) *uid = tmpid; + if (gid) *gid = tmpid; + } +} + +void extract_uidgids(const char *uidgids, uid_t *uid, gid_t *gid, gid_t *gids, + int *gids_count) +{ + char *clobberablegids; + char *nexttok; + char *tok; + int gids_found; + + if (!uidgids || !*uidgids) { + *gid = *uid = 0; + *gids_count = 0; + return; + } + clobberablegids = strdup(uidgids); + strcpy(clobberablegids, uidgids); + nexttok = clobberablegids; + tok = strsep(&nexttok, ","); + pwtoid(tok, uid, gid); + tok = strsep(&nexttok, ","); + if (!tok) { + /* gid is already set above */ + *gids_count = 0; + free(clobberablegids); + return; + } + pwtoid(tok, NULL, gid); + gids_found = 0; + while ((gids_found < *gids_count) && (tok = strsep(&nexttok, ","))) { + pwtoid(tok, NULL, gids); + gids_found++; + gids++; + } + if (nexttok && gids_found == *gids_count) { + fprintf(stderr, "too many group ids\n"); + } + *gids_count = gids_found; + free(clobberablegids); +} + /* * SU can be given a specific command to exec. UID _must_ be * specified for this (ie argc => 3). * * Usage: - * su 1000 - * su 1000 ls -l + * 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) { struct passwd *pw; - int uid, gid, myuid; + uid_t uid, myuid; + gid_t gid, gids[10]; /* Until we have something better, only root and the shell can use su. */ myuid = getuid(); @@ -54,13 +115,13 @@ int main(int argc, char **argv) if(argc < 2) { uid = gid = 0; } else { - pw = getpwnam(argv[1]); - - if(pw == 0) { - uid = gid = atoi(argv[1]); - } else { - uid = pw->pw_uid; - gid = pw->pw_gid; + int gids_count = sizeof(gids)/sizeof(gids[0]); + extract_uidgids(argv[1], &uid, &gid, gids, &gids_count); + if(gids_count) { + if(setgroups(gids_count, gids)) { + fprintf(stderr, "su: failed to set groups\n"); + return 1; + } } } @@ -72,9 +133,10 @@ int main(int argc, char **argv) /* User specified command for exec. */ if (argc == 3 ) { if (execlp(argv[2], argv[2], NULL) < 0) { + int saved_errno = errno; fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], strerror(errno)); - return -errno; + return -saved_errno; } } else if (argc > 3) { /* Copy the rest of the args from main. */ @@ -82,9 +144,10 @@ int main(int argc, char **argv) memset(exec_args, 0, sizeof(exec_args)); memcpy(exec_args, &argv[2], sizeof(exec_args)); if (execvp(argv[2], exec_args) < 0) { + int saved_errno = errno; fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], strerror(errno)); - return -errno; + return -saved_errno; } } |