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 <stdbool.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <unistd.h> 27 28#include <private/android_filesystem_config.h> 29 30void pwtoid(const char* tok, uid_t* uid, gid_t* gid) { 31 struct passwd* pw = getpwnam(tok); 32 if (pw) { 33 if (uid) *uid = pw->pw_uid; 34 if (gid) *gid = pw->pw_gid; 35 } else { 36 char* end; 37 errno = 0; 38 uid_t tmpid = strtoul(tok, &end, 10); 39 if (errno != 0 || end == tok) error(1, errno, "invalid uid/gid '%s'", tok); 40 if (uid) *uid = tmpid; 41 if (gid) *gid = tmpid; 42 } 43} 44 45void extract_uidgids(const char* uidgids, uid_t* uid, gid_t* gid, gid_t* gids, int* gids_count) { 46 char *clobberablegids; 47 char *nexttok; 48 char *tok; 49 int gids_found; 50 51 if (!uidgids || !*uidgids) { 52 *gid = *uid = 0; 53 *gids_count = 0; 54 return; 55 } 56 57 clobberablegids = strdup(uidgids); 58 strcpy(clobberablegids, uidgids); 59 nexttok = clobberablegids; 60 tok = strsep(&nexttok, ","); 61 pwtoid(tok, uid, gid); 62 tok = strsep(&nexttok, ","); 63 if (!tok) { 64 /* gid is already set above */ 65 *gids_count = 0; 66 free(clobberablegids); 67 return; 68 } 69 pwtoid(tok, NULL, gid); 70 gids_found = 0; 71 while ((gids_found < *gids_count) && (tok = strsep(&nexttok, ","))) { 72 pwtoid(tok, NULL, gids); 73 gids_found++; 74 gids++; 75 } 76 if (nexttok && gids_found == *gids_count) { 77 fprintf(stderr, "too many group ids\n"); 78 } 79 *gids_count = gids_found; 80 free(clobberablegids); 81} 82 83int main(int argc, char** argv) { 84 uid_t current_uid = getuid(); 85 if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed"); 86 87 // Handle -h and --help. 88 ++argv; 89 if (*argv && (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0)) { 90 fprintf(stderr, 91 "usage: su [UID[,GID[,GID2]...]] [COMMAND [ARG...]]\n" 92 "\n" 93 "Switch to WHO (default 'root') and run the given command (default sh).\n" 94 "\n" 95 "where WHO is a comma-separated list of user, group,\n" 96 "and supplementary groups in that order.\n" 97 "\n"); 98 return 0; 99 } 100 101 // The default user is root. 102 uid_t uid = 0; 103 gid_t gid = 0; 104 105 // If there are any arguments, the first argument is the uid/gid/supplementary groups. 106 if (*argv) { 107 gid_t gids[10]; 108 int gids_count = sizeof(gids)/sizeof(gids[0]); 109 extract_uidgids(*argv, &uid, &gid, gids, &gids_count); 110 if (gids_count) { 111 if (setgroups(gids_count, gids)) { 112 error(1, errno, "setgroups failed"); 113 } 114 } 115 ++argv; 116 } 117 118 if (setgid(gid)) error(1, errno, "setgid failed"); 119 if (setuid(uid)) error(1, errno, "setuid failed"); 120 121 // Reset parts of the environment. 122 setenv("PATH", _PATH_DEFPATH, 1); 123 unsetenv("IFS"); 124 struct passwd* pw = getpwuid(uid); 125 setenv("LOGNAME", pw->pw_name, 1); 126 setenv("USER", pw->pw_name, 1); 127 128 // Set up the arguments for exec. 129 char* exec_args[argc + 1]; // Having too much space is fine. 130 size_t i = 0; 131 for (; *argv != NULL; ++i) { 132 exec_args[i] = *argv++; 133 } 134 // Default to the standard shell. 135 if (i == 0) exec_args[i++] = "/system/bin/sh"; 136 exec_args[i] = NULL; 137 138 execvp(exec_args[0], exec_args); 139 error(1, errno, "failed to exec %s", exec_args[0]); 140} 141