PageRenderTime 264ms CodeModel.GetById 161ms app.highlight 8ms RepoModel.GetById 93ms app.codeStats 0ms

/Misc/setuid-prog.c

http://unladen-swallow.googlecode.com/
C | 176 lines | 80 code | 15 blank | 81 comment | 30 complexity | 116aaba15fd2b521fa59b17c3a1daa08 MD5 | raw file
  1/*
  2   Template for a setuid program that calls a script.
  3
  4   The script should be in an unwritable directory and should itself
  5   be unwritable.  In fact all parent directories up to the root
  6   should be unwritable.  The script must not be setuid, that's what
  7   this program is for.
  8
  9   This is a template program.  You need to fill in the name of the
 10   script that must be executed.  This is done by changing the
 11   definition of FULL_PATH below.
 12
 13   There are also some rules that should be adhered to when writing
 14   the script itself.
 15
 16   The first and most important rule is to never, ever trust that the
 17   user of the program will behave properly.  Program defensively.
 18   Check your arguments for reasonableness.  If the user is allowed to
 19   create files, check the names of the files.  If the program depends
 20   on argv[0] for the action it should perform, check it.
 21
 22   Assuming the script is a Bourne shell script, the first line of the
 23   script should be
 24	#!/bin/sh -
 25   The - is important, don't omit it.  If you're using esh, the first
 26   line should be
 27	#!/usr/local/bin/esh -f
 28   and for ksh, the first line should be
 29	#!/usr/local/bin/ksh -p
 30   The script should then set the variable IFS to the string
 31   consisting of <space>, <tab>, and <newline>.  After this (*not*
 32   before!), the PATH variable should be set to a reasonable value and
 33   exported.  Do not expect the PATH to have a reasonable value, so do
 34   not trust the old value of PATH.  You should then set the umask of
 35   the program by calling
 36	umask 077 # or 022 if you want the files to be readable
 37   If you plan to change directories, you should either unset CDPATH
 38   or set it to a good value.  Setting CDPATH to just ``.'' (dot) is a
 39   good idea.
 40   If, for some reason, you want to use csh, the first line should be
 41	#!/bin/csh -fb
 42   You should then set the path variable to something reasonable,
 43   without trusting the inherited path.  Here too, you should set the
 44   umask using the command
 45	umask 077 # or 022 if you want the files to be readable
 46*/
 47
 48#include <unistd.h>
 49#include <stdlib.h>
 50#include <stdio.h>
 51#include <sys/types.h>
 52#include <sys/stat.h>
 53#include <string.h>
 54
 55/* CONFIGURATION SECTION */
 56
 57#ifndef FULL_PATH	/* so that this can be specified from the Makefile */
 58/* Uncomment the following line:
 59#define FULL_PATH	"/full/path/of/script" 
 60* Then comment out the #error line. */
 61#error "You must define FULL_PATH somewhere"
 62#endif
 63#ifndef UMASK
 64#define UMASK		077
 65#endif
 66
 67/* END OF CONFIGURATION SECTION */
 68
 69#if defined(__STDC__) && defined(__sgi)
 70#define environ _environ
 71#endif
 72
 73/* don't change def_IFS */
 74char def_IFS[] = "IFS= \t\n";
 75/* you may want to change def_PATH, but you should really change it in */
 76/* your script */
 77#ifdef __sgi
 78char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin";
 79#else
 80char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin";
 81#endif
 82/* don't change def_CDPATH */
 83char def_CDPATH[] = "CDPATH=.";
 84/* don't change def_ENV */
 85char def_ENV[] = "ENV=:";
 86
 87/*
 88   This function changes all environment variables that start with LD_
 89   into variables that start with XD_.  This is important since we
 90   don't want the script that is executed to use any funny shared
 91   libraries.
 92
 93   The other changes to the environment are, strictly speaking, not
 94   needed here.  They can safely be done in the script.  They are done
 95   here because we don't trust the script writer (just like the script
 96   writer shouldn't trust the user of the script).
 97   If IFS is set in the environment, set it to space,tab,newline.
 98   If CDPATH is set in the environment, set it to ``.''.
 99   Set PATH to a reasonable default.
100*/
101void
102clean_environ(void)
103{
104	char **p;
105	extern char **environ;
106
107	for (p = environ; *p; p++) {
108		if (strncmp(*p, "LD_", 3) == 0)
109			**p = 'X';
110		else if (strncmp(*p, "_RLD", 4) == 0)
111			**p = 'X';
112		else if (strncmp(*p, "PYTHON", 6) == 0)
113			**p = 'X';
114		else if (strncmp(*p, "IFS=", 4) == 0)
115			*p = def_IFS;
116		else if (strncmp(*p, "CDPATH=", 7) == 0)
117			*p = def_CDPATH;
118		else if (strncmp(*p, "ENV=", 4) == 0)
119			*p = def_ENV;
120	}
121	putenv(def_PATH);
122}
123
124int
125main(int argc, char **argv)
126{
127	struct stat statb;
128	gid_t egid = getegid();
129	uid_t euid = geteuid();
130
131	/*
132	   Sanity check #1.
133	   This check should be made compile-time, but that's not possible.
134	   If you're sure that you specified a full path name for FULL_PATH,
135	   you can omit this check.
136	*/
137	if (FULL_PATH[0] != '/') {
138		fprintf(stderr, "%s: %s is not a full path name\n", argv[0],
139			FULL_PATH);
140		fprintf(stderr, "You can only use this wrapper if you\n");
141		fprintf(stderr, "compile it with an absolute path.\n");
142		exit(1);
143	}
144
145	/*
146	   Sanity check #2.
147	   Check that the owner of the script is equal to either the
148	   effective uid or the super user.
149	*/
150	if (stat(FULL_PATH, &statb) < 0) {
151		perror("stat");
152		exit(1);
153	}
154	if (statb.st_uid != 0 && statb.st_uid != euid) {
155		fprintf(stderr, "%s: %s has the wrong owner\n", argv[0],
156			FULL_PATH);
157		fprintf(stderr, "The script should be owned by root,\n");
158		fprintf(stderr, "and shouldn't be writeable by anyone.\n");
159		exit(1);
160	}
161
162	if (setregid(egid, egid) < 0)
163		perror("setregid");
164	if (setreuid(euid, euid) < 0)
165		perror("setreuid");
166
167	clean_environ();
168
169	umask(UMASK);
170
171	while (**argv == '-')	/* don't let argv[0] start with '-' */
172		(*argv)++;
173	execv(FULL_PATH, argv);
174	fprintf(stderr, "%s: could not execute the script\n", argv[0]);
175	exit(1);
176}