2 * really.c - program for gaining privilege
4 * Copyright (C) 1992-3 Ian Jackson <ian@davenant.greenend.org.uk>
6 * This is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3,
9 * or (at your option) any later version.
11 * This is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this file; if not, consult the Free Software
18 * Foundation's website at www.fsf.org, or the GNU Project website at
28 #include <sys/types.h>
33 void usagemessage(void) {
34 if (fputs("usage: really [<really-option> ...] [--]"
35 " [<command> [<argument/option> ...]]\n"
36 "really-options specifying the user:\n"
37 " if no options given, set the uid to 0;\n"
38 " -u|--user <username> also sets their default group list\n"
39 " -i|--useronly <username> } set the uid\n"
40 " -I|--uidonly <uid> } but inherits the group list\n"
41 "really-options specifying the group:\n"
42 " -z|--groupsclear only groups specified are to be used\n"
43 " -g|--group <groupname> } add this to\n"
44 " -G|--gid <gid> } the group list\n"
45 "other really-options:\n"
46 " -h|--help display this message\n"
47 " -R|--chroot <dir> chroot (but *not* chdir - danger!)\n",
48 stderr) == EOF) { perror("write usage"); exit(-1); }
51 static const char *opt_user, *opt_useronly, *opt_chroot;
52 static int opt_groupsclear= 0, opt_ngids= 0, opt_uidonly= -1;
53 static int opt_gids[512];
55 static void af_uidonly(const struct cmdinfo *cip, const char *value) {
59 ul= strtoul(value,&ep,10);
60 if (*ep) { fprintf(stderr,"bad uid `%s'\n",value); exit(-1); }
64 static void af_group(const struct cmdinfo *cip, const char *value) {
67 if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
68 badusage("too many groups specified");
70 if (!gr) { fprintf(stderr,"unknown group `%s'\n",value); exit(-1); }
71 opt_gids[opt_ngids++]= gr->gr_gid;
74 static void af_gid(const struct cmdinfo *cip, const char *value) {
78 if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
79 badusage("too many gids specified");
80 ul= strtoul(value,&ep,0);
81 if ((*ep) || (uid_t)ul != ul || ul>INT_MAX) badusage("bad gid `%s'",value);
82 opt_gids[opt_ngids++]= ul;
85 static void af_help(const struct cmdinfo *cip, const char *value) {
86 usagemessage(); exit(0);
89 static const struct cmdinfo cmdinfos[]= {
90 { "user", 'u', 1, 0, &opt_user, 0, },
91 { "useronly", 'i', 1, 0, &opt_useronly, 0 },
92 { "uidonly", 'I', 1, 0, 0, af_uidonly },
93 { "groupsclear", 'z', 0, &opt_groupsclear, 0, 0, 1 },
94 { "group", 'g', 1, 0, 0, af_group },
95 { "gid", 'G', 1, 0, 0, af_gid },
96 { "chroot", 'R', 1, 0, &opt_chroot, 0 },
97 { "help", 'h', 0, 0, 0, af_help },
101 #ifdef REALLY_CHECK_FILE
102 static int checkroot(void) {
104 r= access(REALLY_CHECK_FILE, W_OK);
106 #ifdef REALLY_CHECK_FILE_2
107 r= access(REALLY_CHECK_FILE_2, W_OK);
109 /* If all fails we return the errno from file _2 */
110 #endif /*REALLY_CHECK_FILE_2*/
114 #ifdef REALLY_CHECK_GID
115 static int checkroot(void) {
119 r= getgid(); if (r==REALLY_CHECK_GID) return 0;
120 if (r<0) { perror("getgid check"); exit(-1); }
121 r= getgroups(sizeof(groups)/sizeof(groups[0]),groups);
122 if (r<0) { perror("getgroups check"); exit(-1); }
124 if (groups[i] == REALLY_CHECK_GID) return 0;
128 #ifdef REALLY_CHECK_NONE
129 static int checkroot(void) {
134 int main(int argc, const char *const *argv) {
135 struct passwd *pw= 0;
137 int i, j, ngroups, ngroups_in, maingid, orgmaingid, mainuid, orgmainuid, r;
140 orgmainuid= getuid();
141 if (orgmainuid && checkroot()) { perror("sorry"); exit(-1); }
142 myopt(&argv,cmdinfos);
144 if (opt_groupsclear && !opt_ngids)
145 badusage("-z|--groupsclear must be accompanied by some groups");
146 if (opt_user && (opt_useronly || opt_uidonly!=-1))
147 badusage("-u|--user may not be used with -i|--useronly or -I|--uidonly");
148 if (opt_user && opt_groupsclear)
149 badusage("-u|--user may not be used with -z|--groupsclear");
150 if (opt_uidonly != -1 && (uid_t)opt_uidonly != opt_uidonly)
151 badusage("-I|--uidonly value %d is out of range for a uid",opt_uidonly);
153 if (!opt_user && !opt_useronly && opt_uidonly==-1 && !opt_ngids) {
156 if (opt_user || opt_useronly) {
157 cp= opt_user ? opt_user : opt_useronly;
159 if (!pw) { fprintf(stderr,"unknown user `%s'\n",cp); exit(-1); }
160 opt_uidonly= pw->pw_uid;
163 if (chroot(opt_chroot)) { perror("chroot failed"); exit(-1); }
165 orgmaingid= getgid();
166 if (orgmaingid<0) { perror("getgid failed"); exit(-1); }
168 r= initgroups(opt_user,pw->pw_gid);
169 if (r) { perror("initgroups failed"); exit(-1); }
174 if (opt_groupsclear) {
176 if (opt_ngids > sizeof(groups)/sizeof(groups[0])) {
177 fputs("too many groups to set\n",stderr);
181 ngroups= getgroups(0,0);
182 if (ngroups<0) { perror("getgroups(0,0) failed"); exit(-1); }
183 if (ngroups+opt_ngids > sizeof(groups)/sizeof(groups[0])) {
184 fputs("too many groups already set for total to fit\n",stderr);
187 ngroups= getgroups(ngroups,groups);
188 if (ngroups<0) { perror("getgroups failed"); exit(-1); }
191 maingid= opt_gids[0];
193 if (opt_ngids || opt_groupsclear) {
194 ngroups_in= ngroups; ngroups= 0;
195 for (i=0; i<ngroups_in; i++) {
196 for (j=0; j<ngroups && groups[j] != groups[i]; j++);
197 if (j<ngroups) continue;
198 groups[ngroups++]= groups[i];
200 for (i=0; i<opt_ngids; i++) {
201 for (j=0; j<ngroups && groups[j] != opt_gids[i]; j++);
202 if (j<ngroups) continue;
203 groups[ngroups++]= opt_gids[i];
205 r= setgroups(ngroups,groups);
206 if (r) { perror("setgroups failed"); exit(-1); }
209 r= setgid(maingid); if (r) { perror("setgid failed"); exit(-1); }
210 r= setgid(maingid); if (r) { perror("2nd setgid failed"); exit(-1); }
212 if (opt_uidonly != -1) {
213 mainuid= opt_uidonly;
217 r= setuid(mainuid); if (r) { perror("setuid failed"); exit(-1); }
218 r= setuid(mainuid); if (r) { perror("2nd setuid failed"); exit(-1); }
220 r= seteuid(0); if (r>=0) { fputs("could seteuid 0",stderr); exit(-1); }
221 if (errno != EPERM) {
222 perror("unexpected failure mode for seteuid 0"); exit(-1);
225 r= getuid(); if (r<0) { perror("getuid failed"); exit(-1); }
226 if (r != mainuid) { fputs("getuid mismatch",stderr); exit(-1); }
227 r= geteuid(); if (r<0) { perror("geteuid failed"); exit(-1); }
228 if (r != mainuid) { fputs("geteuid mismatch",stderr); exit(-1); }
230 for (i=0; i<ngroups && maingid != groups[i]; i++);
231 if (i>=ngroups && maingid != orgmaingid) {
232 r= setgid(orgmaingid);
233 if (r>=0) { fputs("could setgid back",stderr); exit(-1); }
234 if (errno != EPERM) {
235 perror("unexpected failure mode for setgid back"); exit(-1);
238 r= getgid(); if (r<0) { perror("getgid failed"); exit(-1); }
239 if (r != maingid) { fputs("getgid mismatch",stderr); exit(-1); }
240 r= getegid(); if (r<0) { perror("getegid failed"); exit(-1); }
241 if (r != maingid) { fputs("getegid mismatch",stderr); exit(-1); }
246 execlp(cp,cp,"-i",(const char*)0);
248 execvp(argv[0],(char**)argv);
250 perror("exec failed");