summaryrefslogtreecommitdiff
path: root/su
diff options
context:
space:
mode:
authorJP Abgrall <jpa@google.com>2013-01-10 17:04:42 -0800
committerJP Abgrall <jpa@google.com>2013-01-10 17:04:42 -0800
commitd198c429f50e1cad98e989d6212508adb45d7919 (patch)
treedf20b2137607427c449c65df7a12400613f35bf8 /su
parentba120713397023ab9ddee07eb581f4d1083dbae8 (diff)
downloadextras-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.c87
1 files changed, 75 insertions, 12 deletions
diff --git a/su/su.c b/su/su.c
index f87f0736..8365379f 100644
--- a/su/su.c
+++ b/su/su.c
@@ -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;
}
}