10 #include <sys/types.h>
14 void usagemessage(void) {
15 if (fputs("usage: really [<user-option>] [<group-option> ...] [--]"
16 " [<command> [<argument/option> ...]]\n"
18 " if no options given, set the uid to 0;\n"
19 " -u|--user <username> also sets their default group list\n"
20 " -i|--useronly <username> } set the uid\n"
21 " -I|--uidonly <uid> } but inherits the group list\n"
23 " -z|--groupsclear only groups specified are to be used\n"
24 " -g|--group <groupname> } add this to\n"
25 " -G|--gid <gid> } the group list\n",
26 stderr) == EOF) { perror("write usage"); exit(-1); }
29 static const char *opt_user, *opt_useronly;
30 static int opt_groupsclear= 0, opt_ngids= 0, opt_uidonly= -1;
31 static int opt_gids[512];
33 static void af_group(const struct cmdinfo *cip, const char *value) {
36 if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
37 badusage("too many groups specified");
39 if (!gr) { fprintf(stderr,"unknown group `%s'\n",value); exit(-1); }
40 opt_gids[opt_ngids++]= gr->gr_gid;
43 static void af_gid(const struct cmdinfo *cip, const char *value) {
47 if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
48 badusage("too many gids specified");
49 ul= strtoul(value,&ep,0);
50 if ((*ep) || (uid_t)ul != ul || ul>INT_MAX) badusage("bad gid `%s'",value);
51 opt_gids[opt_ngids++]= ul;
54 static void af_help(const struct cmdinfo *cip, const char *value) {
55 usagemessage(); exit(0);
58 static const struct cmdinfo cmdinfos[]= {
59 { "user", 'u', 1, 0, &opt_user, 0, },
60 { "useronly", 'i', 1, 0, &opt_useronly, 0 },
61 { "uidonly", 'I', 1, &opt_uidonly, 0, 0 },
62 { "groupsclear", 'z', 0, &opt_groupsclear, 0, 0, 1 },
63 { "group", 'g', 1, 0, 0, af_group },
64 { "gid", 'G', 1, 0, 0, af_gid },
65 { "help", 'h', 0, 0, 0, af_help },
69 #ifdef REALLY_CHECK_FILE
70 static void checkroot(void) {
72 r= access(REALLY_CHECK_FILE,W_OK);
73 if (r) { perror("sorry"); exit(-1); }
76 #ifdef REALLY_CHECK_GID
77 static void checkroot(void) {
81 r= getgid(); if (r==REALLY_CHECK_GID) return;
82 if (r<0) { perror("getgid check"); exit(-1); }
83 r= getgroups(sizeof(groups)/sizeof(groups[0]),groups);
84 if (r<0) { perror("getgroups check"); exit(-1); }
86 if (groups[i] == REALLY_CHECK_GID) return;
87 fputs("sorry\n",stderr); exit(-1);
90 #ifdef REALLY_CHECK_NONE
91 static void checkroot(void) {
95 int main(int argc, const char *const *argv) {
98 int i, j, ngroups, ngroups_in, maingid, orgmaingid, mainuid, orgmainuid, r;
102 myopt(&argv,cmdinfos);
104 if (opt_groupsclear && !opt_ngids)
105 badusage("-z|--groupsclear must be accompanied by some groups");
106 if (opt_user && (opt_useronly || opt_uidonly!=-1))
107 badusage("-u|--user may not be used with -i|--useronly or -I|--uidonly");
108 if (opt_user && opt_groupsclear)
109 badusage("-u|--user may not be used with -z|--groupsclear");
110 if (opt_uidonly != -1 && (uid_t)opt_uidonly != opt_uidonly)
111 badusage("-I|--uidonly value %d is out of range for a uid",opt_uidonly);
113 if (!opt_user && !opt_useronly && opt_uidonly==-1 && !opt_ngids) {
116 if (opt_user || opt_useronly) {
117 cp= opt_user ? opt_user : opt_useronly;
119 if (!pw) { fprintf(stderr,"unknown user `%s'\n",cp); exit(-1); }
120 opt_uidonly= pw->pw_uid;
122 orgmaingid= getgid();
123 orgmainuid= getuid();
124 if (orgmaingid<0) { perror("getgid failed"); exit(-1); }
126 r= initgroups(opt_user,pw->pw_gid);
127 if (r) { perror("initgroups failed"); exit(-1); }
132 if (opt_groupsclear) {
134 if (opt_ngids > sizeof(groups)/sizeof(groups[0])) {
135 fputs("too many groups to set\n",stderr);
139 ngroups= getgroups(0,0);
140 if (ngroups<0) { perror("getgroups(0,0) failed"); exit(-1); }
141 if (ngroups+opt_ngids > sizeof(groups)/sizeof(groups[0])) {
142 fputs("too many groups already set for total to fit\n",stderr);
145 ngroups= getgroups(ngroups,groups);
146 if (ngroups<0) { perror("getgroups failed"); exit(-1); }
149 maingid= opt_gids[0];
151 if (opt_ngids || opt_groupsclear) {
152 ngroups_in= ngroups; ngroups= 0;
153 for (i=0; i<ngroups_in; i++) {
154 for (j=0; j<ngroups && groups[j] != groups[i]; j++);
155 if (j<ngroups) continue;
156 groups[ngroups++]= groups[i];
158 for (i=0; i<opt_ngids; i++) {
159 for (j=0; j<ngroups && groups[j] != opt_gids[i]; j++);
160 if (j<ngroups) continue;
161 groups[ngroups++]= opt_gids[i];
163 r= setgroups(ngroups,groups);
164 if (r) { perror("setgroups failed"); exit(-1); }
167 r= setgid(maingid); if (r) { perror("setgid failed"); exit(-1); }
168 r= setgid(maingid); if (r) { perror("2nd setgid failed"); exit(-1); }
170 if (opt_uidonly != -1) {
171 mainuid= opt_uidonly;
175 r= setuid(mainuid); if (r) { perror("setuid failed"); exit(-1); }
176 r= setuid(mainuid); if (r) { perror("2nd setuid failed"); exit(-1); }
178 r= seteuid(0); if (r>=0) { fputs("could seteuid 0",stderr); exit(-1); }
179 if (errno != EPERM) {
180 perror("unexpected failure mode for seteuid 0"); exit(-1);
183 r= getuid(); if (r<0) { perror("getuid failed"); exit(-1); }
184 if (r != mainuid) { fputs("getuid mismatch",stderr); exit(-1); }
185 r= geteuid(); if (r<0) { perror("geteuid failed"); exit(-1); }
186 if (r != mainuid) { fputs("geteuid mismatch",stderr); exit(-1); }
188 for (i=0; i<ngroups && maingid != groups[i]; i++);
189 if (i>=ngroups && maingid != orgmaingid) {
190 r= setgid(orgmaingid);
191 if (r>=0) { fputs("could setgid back",stderr); exit(-1); }
192 if (errno != EPERM) {
193 perror("unexpected failure mode for setgid back"); exit(-1);
196 r= getgid(); if (r<0) { perror("getgid failed"); exit(-1); }
197 if (r != maingid) { fputs("getgid mismatch",stderr); exit(-1); }
198 r= getegid(); if (r<0) { perror("getegid failed"); exit(-1); }
199 if (r != maingid) { fputs("getegid mismatch",stderr); exit(-1); }
204 execlp(cp,cp,"-i",(const char*)0);
206 execvp(argv[0],(char**)argv);
208 perror("exec failed");