X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=cprogs%2Freally.c;fp=cprogs%2Freally.c;h=b240ea4702fce00157d2dca9566380aa87d4ac5f;hb=c314690b25b449e079fce8b215a3e96924898578;hp=0000000000000000000000000000000000000000;hpb=2e1bf919b1398410eb37c2398890b795028328bd;p=chiark-utils.git diff --git a/cprogs/really.c b/cprogs/really.c new file mode 100644 index 0000000..b240ea4 --- /dev/null +++ b/cprogs/really.c @@ -0,0 +1,210 @@ +/**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "myopt.h" + +void usagemessage(void) { + if (fputs("usage: really [] [ ...] [--]" + " [ [ ...]]\n" + "user-options:\n" + " if no options given, set the uid to 0;\n" + " -u|--user also sets their default group list\n" + " -i|--useronly } set the uid\n" + " -I|--uidonly } but inherits the group list\n" + "group-options:\n" + " -z|--groupsclear only groups specified are to be used\n" + " -g|--group } add this to\n" + " -G|--gid } the group list\n", + stderr) == EOF) { perror("write usage"); exit(-1); } +} + +static const char *opt_user, *opt_useronly; +static int opt_groupsclear= 0, opt_ngids= 0, opt_uidonly= -1; +static int opt_gids[512]; + +static void af_group(const struct cmdinfo *cip, const char *value) { + struct group *gr; + + if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0])) + badusage("too many groups specified"); + gr= getgrnam(value); + if (!gr) { fprintf(stderr,"unknown group `%s'\n",value); exit(-1); } + opt_gids[opt_ngids++]= gr->gr_gid; +} + +static void af_gid(const struct cmdinfo *cip, const char *value) { + char *ep; + unsigned long ul; + + if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0])) + badusage("too many gids specified"); + ul= strtoul(value,&ep,0); + if ((*ep) || (uid_t)ul != ul || ul>INT_MAX) badusage("bad gid `%s'",value); + opt_gids[opt_ngids++]= ul; +} + +static void af_help(const struct cmdinfo *cip, const char *value) { + usagemessage(); exit(0); +} + +static const struct cmdinfo cmdinfos[]= { + { "user", 'u', 1, 0, &opt_user, 0, }, + { "useronly", 'i', 1, 0, &opt_useronly, 0 }, + { "uidonly", 'I', 1, &opt_uidonly, 0, 0 }, + { "groupsclear", 'z', 0, &opt_groupsclear, 0, 0, 1 }, + { "group", 'g', 1, 0, 0, af_group }, + { "gid", 'G', 1, 0, 0, af_gid }, + { "help", 'h', 0, 0, 0, af_help }, + { 0, 0 } +}; + +#ifdef REALLY_CHECK_FILE +static void checkroot(void) { + int r; + r= access(REALLY_CHECK_FILE,W_OK); + if (r) { perror("sorry"); exit(-1); } +} +#endif +#ifdef REALLY_CHECK_GID +static void checkroot(void) { + gid_t groups[512]; + int r; + + r= getgid(); if (r==REALLY_CHECK_GID) return; + if (r<0) { perror("getgid check"); exit(-1); } + r= getgroups(sizeof(groups)/sizeof(groups[0]),groups); + if (r<0) { perror("getgroups check"); exit(-1); } + for (i=0; ipw_uid; + } + orgmaingid= getgid(); + orgmainuid= getuid(); + if (orgmaingid<0) { perror("getgid failed"); exit(-1); } + if (opt_user) { + r= initgroups(opt_user,pw->pw_gid); + if (r) { perror("initgroups failed"); exit(-1); } + maingid= pw->pw_gid; + } else { + maingid= -1; + } + if (opt_groupsclear) { + ngroups= 0; + if (opt_ngids > sizeof(groups)/sizeof(groups[0])) { + fputs("too many groups to set\n",stderr); + exit(-1); + } + } else { + ngroups= getgroups(0,0); + if (ngroups<0) { perror("getgroups(0,0) failed"); exit(-1); } + if (ngroups+opt_ngids > sizeof(groups)/sizeof(groups[0])) { + fputs("too many groups already set for total to fit\n",stderr); + exit(-1); + } + ngroups= getgroups(ngroups,groups); + if (ngroups<0) { perror("getgroups failed"); exit(-1); } + } + if (opt_ngids) { + maingid= opt_gids[0]; + } + if (opt_ngids || opt_groupsclear) { + ngroups_in= ngroups; ngroups= 0; + for (i=0; i=0) { fputs("could seteuid 0",stderr); exit(-1); } + if (errno != EPERM) { + perror("unexpected failure mode for seteuid 0"); exit(-1); + } + } + r= getuid(); if (r<0) { perror("getuid failed"); exit(-1); } + if (r != mainuid) { fputs("getuid mismatch",stderr); exit(-1); } + r= geteuid(); if (r<0) { perror("geteuid failed"); exit(-1); } + if (r != mainuid) { fputs("geteuid mismatch",stderr); exit(-1); } + if (maingid != -1) { + for (i=0; i=ngroups && maingid != orgmaingid) { + r= setgid(orgmaingid); + if (r>=0) { fputs("could setgid back",stderr); exit(-1); } + if (errno != EPERM) { + perror("unexpected failure mode for setgid back"); exit(-1); + } + } + r= getgid(); if (r<0) { perror("getgid failed"); exit(-1); } + if (r != maingid) { fputs("getgid mismatch",stderr); exit(-1); } + r= getegid(); if (r<0) { perror("getegid failed"); exit(-1); } + if (r != maingid) { fputs("getegid mismatch",stderr); exit(-1); } + } + if (!*argv) { + cp= getenv("SHELL"); + if (!cp) cp= "sh"; + execlp(cp,cp,"-i",(const char*)0); + } else { + execvp(argv[0],(char**)argv); + } + perror("exec failed"); + exit(-1); +}