home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume6 / smkdir3 / mkdir.c < prev   
Encoding:
C/C++ Source or Header  |  1989-02-03  |  11.6 KB  |  449 lines

  1. char version[]="mkdir (1.007)";
  2.  
  3. /*
  4.  * Secure mkdir program, solves that nasty race problem...
  5.  *
  6.  *    13 December 1988    Doug Davis         doug@letni.lawnet.com
  7.  *                      and John Elliott IV    iv@trsvax.tandy.com
  8.  *
  9.  * Modification History: 
  10.  *  M007   9 Jan 89 Gordon Burditt       gordon@trsvax.tandy.com
  11.  *                  - Fixed the signal handler to properly handle
  12.  *                    previously ignored signals.
  13.  *                  - Fixed canIwrite() to follow SVID conventions.
  14.  *  M006   6 Jan 89 Doug Davis           doug@letni.lawnet.com
  15.  *                  - Fixed M005 so it would not leave the secure
  16.  *                    tree lying around after recipt of multple 
  17.  *                    signals.
  18.  *  M005   5 Jan 89 John Haugh II        jfh@rpp386.dallas.tx.us
  19.  *                  - Added code to abort without createing all of the
  20.  *                    directoies that were specified.
  21.  *  M004  22 Dec 88 Doug Davis           doug@letni.lawnet.com
  22.  *                  - Fixed the error handler for the second makedir() call
  23.  *                    so it will unlink the securedir if it fails.
  24.  *                  - Fixed the error handler for chown() so that
  25.  *                    it removes the working "secure" directories.
  26.  *                    if it ever fails
  27.  *                  - cleaned up some odd tabs in the comments, and ran
  28.  *                    them thru a spell checker.
  29.  *  M003  22 Dec 88 John Elliott IV      iv@trsvax.tandy.com
  30.  *                  - Fixed the spelling of my name
  31.  *                  - General readability cleanup
  32.  *                  - Fixed backwards #ifdef RAND for srand() call
  33.  *                  - Saved some storage by declaring error msgs char msg[]
  34.  *                  - Fixed call order in "cleanup" code so that if the
  35.  *                    unlink() of the securename fails, we will still try
  36.  *                    to remove our scratch directory
  37.  *                  - Some calls to error() had an errno of 0, which meant
  38.  *                    that mkdir would incorrectly exit 0.  Calls to error()
  39.  *                    now include an exit code, which error() now exits with.
  40.  *                  - Fixed case where link-into-place failed and left all
  41.  *                    our flotsam and jetsam around.  Now rmdir() everything
  42.  *                    before we call error().
  43.  *                  - Changed the two error message symbols which began with
  44.  *                    "Couldnt_" to begin with "Cant_" so the symbols will
  45.  *                    be significant in 7 characters, for those so unfortunate
  46.  *                    not to have flexnames.
  47.  *  M002  21 Dec 88 Doug Davis           doug@letni.lawnet.com
  48.  *                  - Cleaned up code, added function declarations
  49.  *                  - fixed bug in second stat's unlink code
  50.  *  M001  19 Dec 88 John Haugh II          jfn@rpp386
  51.  *                  - Fixes oops in original code, much more secure now.
  52.  *
  53.  *    Theory of operation:
  54.  *        This mkdir first makes a directory to play in, which is
  55.  *         owned by root and is mode 000.  It is made in the same
  56.  *        directory in which the user is requesting his directory.
  57.  *        In this "secure" directory, to which the user allegedly
  58.  *        has no access, the mknod(), chown(), and links for `.'
  59.  *        and `..' are performed.  The new directory is then linked
  60.  *        into place.  Finally, the "secure" directory is removed.
  61.  *
  62.  * This code copyright 1988 by Doug Davis (doug@letni.lawnet.com) 
  63.  *      You are free to modify, hack, fold, spindle, duplicate, pass-along
  64.  *      give-away, publish, transmit, or mutlate this code in any maner,
  65.  *      provided that you give credit where credit is due and don't pretend
  66.  *      that you wrote it.
  67.  * 
  68.  *  If you do my lawyers (and I have a lot of lawyers) will teach you a lesson
  69.  *  or two in copyright law that you will never ever forget.
  70.  *
  71.  */
  72.  
  73. #define MAXPATHLEN    128        /* maximum reasonanble path length */
  74.  
  75. #include <sys/types.h>
  76. #include <signal.h>
  77. #include <sys/stat.h>
  78. #ifdef DEBUG
  79. #  include <stdio.h>
  80. #else /*DEBUG*/
  81. #  define NULL    0
  82. #endif /*DEBUG*/
  83.  
  84. #define MKNODE    1
  85. #define LINK    2
  86.  
  87. char Malloc_Failed[]    = "malloc() failed.";
  88. char Doesnt_Exist[]    = " does not exist.";
  89. char Cannot_Access[]    = "cannot access ";
  90. char Already_Exist[]    = " already exists.";
  91. char Secure_Failed[]    = "makedir secure parent failed ";
  92. char Cant_Link[]    = "Couldn't link to ";
  93. char Mkdir_Failed[]    = "makedir failed ";
  94. char Chown_Failed[]    = "chown() failed ";
  95. char Cant_Unlink[]    = "Couldn't unlink ";
  96.  
  97. extern char *STRRCHR();
  98. extern char *malloc();
  99.  
  100. extern int errno;
  101. extern int getpid();
  102. extern int stat();
  103. extern int chown();
  104. extern int unlink();
  105. extern int strlen();
  106.  
  107. extern unsigned short getuid();
  108. extern unsigned short getgid();
  109. extern long time();
  110.  
  111.  
  112. char *Progname;
  113. int signaled = 0;
  114.  
  115. #ifdef RAND
  116.   extern int rand();
  117. #else /*RAND*/
  118.   extern int getppid();
  119. #endif /*RAND*/
  120.  
  121. /* This function is part of the signal handler, it is used to prevent
  122.  * mkdir from creating all of the directories on the command line, 
  123.  * upon recipt of a signal mkdir will finish the directory that it is
  124.  * currently doing then exit
  125.  */
  126. int
  127. catch()
  128. {
  129.     signaled = 1;
  130.     set_sigs(SIG_IGN);
  131. }
  132.  
  133. main(argc, argv)
  134. int argc;
  135. char *argv[];
  136. {
  137.     Progname = argv[0];
  138.     errno = 0;
  139.  
  140.     if (argc < 2) {
  141.         print("Usage:  ");
  142.         print(Progname);
  143.         print(" directory_name [ ... directory_name ]\n");
  144.         exit(0);
  145.     }
  146.  
  147.     /* Catch those nasty signals that could cause us
  148.      * to mess up the filesystem */
  149.     set_sigs(catch);
  150.  
  151.     while (--argc && ! signaled)
  152.         md(*++argv); /* make each directory */
  153.  
  154.     exit(0);
  155. }
  156.  
  157.  
  158. int
  159. md(s)
  160. char *s;
  161. {
  162.     register char *basename,
  163.                   *parent,
  164.                   *fullname;
  165.  
  166.     unsigned short myuserid,
  167.                    mygroupid;
  168.  
  169.     char securename[MAXPATHLEN],
  170.          securedir[MAXPATHLEN],
  171.          dotsecurename[MAXPATHLEN];
  172.  
  173.     int    rval1,
  174.         rval2;
  175.  
  176.     long snum;
  177.  
  178.     struct    stat sanity;
  179.  
  180.     /* find out who I really am */
  181.     myuserid = getuid();
  182.     mygroupid = getgid();
  183.  
  184.     /* set up the pseudo-RANDom number generation system */
  185. #ifdef RAND
  186.     srand(getpid());
  187. #endif /*RAND*/
  188.  
  189.     /* see if we are explicit or indirect */
  190.     basename = STRRCHR(s, '/');
  191.     if (basename == (char *)NULL) {
  192.         fullname = malloc(strlen(s)+1);
  193.         if (fullname == (char *)NULL) 
  194.             error(Malloc_Failed, (char *)NULL, errno, 1);
  195.         parent = malloc(2);
  196.         if (parent == (char *)NULL)
  197.             error(Malloc_Failed, (char *)NULL, errno, 1);
  198.         parent[0] = '.';
  199.         parent[1] = '\0';
  200.         strcpy(fullname, s);
  201.         basename = s;
  202.     } else {
  203.         fullname = malloc(strlen(s)+1);
  204.         if (fullname == (char *)NULL) 
  205.             error(Malloc_Failed, (char *)NULL, errno, 1);
  206.         strcpy(fullname, s);
  207.         *basename = '\0';
  208.         basename++;
  209.         parent = malloc(strlen(s) + 3);
  210.         if (parent == (char *)NULL)
  211.             error(Malloc_Failed, (char *)NULL, errno, 1);
  212.         strcpy(parent, s);
  213.         strcat(parent, "/.");
  214.     }
  215.  
  216.     /* Generate the secure names ... */
  217.     do {
  218.         /* round and round we go; where we stop depends on
  219.          * the non-existance of securedir */
  220.         snum = time((long *) 0L);    
  221. #ifdef RAND
  222.         sprintf(securedir, "%s/%ld", parent,
  223.             (rand() % 2) ? snum + (long)rand() : snum - (long)rand());
  224.         sprintf(securename, "%s/%ld", securedir,
  225.             (rand() % 2) ? snum - (long)rand() : snum + (long)rand());
  226.         sprintf(dotsecurename, "%s/./.", securename);
  227. #else /*RAND*/
  228.         sprintf(securedir, "%s/%ld", parent, snum - (long)getppid());
  229.         sprintf(securename, "%s/%ld", securedir, snum + (long)getppid());
  230.         sprintf(dotsecurename, "%s/./.", securename);
  231.         snum += (long)getpid();
  232. #endif /*RAND*/
  233.     } while (stat(securedir, &sanity) == 0);
  234.  
  235. #ifdef DEBUG
  236.     /* spill the beans .. */
  237.     printf("parent     == %s\n", parent);
  238.     printf("basename   == %s\n", basename);
  239.     printf("fullname   == %s\n", fullname);
  240.     printf("securedir  == %s\n", securedir);
  241.     printf("securename == %s\n", securename);
  242.     printf("dotsecurename == %s\n", dotsecurename);
  243.     fflush(stdout);
  244. #endif /*DEBUG*/
  245.  
  246.     /* let's see if our parent directory is around... */
  247.     if ((stat(parent, &sanity)) != 0)
  248.         error(parent, Doesnt_Exist, 0, 2);
  249.  
  250.     /* find out if we can write here */
  251.     if (canIwrite(&sanity, myuserid, mygroupid) != 0) 
  252.         error(Cannot_Access, parent, 0, 2);
  253.  
  254.     /* find out if we are going to stomp on something.. */
  255.     if ((stat(fullname, &sanity)) == 0) 
  256.         error(fullname, Already_Exist, 0, 2);
  257.  
  258.     /* make secure parent directory (note the mode of 0) */
  259.     if (makedir(parent, securedir, 0) > 0) 
  260.         error(Secure_Failed, securedir, errno, 2);
  261.     
  262.     /* now make our directory underneath it */
  263.     if (makedir(parent, securename, 0777) > 0) {
  264.         rmdir(securedir);
  265.         error(Mkdir_Failed, securedir, errno, 3);
  266.     }
  267.  
  268.     /* do that eerie little chown() that's the "root" of all our problems */
  269.     if (chown(dotsecurename, myuserid, mygroupid) != 0) {
  270.         rmdir(securename);
  271.         rmdir(securedir);
  272.         error(Chown_Failed, dotsecurename, errno, 3);
  273.     }
  274.     
  275.     /* do a quick sanity check, just to annoy someone trying
  276.      * to trick mkdir into chowning something it shouldn't.. */
  277.     if ((stat(fullname, &sanity)) == 0) {
  278.         /* What happened?  This wasn't here a couple of calls ago... */
  279.         rmdir(securename);
  280.         rmdir(securedir);
  281.         error(fullname, Already_Exist, 0, 4);
  282.     }
  283.         
  284.     /* okay, put it where it belongs */
  285.     if ((link(securename, fullname)) < 0) {
  286.         rmdir(securename);
  287.         rmdir(securedir);
  288.         error(Cant_Link, fullname, errno, 4);
  289.     }
  290.     
  291.     /* remove all our rubbish, and tidy everything up.. */
  292.     if (parent != (char *)NULL) 
  293.         free(parent);
  294.     if (fullname != (char *)NULL)
  295.         free(fullname);
  296.     rval1 = unlink(securename);
  297.         /* Even if the unlink() fails, we really should at least
  298.          * attempt to remove our scratch directory... */
  299.     rval2 = rmdir(securedir);
  300.     if (rval1 < 0)
  301.         error(Cant_Unlink, securename, errno, 5);
  302.     if (rval2 != 0)
  303.         error(Cant_Unlink, securedir, errno, 5);
  304.  
  305.     return (0);
  306. }
  307.  
  308. int
  309. makedir(parent, dir, mode)
  310. char *parent, *dir;
  311. int mode;
  312. {
  313.     char dotdot[MAXPATHLEN];
  314.  
  315. #ifdef DEBUG
  316.     printf("mkdir(%s, %s)\n", parent, dir);
  317.     fflush(stdout);
  318. #endif /*DEBUG*/
  319.  
  320.     /* put the node together */
  321.     if ((mknod(dir, S_IFDIR | mode, 0)) < 0) 
  322.         return (MKNODE);
  323.  
  324.     /* make dot */
  325.     strcpy(dotdot, dir);
  326.     strcat(dotdot, "/.");
  327.     if ((link(dir, dotdot)) < 0) 
  328.         return (LINK);
  329.  
  330.     /* make dotdot */
  331.     strcat(dotdot, ".");
  332.     if ((link(parent, dotdot)) < 0) 
  333.         return (LINK);
  334.  
  335.     return (0);
  336. }
  337.  
  338. int
  339. rmdir(dir)
  340. char *dir;
  341. {
  342.     char dots[MAXPATHLEN];
  343.  
  344. #ifdef DEBUG
  345.     printf("rmdir(%s)\n", dir);
  346.     fflush(stdout);
  347. #endif /*DEBUG*/
  348.  
  349.     strcpy(dots, dir);
  350.     strcat(dots, "/.");
  351.  
  352.     /* unlink(".") */
  353.     if (unlink(dots) < 0)
  354.         return (LINK);
  355.  
  356.     /* unlink("..") */
  357.     strcat(dots, ".");
  358.     if (unlink(dots) < 0)
  359.         return (LINK);
  360.  
  361.     /* unlink the directory itself */
  362.     if (unlink(dir) < 0)
  363.         return (LINK);
  364.  
  365.     return (0);
  366. }
  367.  
  368. int
  369. print(s)
  370. char *s;
  371. {
  372.     return (write(2, s, strlen(s)));
  373. }
  374.  
  375. error(s1, s2, err, ecode)
  376. char *s1,
  377.      *s2;
  378. int err,
  379.     ecode;
  380. {
  381.     print(Progname);
  382.     print(": ");
  383.     print(s1);
  384.     errno = err;
  385.     if (s2 != (char *)NULL)
  386.         print(s2);
  387.     if (err != 0) 
  388.         perror(" ");
  389.     else 
  390.         print("\n");
  391.     exit(ecode);
  392. }
  393.  
  394. int
  395. set_sigs(func)
  396. int *(*func)();
  397. {
  398.     register int i;
  399.  
  400.     for (i=1; i<=NSIG ; i++)
  401.         if (signal(i, SIG_IGN) != SIG_IGN)
  402.             signal(i, func); /* point it at the catch() routine. */
  403.  
  404.     return (0);
  405. }
  406.  
  407. int
  408. canIwrite(stbuff, uid, gid)
  409. register struct stat *stbuff;
  410. register unsigned short uid,
  411.                         gid;
  412. {
  413.     /* we let root get away with anything... */
  414.     if (uid == 0)
  415.         return (0);
  416.  
  417.     /* can I write in it as an OWNER ? */
  418.     if (uid == stbuff->st_uid)
  419.         if (stbuff->st_mode & 0200)
  420.             return (0);
  421.         else
  422.     /* okay, so I can't write here.. */
  423.             return (-1);
  424.  
  425.     /* okay, so how about as a GROUP ? */
  426.     if (gid == stbuff->st_gid)
  427.         if (stbuff->st_mode & 0020)
  428.             return (0);
  429.         else
  430.     /* okay, so I can't write here.. */
  431.             return (-1);
  432.  
  433.     /* alright, how about an OTHER ? */
  434.     if (stbuff->st_mode & 0002)
  435.         return (0);
  436.  
  437.     /* okay, so I can't write here.. */
  438.     return (-1);
  439. }
  440.  
  441. #ifdef DEBUG
  442. unlink(s)
  443. char *s;
  444. {
  445.     printf("Unlink(%s)\n", s);
  446.     fflush(stdout);
  447. }
  448. #endif /*DEBUG*/
  449.