1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <errno.h> 18#include <error.h> 19#include <getopt.h> 20#include <paths.h> 21#include <pwd.h> 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include <unistd.h> 26 27#include <private/android_filesystem_config.h> 28 29void pwtoid(const char* tok, uid_t* uid, gid_t* gid) { 30 struct passwd* pw = getpwnam(tok); 31 if (pw) { 32 if (uid) *uid = pw->pw_uid; 33 if (gid) *gid = pw->pw_gid; 34 } else { 35 char* end; 36 errno = 0; 37 uid_t tmpid = strtoul(tok, &end, 10); 38 if (errno != 0 || end == tok) error(1, errno, "invalid uid/gid '%s'", tok); 39 if (uid) *uid = tmpid; 40 if (gid) *gid = tmpid; 41 } 42} 43 44void extract_uidgids(const char* uidgids, uid_t* uid, gid_t* gid, gid_t* gids, int* gids_count) { 45 char *clobberablegids; 46 char *nexttok; 47 char *tok; 48 int gids_found; 49 50 if (!uidgids || !*uidgids) { 51 *gid = *uid = 0; 52 *gids_count = 0; 53 return; 54 } 55 56 clobberablegids = strdup(uidgids); 57 strcpy(clobberablegids, uidgids); 58 nexttok = clobberablegids; 59 tok = strsep(&nexttok, ","); 60 pwtoid(tok, uid, gid); 61 tok = strsep(&nexttok, ","); 62 if (!tok) { 63 /* gid is already set above */ 64 *gids_count = 0; 65 free(clobberablegids); 66 return; 67 } 68 pwtoid(tok, NULL, gid); 69 gids_found = 0; 70 while ((gids_found < *gids_count) && (tok = strsep(&nexttok, ","))) { 71 pwtoid(tok, NULL, gids); 72 gids_found++; 73 gids++; 74 } 75 if (nexttok && gids_found == *gids_count) { 76 fprintf(stderr, "too many group ids\n"); 77 } 78 *gids_count = gids_found; 79 free(clobberablegids); 80} 81 82int main(int argc, char** argv) { 83 uid_t current_uid = getuid(); 84 if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed"); 85 86 // Handle -h and --help. 87 ++argv; 88 if (*argv && (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0)) { 89 fprintf(stderr, 90 "usage: su [UID[,GID[,GID2]...]] [COMMAND [ARG...]]\n" 91 "\n" 92 "Switch to WHO (default 'root') and run the given command (default sh).\n" 93 "\n" 94 "where WHO is a comma-separated list of user, group,\n" 95 "and supplementary groups in that order.\n" 96 "\n"); 97 return 0; 98 } 99 100 // The default user is root. 101 uid_t uid = 0; 102 gid_t gid = 0; 103 104 // If there are any arguments, the first argument is the uid/gid/supplementary groups. 105 if (*argv) { 106 gid_t gids[10]; 107 int gids_count = sizeof(gids)/sizeof(gids[0]); 108 extract_uidgids(*argv, &uid, &gid, gids, &gids_count); 109 if (gids_count) { 110 if (setgroups(gids_count, gids)) { 111 error(1, errno, "setgroups failed"); 112 } 113 } 114 ++argv; 115 } 116 117 if (setgid(gid)) error(1, errno, "setgid failed"); 118 if (setuid(uid)) error(1, errno, "setuid failed"); 119 120 // Reset parts of the environment. 121 setenv("PATH", _PATH_DEFPATH, 1); 122 unsetenv("IFS"); 123 struct passwd* pw = getpwuid(uid); 124 if (pw) { 125 setenv("LOGNAME", pw->pw_name, 1); 126 setenv("USER", pw->pw_name, 1); 127 } else { 128 unsetenv("LOGNAME"); 129 unsetenv("USER"); 130 } 131 132 // Set up the arguments for exec. 133 char* exec_args[argc + 1]; // Having too much space is fine. 134 size_t i = 0; 135 for (; *argv != NULL; ++i) { 136 exec_args[i] = *argv++; 137 } 138 // Default to the standard shell. 139 if (i == 0) exec_args[i++] = const_cast<char*>("/system/bin/sh"); 140 exec_args[i] = NULL; 141 142 execvp(exec_args[0], exec_args); 143 error(1, errno, "failed to exec %s", exec_args[0]); 144} 145