home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume6 / setuid-1.1 < prev    next >
Text File  |  1989-02-03  |  8KB  |  338 lines

  1. Path: lll-winken!ames!mailrus!tut.cis.ohio-state.edu!ucbvax!unisoft!uunet!allbery
  2. From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  3. Newsgroups: comp.sources.misc
  4. Subject: v06i039: setuid.c version 1.1
  5. Message-ID: <48168@uunet.UU.NET>
  6. Date: 4 Feb 89 03:31:39 GMT
  7. Sender: allbery@uunet.UU.NET
  8. Reply-To: maart@cs.vu.nl (Maarten Litmaath)
  9. Lines: 326
  10. Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  11.  
  12. Posting-number: Volume 6, Issue 39
  13. Submitted-by: maart@cs.vu.nl (Maarten Litmaath)
  14. Archive-name: setuid-1.1
  15.  
  16. ["Secure" setuid shell scripts.  Again.  ++bsa]
  17.  
  18. of course Alexis was right: version 1.0 still suffered from the `linking trick
  19. race condition'. Yet there is a way to prevent it, I venture! We could fstat()
  20. the file descriptor we've got, to see if uid + mode of our inputfile are what
  21. we expect them to be. Or? Alexis' other point has been taken care of too: now
  22. it's possible to supply arguments to the setuid script (a trivial change).
  23. So how about version 1.1?
  24. Regards,
  25.                 Maarten Litmaath @ VU Amsterdam:
  26.                 maart@cs.vu.nl, mcvax!botter!maart
  27.  
  28. : This is a shar archive.  Extract with sh, not csh.
  29. : This archive ends with exit, so do not worry about trailing junk.
  30. : --------------------------- cut here --------------------------
  31. PATH=/bin:/usr/bin:/usr/ucb
  32. echo Extracting 'setuid.8'
  33. sed 's/^X//' > 'setuid.8' << '+ END-OF-FILE ''setuid.8'
  34. X.\" maart@cs.vu.nl - Maarten Litmaath Thu Dec 29 01:14:49 MET 1988
  35. X.\"
  36. X.TH SETUID 8 "Dec 23, 1988"
  37. X.UC 4
  38. X.SH NAME
  39. X.B setuid
  40. X\- run setuid shell scripts safely
  41. X.SH SYNOPSIS
  42. X.B #! /bin/setuid
  43. X.br
  44. X.SH DESCRIPTION
  45. X.B setuid
  46. Xis never normally executed from a shell. Instead it can be used
  47. Xas the interpreter for shell scripts that need to be run setuid to someone
  48. Xelse: this is done by making the first line of the script
  49. X.PP
  50. X.ti+5n
  51. X#! /bin/setuid
  52. X.PP
  53. Xrather than the usual
  54. X.PP
  55. X.ti+5n
  56. X#! /bin/sh
  57. X.PP
  58. X.B setuid
  59. Xtries to open the script for reading. If successful it discards the first
  60. Xline (containing the \fB#! /bin/setuid\fR) and tries to read a line
  61. Xformatted as follows:
  62. X.br
  63. X.br
  64. X.PP
  65. X.ti+5n
  66. X#? absolute\-path\-of\-interpreter arguments
  67. X.PP
  68. XExample:
  69. X.PP
  70. X.ti+5n
  71. X#? /bin/csh -bf /usr/etc/setuid_script
  72. X.PP
  73. XTo avoid `linking tricks' for obtaining all privileges of the owner of the
  74. Xfile, 2 measures have been taken:
  75. X.br
  76. X.br
  77. X.PP
  78. X1) the script must contain its own safe invocation, that is the `#?' line;
  79. X.br
  80. X2) just before the final
  81. X.B execv(2)
  82. X.B setuid
  83. Xchecks if the owner and mode of the script are still what they are supposed
  84. Xto be; if there is a discrepancy,
  85. X.B setuid
  86. Xwill abort with an error message.
  87. X.PP
  88. X.B setuid
  89. Xwill only
  90. Xexec a pathname beginning with a '/', to improve security.
  91. XOf course
  92. X.B setuid
  93. Xcan be `fooled' by supplying dubious arguments to the interpreter, like
  94. Xrelative pathnames. Furthermore it is a mistake to let the last directory
  95. Xcomponent of the ultimate path be writable by others. In our example /usr/etc
  96. Xshould not be writable for ordinary users.
  97. X.PP
  98. XIn addition, for the sake of security,
  99. X.B setuid
  100. Xsets the PATH environment variable back to a simple default, and deletes
  101. Xthe IFS environment variable.
  102. X.SH AUTHOR
  103. XMaarten Litmaath @ VU Informatika Amsterdam (maart@cs.vu.nl)
  104. X.SH "SEE ALSO"
  105. X.BR sh (1), csh (1)
  106. X.SH BUGS
  107. XThe maintenance of setuid scripts is a bit annoying: if a script is moved,
  108. Xone must not forget to change the path mentioned in the script. Possibly
  109. Xthe editing causes the setuid bit to get turned off.
  110. + END-OF-FILE setuid.8
  111. chmod 'u=rw,g=r,o=r' 'setuid.8'
  112. set `wc -c 'setuid.8'`
  113. count=$1
  114. case $count in
  115. 2044)    :;;
  116. *)    echo 'Bad character count in ''setuid.8' >&2
  117.         echo 'Count should be 2044' >&2
  118. esac
  119. echo Extracting 'setuid.c'
  120. sed 's/^X//' > 'setuid.c' << '+ END-OF-FILE ''setuid.c'
  121. Xchar    sccsid[] = "@(#) setuid.c 1.1 88/12/29 Maarten Litmaath";
  122. X
  123. X/*
  124. X * setuid.c
  125. X * execute setuid shell scripts safely
  126. X * see setuid.8
  127. X */
  128. X
  129. X#include    <sys/types.h>
  130. X#include    <sys/stat.h>
  131. X#include    <stdio.h>
  132. X
  133. X
  134. X#define        COMMENT        '#'
  135. X#define        MAGIC        '?'
  136. X#define        MAXARGV        1024
  137. X#define        MAXLEN        256
  138. X#define        SEC_ARGC    1
  139. X#define        SEC_OPEN    2
  140. X#define        SEC_FMT        3
  141. X#define        SEC_READ    4
  142. X#define        SEC_EXEC    5
  143. X#define        SEC_STAT    6
  144. X#define        SEC_ALARM    7
  145. X
  146. X
  147. Xchar    E_argc[] = "%s: argument expected\n",
  148. X    E_open[] = "%s: cannot open ",
  149. X    E_fmt[] = "%s: format error in %s\n",
  150. X    E_read[] = "%s: read error in ",
  151. X    E_exec[] = "%s: cannot execute ",
  152. X    E_stat[] = "%s: cannot fstat ",
  153. X    E_alarm[] = "%s: %s is fake!\n",
  154. X    Defaultpath[] = "/bin:/usr/bin",
  155. X    *interpreter,
  156. X    *newargv[MAXARGV];
  157. X
  158. X
  159. Xmain(argc, argv)
  160. Xint    argc;
  161. Xchar    **argv;
  162. X{
  163. X    FILE    *fp;
  164. X    int    c;
  165. X    char    *prog, *file, *strrchr();
  166. X    struct    stat    st;
  167. X    
  168. X    
  169. X    if (!(prog = strrchr(argv[0], '/')))
  170. X        prog = argv[0];
  171. X    else
  172. X        ++prog;
  173. X
  174. X    if (argc < 2) {
  175. X        fprintf(stderr, E_argc, prog);
  176. X        exit(SEC_ARGC);
  177. X    }
  178. X    file = argv[1];
  179. X
  180. X    if (!(fp = fopen(file, "r"))) {
  181. X        fprintf(stderr, E_open, prog);
  182. X        perror(file);
  183. X        exit(SEC_OPEN);
  184. X    }
  185. X
  186. X    while ((c = getc(fp)) != '\n' && c != EOF)    /* skip #! line */
  187. X        ;
  188. X
  189. X
  190. X    if (!(c = getparams(fp))) {
  191. X        if (ferror(fp)) {
  192. X            fprintf(stderr, E_read, prog);
  193. X            perror(file);
  194. X            exit(SEC_READ);
  195. X        }
  196. X        fprintf(stderr, E_fmt, prog, file);
  197. X        exit(SEC_FMT);
  198. X    }
  199. X
  200. X    argv += 2;            /* skip prog + file */
  201. X
  202. X    while (*argv && c < MAXARGV - 1)
  203. X        newargv[c++] = *argv++;
  204. X
  205. X    newargv[c] = 0;
  206. X
  207. X#ifdef    DEBUG
  208. X    printf("interpreter='%s'\n", interpreter);
  209. X    for (c = 0; newargv[c]; c++)
  210. X        printf("newargv[%d]='%s'\n", c, newargv[c]);
  211. X#endif    DEBUG
  212. X
  213. X    (void) unsetenv("IFS");
  214. X    (void) setenv("PATH", Defaultpath, 1);
  215. X
  216. X    if (fstat(fileno(fp), &st) != 0) {
  217. X        fprintf(stderr, E_stat, prog);
  218. X        perror(file);
  219. X        exit(SEC_STAT);
  220. X    }
  221. X
  222. X    if (!(st.st_mode & (S_ISUID | S_ISGID)) ||
  223. X        (st.st_mode & S_ISUID) && st.st_uid != geteuid() ||
  224. X        (st.st_mode & S_ISGID) && st.st_gid != getegid()) {
  225. X        fprintf(stderr, E_alarm, prog, file);
  226. X        exit(SEC_ALARM);
  227. X    }
  228. X
  229. X    execv(interpreter, newargv);
  230. X
  231. X    fprintf(stderr, E_exec, prog);
  232. X    perror(interpreter);
  233. X    exit(SEC_EXEC);
  234. X}
  235. X
  236. X
  237. Xstatic    int    getparams(fp)
  238. XFILE    *fp;
  239. X{
  240. X    char    buf[MAXLEN], *skipblanks(), *skiptoblank(), *strrchr();
  241. X    register char    *p;
  242. X    register int    i = 0;
  243. X
  244. X
  245. X    for (;;) {
  246. X        if (!fgets(buf, sizeof buf, fp) ||
  247. X            buf[strlen(buf) - 1] != '\n')
  248. X            return 0;
  249. X
  250. X        p = skipblanks(buf);
  251. X
  252. X        switch (*p++) {
  253. X            case '\n':
  254. X                continue;
  255. X            case COMMENT:
  256. X                break;
  257. X            default:
  258. X                return 0;
  259. X        }
  260. X
  261. X        if (*p++ == MAGIC)
  262. X            break;
  263. X    }
  264. X
  265. X    p = skipblanks(p);
  266. X
  267. X    if (*(interpreter = p) != '/')
  268. X        return 0;
  269. X
  270. X    p = skiptoblank(p);
  271. X
  272. X    if (*p == '\n')
  273. X        return 0;
  274. X
  275. X    *p++ = '\0';
  276. X
  277. X    newargv[0] = strrchr(interpreter, '/') + 1;
  278. X
  279. X    while (i < MAXARGV - 1) {
  280. X        p = skipblanks(p);
  281. X
  282. X        if (*p == '\n')
  283. X            break;
  284. X
  285. X        newargv[++i] = p;
  286. X
  287. X        p = skiptoblank(p);
  288. X
  289. X        if (*p == '\n')
  290. X            break;
  291. X
  292. X        *p++ = '\0';
  293. X    }
  294. X
  295. X    newargv[++i] = 0;
  296. X
  297. X    /*
  298. X     * we expect 2 args at least: interpreter + file
  299. X     * furthermore we expect p to point to end of line by now
  300. X     */
  301. X
  302. X    if (i <= 1 || *p != '\n')
  303. X        return 0;
  304. X
  305. X    *p = '\0';
  306. X    return i;
  307. X}
  308. X
  309. X
  310. Xstatic    char    *skipblanks(p)
  311. Xregister char    *p;
  312. X{
  313. X    while (*p == ' ' || *p == '\t')
  314. X        ++p;
  315. X
  316. X    return p;
  317. X}
  318. X
  319. X
  320. Xstatic    char    *skiptoblank(p)
  321. Xregister char    *p;
  322. X{
  323. X    while (*p != ' ' && *p != '\t' && *p != '\n')
  324. X        ++p;
  325. X
  326. X    return p;
  327. X}
  328. + END-OF-FILE setuid.c
  329. chmod 'u=rw,g=r,o=r' 'setuid.c'
  330. set `wc -c 'setuid.c'`
  331. count=$1
  332. case $count in
  333. 3320)    :;;
  334. *)    echo 'Bad character count in ''setuid.c' >&2
  335.         echo 'Count should be 3320' >&2
  336. esac
  337. exit 0
  338.