chiark / gitweb /
stuff from chiark:/usr/local/src/misc/ (as found)
[chiark-utils.git] / cprogs / really.c~
1 /**/
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <limits.h>
6 #include <unistd.h>
7 #include <pwd.h>
8 #include <grp.h>
9 #include <sys/types.h>
10
11 #include "myopt.h"
12
13 void usagemessage(void) {
14   if (fputs("usage: really [<user-option>] [<group-option> ...] [--]"
15             " [<command> [<argument/option> ...]]\n"
16             "user-options:\n"
17             " if no options given, set the uid to 0;\n"
18             " -u|--user <username>     also sets their default group list\n"
19             " -i|--useronly <username> } set the uid\n"
20             " -I|--uidonly <uid>       }  but inherits the group list\n"
21             "group-options:\n"
22             " -z|--groupsclear         only groups specified are to be used\n"
23             " -g|--group <groupname>   } add this to\n"
24             " -G|--gid <gid>           }  the group list\n",
25             stderr) == EOF) { perror("write usage"); exit(-1); }
26 }
27
28 static const char *opt_user, *opt_useronly;
29 static int opt_groupsclear= 0, opt_ngids= 0, opt_uidonly= -1;
30 static int opt_gids[512];
31
32 static void af_group(const struct cmdinfo *cip, const char *value) {
33   struct group *gr;
34
35   if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
36     badusage("too many groups specified");
37   gr= getgrnam(value);
38   if (!gr) { fprintf(stderr,"unknown group `%s'\n",value); exit(-1); }
39   opt_gids[opt_ngids++]= gr->gr_gid;
40 }
41
42 static void af_gid(const struct cmdinfo *cip, const char *value) {
43   char *ep;
44   unsigned long ul;
45
46   if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
47     badusage("too many gids specified");
48   ul= strtoul(value,&ep,0);
49   if ((*ep) || (uid_t)ul != ul || ul>INT_MAX) badusage("bad gid `%s'",value);
50   opt_gids[opt_ngids++]= ul;
51 }
52
53 static void af_help(const struct cmdinfo *cip, const char *value) {
54   usagemessage(); exit(0);
55 }
56
57 static const struct cmdinfo cmdinfos[]= {
58   { "user",         'u',  1,  0, &opt_user,          0,           },
59   { "useronly",     'i',  1,  0, &opt_useronly,      0            },
60   { "uidonly",      'I',  1,  &opt_uidonly, 0,       0            },
61   { "groupsclear",  'z',  0,  &opt_groupsclear, 0,   0,        1  },
62   { "group",        'g',  1,  0, 0,                  af_group     },
63   { "gid",          'G',  1,  0, 0,                  af_gid       },
64   { "help",         'h',  0,  0, 0,                  af_help      },
65   {  0,              0                                            }
66 };
67
68 #ifdef REALLY_CHECK_FILE
69 void checkroot(void) {
70   int r;
71   r= access(REALLY_CHECK_FILE,W_OK);
72   if (r) { perror("sorry"); exit(-1); }
73 }
74 #endif
75 #ifdef REALLY_CHECK_GID
76 void checkroot(void) {
77   gid_t groups[512];
78   int r;
79
80   r= getgid(); if (r==REALLY_CHECK_GID) return;
81   if (r<0) { perror("getgid check"); exit(-1); }
82   r= getgroups(sizeof(groups)/sizeof(groups[0]),groups);
83   if (r<0) { perror("getgroups check"); exit(-1); }
84   for (i=0; i<r; i++)
85     if (groups[i] == REALLY_CHECK_GID) return;
86   fputs("sorry\n",stderr); exit(-1);
87 }
88 #endif
89 #ifdef REALLY_CHECK_NONE
90 void checkroot(void) {
91 }
92 #endif
93
94 int main(int argc, const char *const *argv) {
95   struct passwd *pw= 0;
96   gid_t groups[512];
97   int i, j, ngroups, ngroups_in, maingid, orgmaingid, mainuid, orgmainuid, r;
98   const char *cp;
99   
100   checkroot();
101   myopt(&argv,cmdinfos);
102
103   if (opt_groupsclear && !opt_ngids)
104     badusage("-z|--groupsclear must be accompanied by some groups");
105   if (opt_user && (opt_useronly || opt_uidonly!=-1))
106     badusage("-u|--user may not be used with -i|--useronly or -I|--uidonly");
107   if (opt_user && opt_groupsclear)
108     badusage("-u|--user may not be used with -z|--groupsclear");
109   if (opt_uidonly != -1 && (uid_t)opt_uidonly != opt_uidonly)
110     badusage("-I|--uidonly value %d is out of range for a uid",opt_uidonly);
111
112   if (!opt_user && !opt_useronly && opt_uidonly==-1 && !opt_ngids) {
113     opt_uidonly= 0;
114   }
115   if (opt_user || opt_useronly) {
116     cp= opt_user ? opt_user : opt_useronly;
117     pw= getpwnam(cp);
118     if (!pw) { fprintf(stderr,"unknown user `%s'\n",cp); exit(-1); }
119     opt_uidonly= pw->pw_uid;
120   }
121   orgmaingid= getgid();
122   orgmainuid= getuid();
123   if (orgmaingid<0) { perror("getgid failed"); exit(-1); }
124   if (opt_user) {
125     r= initgroups(opt_user,pw->pw_gid);
126     if (r) { perror("initgroups failed"); exit(-1); }
127     maingid= pw->pw_gid;
128   } else {
129     maingid= -1;
130   }
131   if (opt_groupsclear) {
132     ngroups= 0;
133     if (opt_ngids > sizeof(groups)/sizeof(groups[0])) {
134       fputs("too many groups to set\n",stderr);
135       exit(-1);
136     }
137   } else {
138     ngroups= getgroups(0,0);
139     if (ngroups<0) { perror("getgroups(0,0) failed"); exit(-1); }
140     if (ngroups+opt_ngids > sizeof(groups)/sizeof(groups[0])) {
141       fputs("too many groups already set for total to fit\n",stderr);
142       exit(-1);
143     }
144     ngroups= getgroups(ngroups,groups);
145     if (ngroups<0) { perror("getgroups failed"); exit(-1); }
146   }
147   if (opt_ngids) {
148     maingid= opt_gids[0];
149   }
150   if (opt_ngids || opt_groupsclear) {
151     ngroups_in= ngroups; ngroups= 0;
152     for (i=0; i<ngroups_in; i++) {
153       for (j=0; j<ngroups && groups[j] != groups[i]; j++);
154       if (j<ngroups) continue;
155       groups[ngroups++]= groups[i];
156     }
157     for (i=0; i<opt_ngids; i++) {
158       for (j=0; j<ngroups && groups[j] != opt_gids[i]; j++);
159       if (j<ngroups) continue;
160       groups[ngroups++]= opt_gids[i];
161     }
162     r= setgroups(ngroups,groups);
163     if (r) { perror("setgroups failed"); exit(-1); }
164   }
165   if (maingid != -1) {
166     r= setgid(maingid); if (r) { perror("setgid failed"); exit(-1); }
167     r= setgid(maingid); if (r) { perror("2nd setgid failed"); exit(-1); }
168   }
169   if (opt_uidonly != -1) {
170     mainuid= opt_uidonly;
171   } else {
172     mainuid= orgmainuid;
173   }
174   r= setuid(mainuid); if (r) { perror("setuid failed"); exit(-1); }
175   r= setuid(mainuid); if (r) { perror("2nd setuid failed"); exit(-1); }
176   if (mainuid != 0) {
177     r= seteuid(0); if (r>=0) { fputs("could seteuid 0",stderr); exit(-1); }
178     if (errno != EPERM) {
179       perror("unexpected failure mode for seteuid 0"); exit(-1);
180     }
181   }
182   r= getuid(); if (r<0) { perror("getuid failed"); exit(-1); }
183   if (r != mainuid) { fputs("getuid mismatch",stderr); exit(-1); }
184   r= geteuid(); if (r<0) { perror("geteuid failed"); exit(-1); }
185   if (r != mainuid) { fputs("geteuid mismatch",stderr); exit(-1); }
186   if (maingid != -1) {
187     for (i=0; i<ngroups && maingid != groups[i]; i++);
188     if (i>=ngroups && maingid != orgmaingid) {
189       r= setgid(orgmaingid);
190       if (r>=0) { fputs("could setgid back",stderr); exit(-1); }
191       if (errno != EPERM) {
192         perror("unexpected failure mode for setgid back"); exit(-1);
193       }
194     }
195     r= getgid(); if (r<0) { perror("getgid failed"); exit(-1); }
196     if (r != maingid) { fputs("getgid mismatch",stderr); exit(-1); }
197     r= getegid(); if (r<0) { perror("getegid failed"); exit(-1); }
198     if (r != maingid) { fputs("getegid mismatch",stderr); exit(-1); }
199   }
200   if (!*argv) {
201     cp= getenv("SHELL");
202     if (!cp) cp= "sh";
203     execlp(cp,cp,"-i",(const char*)0);
204   } else {
205     execvp(argv[0],(char**)argv);
206   }
207   perror("exec failed");
208   exit(-1);
209 }