| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 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 | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 24 |     #!/bin/sh - | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 25 |    The - is important, don't omit it.  If you're using esh, the first | 
 | 26 |    line should be | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 27 |     #!/usr/local/bin/esh -f | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 28 |    and for ksh, the first line should be | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 29 |     #!/usr/local/bin/ksh -p | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 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 | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 36 |     umask 077 # or 022 if you want the files to be readable | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 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 | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 41 |     #!/bin/csh -fb | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 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 | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 45 |     umask 077 # or 022 if you want the files to be readable | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 46 | */ | 
 | 47 |  | 
 | 48 | #include <unistd.h> | 
 | 49 | #include <stdlib.h> | 
 | 50 | #include <stdio.h> | 
 | 51 | #include <sys/types.h> | 
 | 52 | #include <sys/stat.h> | 
| Jeremy Hylton | 1deebab | 1998-09-10 18:10:59 +0000 | [diff] [blame] | 53 | #include <string.h> | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 54 |  | 
 | 55 | /* CONFIGURATION SECTION */ | 
 | 56 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 57 | #ifndef FULL_PATH       /* so that this can be specified from the Makefile */ | 
| Jeremy Hylton | faff0bd | 1998-09-10 20:18:09 +0000 | [diff] [blame] | 58 | /* Uncomment the following line: | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 59 | #define FULL_PATH       "/full/path/of/script" | 
| Jeremy Hylton | 1deebab | 1998-09-10 18:10:59 +0000 | [diff] [blame] | 60 | * Then comment out the #error line. */ | 
| Guido van Rossum | 106a470 | 1998-09-10 18:22:18 +0000 | [diff] [blame] | 61 | #error "You must define FULL_PATH somewhere" | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 62 | #endif | 
 | 63 | #ifndef UMASK | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 64 | #define UMASK           077 | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 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 */ | 
 | 74 | char 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 | 
 | 78 | char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin"; | 
 | 79 | #else | 
 | 80 | char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin"; | 
 | 81 | #endif | 
 | 82 | /* don't change def_CDPATH */ | 
 | 83 | char def_CDPATH[] = "CDPATH=."; | 
 | 84 | /* don't change def_ENV */ | 
 | 85 | char 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 | */ | 
 | 101 | void | 
 | 102 | clean_environ(void) | 
 | 103 | { | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 104 |     char **p; | 
 | 105 |     extern char **environ; | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 106 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 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); | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 122 | } | 
 | 123 |  | 
 | 124 | int | 
 | 125 | main(int argc, char **argv) | 
 | 126 | { | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 127 |     struct stat statb; | 
 | 128 |     gid_t egid = getegid(); | 
 | 129 |     uid_t euid = geteuid(); | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 130 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 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 |     } | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 144 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 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 writable by anyone.\n"); | 
 | 159 |         exit(1); | 
 | 160 |     } | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 161 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 162 |     if (setregid(egid, egid) < 0) | 
 | 163 |         perror("setregid"); | 
 | 164 |     if (setreuid(euid, euid) < 0) | 
 | 165 |         perror("setreuid"); | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 166 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 167 |     clean_environ(); | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 168 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 169 |     umask(UMASK); | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 170 |  | 
| Antoine Pitrou | f95a1b3 | 2010-05-09 15:52:27 +0000 | [diff] [blame] | 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); | 
| Guido van Rossum | ffa257d | 1995-07-20 21:57:15 +0000 | [diff] [blame] | 176 | } |