| /* |
| Template for a setuid program that calls a script. |
| |
| The script should be in an unwritable directory and should itself |
| be unwritable. In fact all parent directories up to the root |
| should be unwritable. The script must not be setuid, that's what |
| this program is for. |
| |
| This is a template program. You need to fill in the name of the |
| script that must be executed. This is done by changing the |
| definition of FULL_PATH below. |
| |
| There are also some rules that should be adhered to when writing |
| the script itself. |
| |
| The first and most important rule is to never, ever trust that the |
| user of the program will behave properly. Program defensively. |
| Check your arguments for reasonableness. If the user is allowed to |
| create files, check the names of the files. If the program depends |
| on argv[0] for the action it should perform, check it. |
| |
| Assuming the script is a Bourne shell script, the first line of the |
| script should be |
| #!/bin/sh - |
| The - is important, don't omit it. If you're using esh, the first |
| line should be |
| #!/usr/local/bin/esh -f |
| and for ksh, the first line should be |
| #!/usr/local/bin/ksh -p |
| The script should then set the variable IFS to the string |
| consisting of <space>, <tab>, and <newline>. After this (*not* |
| before!), the PATH variable should be set to a reasonable value and |
| exported. Do not expect the PATH to have a reasonable value, so do |
| not trust the old value of PATH. You should then set the umask of |
| the program by calling |
| umask 077 # or 022 if you want the files to be readable |
| If you plan to change directories, you should either unset CDPATH |
| or set it to a good value. Setting CDPATH to just ``.'' (dot) is a |
| good idea. |
| If, for some reason, you want to use csh, the first line should be |
| #!/bin/csh -fb |
| You should then set the path variable to something reasonable, |
| without trusting the inherited path. Here too, you should set the |
| umask using the command |
| umask 077 # or 022 if you want the files to be readable |
| */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <string.h> |
| |
| /* CONFIGURATION SECTION */ |
| |
| #ifndef FULL_PATH /* so that this can be specified from the Makefile */ |
| /* Uncomment the following line: |
| #define FULL_PATH "/full/path/of/script" |
| * Then comment out the #error line. */ |
| #error "You must define FULL_PATH somewhere" |
| #endif |
| #ifndef UMASK |
| #define UMASK 077 |
| #endif |
| |
| /* END OF CONFIGURATION SECTION */ |
| |
| #if defined(__STDC__) && defined(__sgi) |
| #define environ _environ |
| #endif |
| |
| /* don't change def_IFS */ |
| char def_IFS[] = "IFS= \t\n"; |
| /* you may want to change def_PATH, but you should really change it in */ |
| /* your script */ |
| #ifdef __sgi |
| char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin"; |
| #else |
| char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin"; |
| #endif |
| /* don't change def_CDPATH */ |
| char def_CDPATH[] = "CDPATH=."; |
| /* don't change def_ENV */ |
| char def_ENV[] = "ENV=:"; |
| |
| /* |
| This function changes all environment variables that start with LD_ |
| into variables that start with XD_. This is important since we |
| don't want the script that is executed to use any funny shared |
| libraries. |
| |
| The other changes to the environment are, strictly speaking, not |
| needed here. They can safely be done in the script. They are done |
| here because we don't trust the script writer (just like the script |
| writer shouldn't trust the user of the script). |
| If IFS is set in the environment, set it to space,tab,newline. |
| If CDPATH is set in the environment, set it to ``.''. |
| Set PATH to a reasonable default. |
| */ |
| void |
| clean_environ(void) |
| { |
| char **p; |
| extern char **environ; |
| |
| for (p = environ; *p; p++) { |
| if (strncmp(*p, "LD_", 3) == 0) |
| **p = 'X'; |
| else if (strncmp(*p, "_RLD", 4) == 0) |
| **p = 'X'; |
| else if (strncmp(*p, "PYTHON", 6) == 0) |
| **p = 'X'; |
| else if (strncmp(*p, "IFS=", 4) == 0) |
| *p = def_IFS; |
| else if (strncmp(*p, "CDPATH=", 7) == 0) |
| *p = def_CDPATH; |
| else if (strncmp(*p, "ENV=", 4) == 0) |
| *p = def_ENV; |
| } |
| putenv(def_PATH); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| struct stat statb; |
| gid_t egid = getegid(); |
| uid_t euid = geteuid(); |
| |
| /* |
| Sanity check #1. |
| This check should be made compile-time, but that's not possible. |
| If you're sure that you specified a full path name for FULL_PATH, |
| you can omit this check. |
| */ |
| if (FULL_PATH[0] != '/') { |
| fprintf(stderr, "%s: %s is not a full path name\n", argv[0], |
| FULL_PATH); |
| fprintf(stderr, "You can only use this wrapper if you\n"); |
| fprintf(stderr, "compile it with an absolute path.\n"); |
| exit(1); |
| } |
| |
| /* |
| Sanity check #2. |
| Check that the owner of the script is equal to either the |
| effective uid or the super user. |
| */ |
| if (stat(FULL_PATH, &statb) < 0) { |
| perror("stat"); |
| exit(1); |
| } |
| if (statb.st_uid != 0 && statb.st_uid != euid) { |
| fprintf(stderr, "%s: %s has the wrong owner\n", argv[0], |
| FULL_PATH); |
| fprintf(stderr, "The script should be owned by root,\n"); |
| fprintf(stderr, "and shouldn't be writable by anyone.\n"); |
| exit(1); |
| } |
| |
| if (setregid(egid, egid) < 0) |
| perror("setregid"); |
| if (setreuid(euid, euid) < 0) |
| perror("setreuid"); |
| |
| clean_environ(); |
| |
| umask(UMASK); |
| |
| while (**argv == '-') /* don't let argv[0] start with '-' */ |
| (*argv)++; |
| execv(FULL_PATH, argv); |
| fprintf(stderr, "%s: could not execute the script\n", argv[0]); |
| exit(1); |
| } |