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