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