--- /dev/null
+/*
+ * libdpkg - Debian packaging suite library routines
+ * myopt.c - my very own option parsing
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.uk>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+
+#include "config.h"
+#include "myopt.h"
+#include "dpkg.h"
+
+void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos) {
+ const struct cmdinfo *cip;
+ const char *p, *value;
+ int l;
+
+ ++(*argvp);
+ while ((p= **argvp) && *p == '-') {
+ ++(*argvp);
+ if (!strcmp(p,"--")) break;
+ if (*++p == '-') {
+ ++p; value=0;
+ for (cip= cmdinfos;
+ cip->olong || cip->oshort;
+ cip++) {
+ if (!cip->olong) continue;
+ if (!strcmp(p,cip->olong)) break;
+ l= strlen(cip->olong);
+ if (!strncmp(p,cip->olong,l) &&
+ (p[l]== ((cip->takesvalue==2) ? '-' : '='))) { value=p+l+1; break; }
+ }
+ if (!cip->olong) badusage("unknown option --%s",p);
+ if (cip->takesvalue) {
+ if (!value) {
+ value= *(*argvp)++;
+ if (!value) badusage("--%s option takes a value",cip->olong);
+ }
+ if (cip->call) cip->call(cip,value);
+ else *cip->sassignto= value;
+ } else {
+ if (value) badusage("--%s option does not take a value",cip->olong);
+ if (cip->call) cip->call(cip,0);
+ else *cip->iassignto= cip->arg;
+ }
+ } else {
+ while (*p) {
+ for (cip= cmdinfos; (cip->olong || cip->oshort) && *p != cip->oshort; cip++);
+ if (!cip->oshort) badusage("unknown option -%c",*p);
+ p++;
+ if (cip->takesvalue) {
+ if (!*p) {
+ value= *(*argvp)++;
+ if (!value) badusage("-%c option takes a value",cip->oshort);
+ } else {
+ value= p; p="";
+ if (*value == '=') value++;
+ }
+ if (cip->call) cip->call(cip,value);
+ else *cip->sassignto= value;
+ } else {
+ if (*p == '=') badusage("-%c option does not take a value",cip->oshort);
+ if (cip->call) cip->call(cip,0);
+ else *cip->iassignto= cip->arg;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * libdpkg - Debian packaging suite library routines
+ * myopt.h - declarations for my very own option parsing
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.uk>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef MYOPT_H
+#define MYOPT_H
+
+typedef void (*voidfnp)(void);
+
+struct cmdinfo {
+ const char *olong;
+ char oshort;
+ int takesvalue; /* 0 = normal 1 = standard value 2 = option string cont */
+ int *iassignto;
+ const char **sassignto;
+ void (*call)(const struct cmdinfo*, const char *value);
+ int arg;
+ void *parg;
+ voidfnp farg;
+};
+
+void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos);
+
+#endif /* MYOPT_H */
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
-#include <errno.h>
#include <sys/types.h>
+#include <errno.h>
#include "myopt.h"
--- /dev/null
+/**/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include "myopt.h"
+
+void usagemessage(void) {
+ if (fputs("usage: really [<user-option>] [<group-option> ...] [--]"
+ " [<command> [<argument/option> ...]]\n"
+ "user-options:\n"
+ " if no options given, set the uid to 0;\n"
+ " -u|--user <username> also sets their default group list\n"
+ " -i|--useronly <username> } set the uid\n"
+ " -I|--uidonly <uid> } but inherits the group list\n"
+ "group-options:\n"
+ " -z|--groupsclear only groups specified are to be used\n"
+ " -g|--group <groupname> } add this to\n"
+ " -G|--gid <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
+void checkroot(void) {
+ int r;
+ r= access(REALLY_CHECK_FILE,W_OK);
+ if (r) { perror("sorry"); exit(-1); }
+}
+#endif
+#ifdef REALLY_CHECK_GID
+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; i<r; i++)
+ if (groups[i] == REALLY_CHECK_GID) return;
+ fputs("sorry\n",stderr); exit(-1);
+}
+#endif
+#ifdef REALLY_CHECK_NONE
+void checkroot(void) {
+}
+#endif
+
+int main(int argc, const char *const *argv) {
+ struct passwd *pw= 0;
+ gid_t groups[512];
+ int i, j, ngroups, ngroups_in, maingid, orgmaingid, mainuid, orgmainuid, r;
+ const char *cp;
+
+ checkroot();
+ myopt(&argv,cmdinfos);
+
+ if (opt_groupsclear && !opt_ngids)
+ badusage("-z|--groupsclear must be accompanied by some groups");
+ if (opt_user && (opt_useronly || opt_uidonly!=-1))
+ badusage("-u|--user may not be used with -i|--useronly or -I|--uidonly");
+ if (opt_user && opt_groupsclear)
+ badusage("-u|--user may not be used with -z|--groupsclear");
+ if (opt_uidonly != -1 && (uid_t)opt_uidonly != opt_uidonly)
+ badusage("-I|--uidonly value %d is out of range for a uid",opt_uidonly);
+
+ if (!opt_user && !opt_useronly && opt_uidonly==-1 && !opt_ngids) {
+ opt_uidonly= 0;
+ }
+ if (opt_user || opt_useronly) {
+ cp= opt_user ? opt_user : opt_useronly;
+ pw= getpwnam(cp);
+ if (!pw) { fprintf(stderr,"unknown user `%s'\n",cp); exit(-1); }
+ opt_uidonly= pw->pw_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<ngroups_in; i++) {
+ for (j=0; j<ngroups && groups[j] != groups[i]; j++);
+ if (j<ngroups) continue;
+ groups[ngroups++]= groups[i];
+ }
+ for (i=0; i<opt_ngids; i++) {
+ for (j=0; j<ngroups && groups[j] != opt_gids[i]; j++);
+ if (j<ngroups) continue;
+ groups[ngroups++]= opt_gids[i];
+ }
+ r= setgroups(ngroups,groups);
+ if (r) { perror("setgroups failed"); exit(-1); }
+ }
+ if (maingid != -1) {
+ r= setgid(maingid); if (r) { perror("setgid failed"); exit(-1); }
+ r= setgid(maingid); if (r) { perror("2nd setgid failed"); exit(-1); }
+ }
+ if (opt_uidonly != -1) {
+ mainuid= opt_uidonly;
+ } else {
+ mainuid= orgmainuid;
+ }
+ r= setuid(mainuid); if (r) { perror("setuid failed"); exit(-1); }
+ r= setuid(mainuid); if (r) { perror("2nd setuid failed"); exit(-1); }
+ if (mainuid != 0) {
+ r= seteuid(0); if (r>=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 != groups[i]; i++);
+ if (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);
+}
--- /dev/null
+#!/usr/bin/perl
+
+$testuser= 'testac';
+$testgroup= 'testac';
+$testuid= 1000;
+$testgid= 1000;
+@testxgids= qw(1000);
+$numgid= 50008;
+$othergroup= 'daemon';
+$othergid= 1;
+
+$tests= <<END;
+-u $testuser
+$testuid $testgid @testxgids
+
+-u $testuser -z
+ERROR
+
+-u $testuser -g $othergroup
+$testuid $othergid @testxgids $othergid
+
+-u $testuser -z -g $othergroup
+$testuid $othergid $othergid
+
+-u $testuser -G $numgid -g $othergroup
+$testuid $numgid @testxgids $numgid $othergid
+
+-u $testuser -z -G $numgid -g $othergroup
+$testuid $numgid $numgid $othergid
+
+-u $testuser -g $testgroup -G $testgid
+$testuid $testgid @testxgids
+
+-u $testuser -z -g $testgroup -G $testgid
+$testuid $testgid @testxgids
+
+-u $testuser -g $othergroup -g $testgroup -G $testgid
+$testuid $othergid @testxgids $othergid
+
+-u $testuser -z -g $othergroup -g $testgroup -G $testgid
+$testuid $othergid $othergid
+
+-u $testuser -G $numgid -g $othergroup -g $testgroup -G $testgid
+$testuid $numgid @testxgids $numgid $othergid
+
+-u $testuser -z -G $numgid -g $othergroup -g $testgroup -G $testgid
+$testuid $numgid $numgid $othergid
+
+
+0 $orggid @orgxgids
+
+-z
+ERROR
+
+-g $othergroup
+$orguid $othergid $othergid @orgxgids
+
+-z -g $othergroup
+$orguid $othergid $othergid
+
+-G $numgid -g $othergroup
+$orguid $numgid $numgid $othergid @orgxgids
+
+-z -G $numgid -g $othergroup
+$orguid $numgid $numgid $othergid
+
+-g $orggroup -G $orggid
+$orguid $orggid @orgxgids
+
+-z -g $orggroup -G $orggid
+$orguid $orggid $orggid
+
+-g $othergroup -g $orggroup -G $orggid
+$orguid $othergid $othergid @orgxgids
+
+-z -g $othergroup -g $orggroup -G $orggid
+$orguid $othergid $othergid $orggid
+
+-G $numgid -g $othergroup -g $orggroup -G $orggid
+$orguid $numgid $numgid $othergid @orgxgids
+
+-z -G $numgid -g $othergroup -g $orggroup -G $orggid
+$orguid $numgid $numgid $othergid
+END
+
+sub parseid ($) {
+ my ($id) = @_;
+ my $orgid= $id;
+ my $out= '';
+ my $part;
+ chomp($id);
+ $id =~ s/^uid=(\d+)// or return $orgid;
+ $out= $1;
+ $id =~ s/^\([^\)]+\)//; $id =~ s/^\s+// or return $orgid;
+ $id =~ s/^gid=(\d+)// or return $orgid;
+ $out.= " $1";
+ $id =~ s/^\([^\)]+\)//; $id =~ s/^\s+// or return $orgid;
+ $id =~ s/^groups=// or return $orgid;
+ for $part (split(/,/,$id)) {
+ $part =~ s/^(\d+)// or return $orgid;
+ $out.= " $1";
+ $part =~ s/^\([^\)]+\)//; $part eq '' or return $orgid;
+ }
+ return $out;
+}
+
+$org= `id`;
+$org =~ m/^uid=\d+\(([^\)]+)\) gid=\d+\(([^\)]+)\) / or die "$org ?";
+$orguser= $1; $orggroup= $2;
+$org= parseid($org);
+$org =~ m/^\d+ \d+ / or die $org;
+($orguid,$orggid,$orgxgids)= split(/ /,$org);
+
+@tests= split(/\n/,$tests);
+for ($i=0; $i<$#tests; $i+=3) {
+ $tests[$i+2] =~ m/\S/ and die "$tests[$i+2] $i";
+ $out= `./really-test $tests[$i] id`;
+ $newout= parseid($out);
+ next if $newout eq $tests[$i+1];
+ die "$newout ~= $tests[$i+1] ($tests[$i]) $i";
+}
--- /dev/null
+/**/
+
+#include <netinet/in.h>
+
+_syscall2(long,socketcall,int,call,unsigned long *,args);
+int real_connect(int sockfd, const struct sockaddr *saddr, int addrlen)
+{
+ unsigned long args[3];
+
+ args[0] = sockfd;
+ args[1] = (unsigned long)saddr;
+ args[2] = addrlen;
+ return socketcall(SYS_CONNECT, args);
+}
+
+int connect(int fd, struct sockaddr_in *them, int *addrlen) {
+ int r,l;
+ struct sockaddr_in us;
+ pid_t c;
+
+ if (*addrlen == sizeof(us) &&
+ them->sin_family == AF_INET &&
+ them->sin_port == htons(13)) {
+ r= getsockname(fd,&us,&l); if (r<0) return r;
+ if (!ntohs(us.port)) {
+ for (i=1023; i>0; i++) {
+ us.port= htons(i);
+ if (!bind(fd,&us,sizeof(us))) break;
+ if (errno != EADDRINUSE) return -1;
+ }
+ if (!i) return -1;
+ }
+ }
+ return real_connect(fd,them,addrlen);
+}
-Wpointer-arith -O2 -g -DREALLY_CHECK_FILE='"/etc/inittab"'
LDFLAGS=
-TARGETS=really
+TARGETS=really ucgi ucgitarget
all: $(TARGETS)
+ucgi: ucgi.o ucgicommon.o
+
+ucgitarget: ucgitarget.o ucgicommon.o
+
really: really.o myopt.o
really-test: really Makefile