home *** CD-ROM | disk | FTP | other *** search
- char version[]="mkdir (1.007)";
-
- /*
- * Secure mkdir program, solves that nasty race problem...
- *
- * 13 December 1988 Doug Davis doug@letni.lawnet.com
- * and John Elliott IV iv@trsvax.tandy.com
- *
- * Modification History:
- * M007 9 Jan 89 Gordon Burditt gordon@trsvax.tandy.com
- * - Fixed the signal handler to properly handle
- * previously ignored signals.
- * - Fixed canIwrite() to follow SVID conventions.
- * M006 6 Jan 89 Doug Davis doug@letni.lawnet.com
- * - Fixed M005 so it would not leave the secure
- * tree lying around after recipt of multple
- * signals.
- * M005 5 Jan 89 John Haugh II jfh@rpp386.dallas.tx.us
- * - Added code to abort without createing all of the
- * directoies that were specified.
- * M004 22 Dec 88 Doug Davis doug@letni.lawnet.com
- * - Fixed the error handler for the second makedir() call
- * so it will unlink the securedir if it fails.
- * - Fixed the error handler for chown() so that
- * it removes the working "secure" directories.
- * if it ever fails
- * - cleaned up some odd tabs in the comments, and ran
- * them thru a spell checker.
- * M003 22 Dec 88 John Elliott IV iv@trsvax.tandy.com
- * - Fixed the spelling of my name
- * - General readability cleanup
- * - Fixed backwards #ifdef RAND for srand() call
- * - Saved some storage by declaring error msgs char msg[]
- * - Fixed call order in "cleanup" code so that if the
- * unlink() of the securename fails, we will still try
- * to remove our scratch directory
- * - Some calls to error() had an errno of 0, which meant
- * that mkdir would incorrectly exit 0. Calls to error()
- * now include an exit code, which error() now exits with.
- * - Fixed case where link-into-place failed and left all
- * our flotsam and jetsam around. Now rmdir() everything
- * before we call error().
- * - Changed the two error message symbols which began with
- * "Couldnt_" to begin with "Cant_" so the symbols will
- * be significant in 7 characters, for those so unfortunate
- * not to have flexnames.
- * M002 21 Dec 88 Doug Davis doug@letni.lawnet.com
- * - Cleaned up code, added function declarations
- * - fixed bug in second stat's unlink code
- * M001 19 Dec 88 John Haugh II jfn@rpp386
- * - Fixes oops in original code, much more secure now.
- *
- * Theory of operation:
- * This mkdir first makes a directory to play in, which is
- * owned by root and is mode 000. It is made in the same
- * directory in which the user is requesting his directory.
- * In this "secure" directory, to which the user allegedly
- * has no access, the mknod(), chown(), and links for `.'
- * and `..' are performed. The new directory is then linked
- * into place. Finally, the "secure" directory is removed.
- *
- * This code copyright 1988 by Doug Davis (doug@letni.lawnet.com)
- * You are free to modify, hack, fold, spindle, duplicate, pass-along
- * give-away, publish, transmit, or mutlate this code in any maner,
- * provided that you give credit where credit is due and don't pretend
- * that you wrote it.
- *
- * If you do my lawyers (and I have a lot of lawyers) will teach you a lesson
- * or two in copyright law that you will never ever forget.
- *
- */
-
- #define MAXPATHLEN 128 /* maximum reasonanble path length */
-
- #include <sys/types.h>
- #include <signal.h>
- #include <sys/stat.h>
- #ifdef DEBUG
- # include <stdio.h>
- #else /*DEBUG*/
- # define NULL 0
- #endif /*DEBUG*/
-
- #define MKNODE 1
- #define LINK 2
-
- char Malloc_Failed[] = "malloc() failed.";
- char Doesnt_Exist[] = " does not exist.";
- char Cannot_Access[] = "cannot access ";
- char Already_Exist[] = " already exists.";
- char Secure_Failed[] = "makedir secure parent failed ";
- char Cant_Link[] = "Couldn't link to ";
- char Mkdir_Failed[] = "makedir failed ";
- char Chown_Failed[] = "chown() failed ";
- char Cant_Unlink[] = "Couldn't unlink ";
-
- extern char *STRRCHR();
- extern char *malloc();
-
- extern int errno;
- extern int getpid();
- extern int stat();
- extern int chown();
- extern int unlink();
- extern int strlen();
-
- extern unsigned short getuid();
- extern unsigned short getgid();
- extern long time();
-
-
- char *Progname;
- int signaled = 0;
-
- #ifdef RAND
- extern int rand();
- #else /*RAND*/
- extern int getppid();
- #endif /*RAND*/
-
- /* This function is part of the signal handler, it is used to prevent
- * mkdir from creating all of the directories on the command line,
- * upon recipt of a signal mkdir will finish the directory that it is
- * currently doing then exit
- */
- int
- catch()
- {
- signaled = 1;
- set_sigs(SIG_IGN);
- }
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- Progname = argv[0];
- errno = 0;
-
- if (argc < 2) {
- print("Usage: ");
- print(Progname);
- print(" directory_name [ ... directory_name ]\n");
- exit(0);
- }
-
- /* Catch those nasty signals that could cause us
- * to mess up the filesystem */
- set_sigs(catch);
-
- while (--argc && ! signaled)
- md(*++argv); /* make each directory */
-
- exit(0);
- }
-
-
- int
- md(s)
- char *s;
- {
- register char *basename,
- *parent,
- *fullname;
-
- unsigned short myuserid,
- mygroupid;
-
- char securename[MAXPATHLEN],
- securedir[MAXPATHLEN],
- dotsecurename[MAXPATHLEN];
-
- int rval1,
- rval2;
-
- long snum;
-
- struct stat sanity;
-
- /* find out who I really am */
- myuserid = getuid();
- mygroupid = getgid();
-
- /* set up the pseudo-RANDom number generation system */
- #ifdef RAND
- srand(getpid());
- #endif /*RAND*/
-
- /* see if we are explicit or indirect */
- basename = STRRCHR(s, '/');
- if (basename == (char *)NULL) {
- fullname = malloc(strlen(s)+1);
- if (fullname == (char *)NULL)
- error(Malloc_Failed, (char *)NULL, errno, 1);
- parent = malloc(2);
- if (parent == (char *)NULL)
- error(Malloc_Failed, (char *)NULL, errno, 1);
- parent[0] = '.';
- parent[1] = '\0';
- strcpy(fullname, s);
- basename = s;
- } else {
- fullname = malloc(strlen(s)+1);
- if (fullname == (char *)NULL)
- error(Malloc_Failed, (char *)NULL, errno, 1);
- strcpy(fullname, s);
- *basename = '\0';
- basename++;
- parent = malloc(strlen(s) + 3);
- if (parent == (char *)NULL)
- error(Malloc_Failed, (char *)NULL, errno, 1);
- strcpy(parent, s);
- strcat(parent, "/.");
- }
-
- /* Generate the secure names ... */
- do {
- /* round and round we go; where we stop depends on
- * the non-existance of securedir */
- snum = time((long *) 0L);
- #ifdef RAND
- sprintf(securedir, "%s/%ld", parent,
- (rand() % 2) ? snum + (long)rand() : snum - (long)rand());
- sprintf(securename, "%s/%ld", securedir,
- (rand() % 2) ? snum - (long)rand() : snum + (long)rand());
- sprintf(dotsecurename, "%s/./.", securename);
- #else /*RAND*/
- sprintf(securedir, "%s/%ld", parent, snum - (long)getppid());
- sprintf(securename, "%s/%ld", securedir, snum + (long)getppid());
- sprintf(dotsecurename, "%s/./.", securename);
- snum += (long)getpid();
- #endif /*RAND*/
- } while (stat(securedir, &sanity) == 0);
-
- #ifdef DEBUG
- /* spill the beans .. */
- printf("parent == %s\n", parent);
- printf("basename == %s\n", basename);
- printf("fullname == %s\n", fullname);
- printf("securedir == %s\n", securedir);
- printf("securename == %s\n", securename);
- printf("dotsecurename == %s\n", dotsecurename);
- fflush(stdout);
- #endif /*DEBUG*/
-
- /* let's see if our parent directory is around... */
- if ((stat(parent, &sanity)) != 0)
- error(parent, Doesnt_Exist, 0, 2);
-
- /* find out if we can write here */
- if (canIwrite(&sanity, myuserid, mygroupid) != 0)
- error(Cannot_Access, parent, 0, 2);
-
- /* find out if we are going to stomp on something.. */
- if ((stat(fullname, &sanity)) == 0)
- error(fullname, Already_Exist, 0, 2);
-
- /* make secure parent directory (note the mode of 0) */
- if (makedir(parent, securedir, 0) > 0)
- error(Secure_Failed, securedir, errno, 2);
-
- /* now make our directory underneath it */
- if (makedir(parent, securename, 0777) > 0) {
- rmdir(securedir);
- error(Mkdir_Failed, securedir, errno, 3);
- }
-
- /* do that eerie little chown() that's the "root" of all our problems */
- if (chown(dotsecurename, myuserid, mygroupid) != 0) {
- rmdir(securename);
- rmdir(securedir);
- error(Chown_Failed, dotsecurename, errno, 3);
- }
-
- /* do a quick sanity check, just to annoy someone trying
- * to trick mkdir into chowning something it shouldn't.. */
- if ((stat(fullname, &sanity)) == 0) {
- /* What happened? This wasn't here a couple of calls ago... */
- rmdir(securename);
- rmdir(securedir);
- error(fullname, Already_Exist, 0, 4);
- }
-
- /* okay, put it where it belongs */
- if ((link(securename, fullname)) < 0) {
- rmdir(securename);
- rmdir(securedir);
- error(Cant_Link, fullname, errno, 4);
- }
-
- /* remove all our rubbish, and tidy everything up.. */
- if (parent != (char *)NULL)
- free(parent);
- if (fullname != (char *)NULL)
- free(fullname);
- rval1 = unlink(securename);
- /* Even if the unlink() fails, we really should at least
- * attempt to remove our scratch directory... */
- rval2 = rmdir(securedir);
- if (rval1 < 0)
- error(Cant_Unlink, securename, errno, 5);
- if (rval2 != 0)
- error(Cant_Unlink, securedir, errno, 5);
-
- return (0);
- }
-
- int
- makedir(parent, dir, mode)
- char *parent, *dir;
- int mode;
- {
- char dotdot[MAXPATHLEN];
-
- #ifdef DEBUG
- printf("mkdir(%s, %s)\n", parent, dir);
- fflush(stdout);
- #endif /*DEBUG*/
-
- /* put the node together */
- if ((mknod(dir, S_IFDIR | mode, 0)) < 0)
- return (MKNODE);
-
- /* make dot */
- strcpy(dotdot, dir);
- strcat(dotdot, "/.");
- if ((link(dir, dotdot)) < 0)
- return (LINK);
-
- /* make dotdot */
- strcat(dotdot, ".");
- if ((link(parent, dotdot)) < 0)
- return (LINK);
-
- return (0);
- }
-
- int
- rmdir(dir)
- char *dir;
- {
- char dots[MAXPATHLEN];
-
- #ifdef DEBUG
- printf("rmdir(%s)\n", dir);
- fflush(stdout);
- #endif /*DEBUG*/
-
- strcpy(dots, dir);
- strcat(dots, "/.");
-
- /* unlink(".") */
- if (unlink(dots) < 0)
- return (LINK);
-
- /* unlink("..") */
- strcat(dots, ".");
- if (unlink(dots) < 0)
- return (LINK);
-
- /* unlink the directory itself */
- if (unlink(dir) < 0)
- return (LINK);
-
- return (0);
- }
-
- int
- print(s)
- char *s;
- {
- return (write(2, s, strlen(s)));
- }
-
- error(s1, s2, err, ecode)
- char *s1,
- *s2;
- int err,
- ecode;
- {
- print(Progname);
- print(": ");
- print(s1);
- errno = err;
- if (s2 != (char *)NULL)
- print(s2);
- if (err != 0)
- perror(" ");
- else
- print("\n");
- exit(ecode);
- }
-
- int
- set_sigs(func)
- int *(*func)();
- {
- register int i;
-
- for (i=1; i<=NSIG ; i++)
- if (signal(i, SIG_IGN) != SIG_IGN)
- signal(i, func); /* point it at the catch() routine. */
-
- return (0);
- }
-
- int
- canIwrite(stbuff, uid, gid)
- register struct stat *stbuff;
- register unsigned short uid,
- gid;
- {
- /* we let root get away with anything... */
- if (uid == 0)
- return (0);
-
- /* can I write in it as an OWNER ? */
- if (uid == stbuff->st_uid)
- if (stbuff->st_mode & 0200)
- return (0);
- else
- /* okay, so I can't write here.. */
- return (-1);
-
- /* okay, so how about as a GROUP ? */
- if (gid == stbuff->st_gid)
- if (stbuff->st_mode & 0020)
- return (0);
- else
- /* okay, so I can't write here.. */
- return (-1);
-
- /* alright, how about an OTHER ? */
- if (stbuff->st_mode & 0002)
- return (0);
-
- /* okay, so I can't write here.. */
- return (-1);
- }
-
- #ifdef DEBUG
- unlink(s)
- char *s;
- {
- printf("Unlink(%s)\n", s);
- fflush(stdout);
- }
- #endif /*DEBUG*/
-