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