home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume05 / setuid < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  6.8 KB

  1. From decwrl!ucbvax!pasteur!ames!xanth!nic.MR.NET!hal!ncoast!allbery Thu Dec 29 19:07:04 PST 1988
  2. Article 773 of comp.sources.misc:
  3. Path: granite!decwrl!ucbvax!pasteur!ames!xanth!nic.MR.NET!hal!ncoast!allbery
  4. From: maart@cs.vu.nl.UUCP (Maarten Litmaath)
  5. Newsgroups: comp.sources.misc
  6. Subject: v05i097: setuid.c -- another approach to setuid shell scripts
  7. Message-ID: <8812231209.aa08936@piraat.cs.vu.nl>
  8. Date: 29 Dec 88 01:54:43 GMT
  9. Sender: allbery@ncoast.UUCP
  10. Reply-To: maart@cs.vu.nl.UUCP (Maarten Litmaath)
  11. Lines: 292
  12. Approved: allbery@ncoast.UUCP
  13.  
  14. Posting-number: Volume 5, Issue 97
  15. Submitted-by: "Maarten Litmaath" <maart@cs.vu.nl.UUCP>
  16. Archive-name: setuid
  17.  
  18. [I don't care how "secure" you make them, I still don't trust them.... ++bsa]
  19. [As usual, any setuid gurus want to comment on the security or lack of same
  20. in this one?  ++bsa]
  21.  
  22. Instead of patching David's original idea, I tried a different approach.
  23. Using David's solution one must maintain a `database' of valid setuid shell
  24. scripts and ordinary users cannot create new entries unless there's an update
  25. program accessible to them too. It's /bin/secure who's setuid. Using my
  26. method it's again the shell script itself who's got the setuid bit.
  27. /bin/setuid (notice slightly different name :-) is a mere executable DOING
  28. THE RIGHT THINGS. Read the manual, examine the source, be convinced :-)
  29. Regards,
  30.                 Maarten Litmaath @ VU Amsterdam:
  31.                 maart@cs.vu.nl, mcvax!botter!maart
  32.  
  33. : This is a shar archive.  Extract with sh, not csh.
  34. : This archive ends with exit, so do not worry about trailing junk.
  35. : --------------------------- cut here --------------------------
  36. PATH=/bin:/usr/bin:/usr/ucb
  37. echo Extracting 'setuid.8'
  38. sed 's/^X//' > 'setuid.8' << '+ END-OF-FILE ''setuid.8'
  39. X.\" maart@cs.vu.nl - Maarten Litmaath Fri Dec 23 10:13:51 MET 1988
  40. X.\"
  41. X.TH SETUID 8 "Dec 23, 1988"
  42. X.UC 4
  43. X.SH NAME
  44. X.B setuid
  45. X\- run setuid shell scripts safely
  46. X.SH SYNOPSIS
  47. X.B #! /bin/setuid
  48. X.br
  49. X.SH DESCRIPTION
  50. X.B setuid
  51. Xis never normally executed from a shell. Instead it can be used
  52. Xas the interpreter for shell scripts that need to be run setuid to someone
  53. Xelse: this is done by making the first line of the script
  54. X.PP
  55. X.ti+5n
  56. X#! /bin/setuid
  57. X.PP
  58. Xrather than the usual
  59. X.PP
  60. X.ti+5n
  61. X#! /bin/sh
  62. X.PP
  63. X.B setuid
  64. Xtries to open the script for reading. If successful it discards the first
  65. Xline (containing the \fB#! /bin/setuid\fR) and tries to read a line
  66. Xformatted as follows:
  67. X.br
  68. X.br
  69. X.PP
  70. X.ti+5n
  71. X#? absolute\-path\-of\-interpreter arguments
  72. X.PP
  73. XExample:
  74. X.PP
  75. X.ti+5n
  76. X#? /bin/csh -bf /usr/etc/setuid_script
  77. X.PP
  78. XLinking to the script in order to gain root privileges is useless, because
  79. Xthe script contains its own invocation command sequence, including its
  80. Xabsolute path: the real name of the link cannot be found in the ultimate
  81. Xcommand.
  82. X.B setuid
  83. Xwill only
  84. Xexec a pathname beginning with a '/', to improve security.
  85. XOf course
  86. X.B setuid
  87. Xcan be `fooled' by supplying dubious arguments to the interpreter, like
  88. Xrelative pathnames. Furthermore it is a mistake to let the last directory
  89. Xcomponent of the ultimate path be writable by others. In our example /usr/etc
  90. Xshould not be writable for ordinary users.
  91. X.PP
  92. XIn addition, for the sake of security,
  93. X.B setuid
  94. Xsets the PATH environment variable back to a simple default, and deletes
  95. Xthe IFS environment variable.
  96. X.SH AUTHOR
  97. XMaarten Litmaath @ VU Informatika Amsterdam (maart@cs.vu.nl)
  98. X.SH "SEE ALSO"
  99. X.BR sh (1), csh (1)
  100. X.SH BUGS
  101. XThe maintenance of setuid scripts is a bit annoying: if a script is moved,
  102. Xone must not forget to change the path mentioned in the script. Possibly
  103. Xthe editing causes the setuid bit to get turned off.
  104. + END-OF-FILE setuid.8
  105. chmod 'u=rw,g=r,o=r' 'setuid.8'
  106. set `wc -c 'setuid.8'`
  107. count=$1
  108. case $count in
  109. 1867)    :;;
  110. *)    echo 'Bad character count in ''setuid.8' >&2
  111.         echo 'Count should be 1867' >&2
  112. esac
  113. echo Extracting 'setuid.c'
  114. sed 's/^X//' > 'setuid.c' << '+ END-OF-FILE ''setuid.c'
  115. Xchar    sccsid[] = "@(#) setuid.c 1.0 88/12/24 Maarten Litmaath";
  116. X
  117. X/*
  118. X * setuid.c
  119. X * execute setuid shell scripts safely
  120. X * see setuid.8
  121. X */
  122. X
  123. X#include    <stdio.h>
  124. X
  125. X
  126. X#define        COMMENT        '#'
  127. X#define        MAGIC        '?'
  128. X#define        MAXARGV        1024
  129. X#define        MAXLEN        256
  130. X#define        SEC_ARGC    1
  131. X#define        SEC_OPEN    2
  132. X#define        SEC_FMT        3
  133. X#define        SEC_READ    4
  134. X#define        SEC_EXEC    5
  135. X
  136. X
  137. Xtypedef    int    bool;
  138. X
  139. X
  140. Xchar    E_argc[] = "%s: argument expected\n",
  141. X    E_open[] = "%s: cannot open ",
  142. X    E_fmt[] = "%s: format error in %s\n",
  143. X    E_read[] = "%s: read error in ",
  144. X    E_exec[] = "%s: cannot execute ",
  145. X    Defaultpath[] = "/bin:/usr/bin",
  146. X    *interpreter,
  147. X    *newargv[MAXARGV];
  148. X
  149. X
  150. Xmain(argc, argv)
  151. Xint    argc;
  152. Xchar    **argv;
  153. X{
  154. X    FILE    *fp;
  155. X    int    c;
  156. X    char    *prog, *strrchr();
  157. X    
  158. X    
  159. X    if (!(prog = strrchr(argv[0], '/')))
  160. X        prog = argv[0];
  161. X    else
  162. X        ++prog;
  163. X
  164. X    if (argc != 2) {
  165. X        fprintf(stderr, E_argc, prog);
  166. X        exit(SEC_ARGC);
  167. X    }
  168. X
  169. X    if (!(fp = fopen(argv[1], "r"))) {
  170. X        fprintf(stderr, E_open, prog);
  171. X        perror(argv[1]);
  172. X        exit(SEC_OPEN);
  173. X    }
  174. X
  175. X    while ((c = getc(fp)) != '\n' && c != EOF)    /* skip #! line */
  176. X        ;
  177. X
  178. X
  179. X    if (!getparams(fp)) {
  180. X        if (ferror(fp)) {
  181. X            fprintf(stderr, E_read, prog);
  182. X            perror(argv[1]);
  183. X            exit(SEC_READ);
  184. X        }
  185. X        fprintf(stderr, E_fmt, prog, argv[1]);
  186. X        exit(SEC_FMT);
  187. X    }
  188. X
  189. X#ifdef    DEBUG
  190. X    for (c = 0; newargv[c]; c++)
  191. X        printf("newargv[%d]='%s'\n", c, newargv[c]);
  192. X#endif    DEBUG
  193. X
  194. X    (void) unsetenv("IFS");
  195. X    (void) setenv("PATH", Defaultpath, 1);
  196. X
  197. X    execv(interpreter, newargv);
  198. X
  199. X    fprintf(stderr, E_exec, prog);
  200. X    perror(interpreter);
  201. X    exit(SEC_EXEC);
  202. X}
  203. X
  204. X
  205. Xstatic    bool    getparams(fp)
  206. XFILE    *fp;
  207. X{
  208. X    char    buf[MAXLEN], *skipblanks(), *skiptoblank(), *strrchr();
  209. X    register char    *p;
  210. X    register int    i = 0;
  211. X
  212. X
  213. X    for (;;) {
  214. X        if (!fgets(buf, sizeof buf, fp) ||
  215. X            buf[strlen(buf) - 1] != '\n')
  216. X            return 0;
  217. X
  218. X        p = skipblanks(buf);
  219. X
  220. X        switch (*p++) {
  221. X            case '\n':
  222. X                continue;
  223. X            case COMMENT:
  224. X                break;
  225. X            default:
  226. X                return 0;
  227. X        }
  228. X
  229. X        if (*p++ == MAGIC)
  230. X            break;
  231. X    }
  232. X
  233. X    p = skipblanks(p);
  234. X
  235. X    if (*(interpreter = p) != '/')
  236. X        return 0;
  237. X
  238. X    p = skiptoblank(p);
  239. X
  240. X    if (*p == '\n')
  241. X        return 0;
  242. X
  243. X    *p++ = '\0';
  244. X
  245. X    newargv[0] = strrchr(interpreter, '/') + 1;
  246. X
  247. X    while (i < MAXARGV - 1) {
  248. X        p = skipblanks(p);
  249. X
  250. X        if (*p == '\n')
  251. X            break;
  252. X
  253. X        newargv[++i] = p;
  254. X
  255. X        p = skiptoblank(p);
  256. X
  257. X        if (*p == '\n')
  258. X            break;
  259. X
  260. X        *p++ = '\0';
  261. X    }
  262. X
  263. X    newargv[++i] = 0;
  264. X
  265. X    /*
  266. X     * we expect 2 args at least: interpreter + file
  267. X     * furthermore we expect p to point to end of line by now
  268. X     */
  269. X
  270. X    if (i <= 1 || *p != '\n')
  271. X        return 0;
  272. X
  273. X    *p = '\0';
  274. X    return 1;
  275. X}
  276. X
  277. X
  278. Xstatic    char    *skipblanks(p)
  279. Xregister char    *p;
  280. X{
  281. X    while (*p == ' ' || *p == '\t')
  282. X        ++p;
  283. X
  284. X    return p;
  285. X}
  286. X
  287. X
  288. Xstatic    char    *skiptoblank(p)
  289. Xregister char    *p;
  290. X{
  291. X    while (*p != ' ' && *p != '\t' && *p != '\n')
  292. X        ++p;
  293. X
  294. X    return p;
  295. X}
  296. + END-OF-FILE setuid.c
  297. chmod 'u=rw,g=r,o=r' 'setuid.c'
  298. set `wc -c 'setuid.c'`
  299. count=$1
  300. case $count in
  301. 2671)    :;;
  302. *)    echo 'Bad character count in ''setuid.c' >&2
  303.         echo 'Count should be 2671' >&2
  304. esac
  305. exit 0
  306.  
  307.  
  308.