Home | History | Annotate | Download | only in su
      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