1 /* 2 ** 3 ** Copyright 2008, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 #define LOG_TAG "su" 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sys/types.h> 24 #include <dirent.h> 25 #include <errno.h> 26 27 #include <unistd.h> 28 #include <time.h> 29 30 #include <pwd.h> 31 32 #include <private/android_filesystem_config.h> 33 34 35 void pwtoid(const char *tok, uid_t *uid, gid_t *gid) 36 { 37 struct passwd *pw; 38 pw = getpwnam(tok); 39 if (pw) { 40 if (uid) *uid = pw->pw_uid; 41 if (gid) *gid = pw->pw_gid; 42 } else { 43 uid_t tmpid = atoi(tok); 44 if (uid) *uid = tmpid; 45 if (gid) *gid = tmpid; 46 } 47 } 48 49 void extract_uidgids(const char *uidgids, uid_t *uid, gid_t *gid, gid_t *gids, 50 int *gids_count) 51 { 52 char *clobberablegids; 53 char *nexttok; 54 char *tok; 55 int gids_found; 56 57 if (!uidgids || !*uidgids) { 58 *gid = *uid = 0; 59 *gids_count = 0; 60 return; 61 } 62 clobberablegids = strdup(uidgids); 63 strcpy(clobberablegids, uidgids); 64 nexttok = clobberablegids; 65 tok = strsep(&nexttok, ","); 66 pwtoid(tok, uid, gid); 67 tok = strsep(&nexttok, ","); 68 if (!tok) { 69 /* gid is already set above */ 70 *gids_count = 0; 71 free(clobberablegids); 72 return; 73 } 74 pwtoid(tok, NULL, gid); 75 gids_found = 0; 76 while ((gids_found < *gids_count) && (tok = strsep(&nexttok, ","))) { 77 pwtoid(tok, NULL, gids); 78 gids_found++; 79 gids++; 80 } 81 if (nexttok && gids_found == *gids_count) { 82 fprintf(stderr, "too many group ids\n"); 83 } 84 *gids_count = gids_found; 85 free(clobberablegids); 86 } 87 88 /* 89 * SU can be given a specific command to exec. UID _must_ be 90 * specified for this (ie argc => 3). 91 * 92 * Usage: 93 * su 1000 94 * su 1000 ls -l 95 * or 96 * su [uid[,gid[,group1]...] [cmd]] 97 * E.g. 98 * su 1000,shell,net_bw_acct,net_bw_stats id 99 * will return 100 * uid=1000(system) gid=2000(shell) groups=3006(net_bw_stats),3007(net_bw_acct) 101 */ 102 int main(int argc, char **argv) 103 { 104 struct passwd *pw; 105 uid_t uid, myuid; 106 gid_t gid, gids[10]; 107 108 /* Until we have something better, only root and the shell can use su. */ 109 myuid = getuid(); 110 if (myuid != AID_ROOT && myuid != AID_SHELL) { 111 fprintf(stderr,"su: uid %d not allowed to su\n", myuid); 112 return 1; 113 } 114 115 if(argc < 2) { 116 uid = gid = 0; 117 } else { 118 int gids_count = sizeof(gids)/sizeof(gids[0]); 119 extract_uidgids(argv[1], &uid, &gid, gids, &gids_count); 120 if(gids_count) { 121 if(setgroups(gids_count, gids)) { 122 fprintf(stderr, "su: failed to set groups\n"); 123 return 1; 124 } 125 } 126 } 127 128 if(setgid(gid) || setuid(uid)) { 129 fprintf(stderr,"su: permission denied\n"); 130 return 1; 131 } 132 133 /* User specified command for exec. */ 134 if (argc == 3 ) { 135 if (execlp(argv[2], argv[2], NULL) < 0) { 136 int saved_errno = errno; 137 fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], 138 strerror(errno)); 139 return -saved_errno; 140 } 141 } else if (argc > 3) { 142 /* Copy the rest of the args from main. */ 143 char *exec_args[argc - 1]; 144 memset(exec_args, 0, sizeof(exec_args)); 145 memcpy(exec_args, &argv[2], sizeof(exec_args)); 146 if (execvp(argv[2], exec_args) < 0) { 147 int saved_errno = errno; 148 fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], 149 strerror(errno)); 150 return -saved_errno; 151 } 152 } 153 154 /* Default exec shell. */ 155 execlp("/system/bin/sh", "sh", NULL); 156 157 fprintf(stderr, "su: exec failed\n"); 158 return 1; 159 } 160