home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!decwrl!bu.edu!wang!news
- From: warren@itexjct.jct.ac.il (Warren Burstein)
- Newsgroups: comp.unix.admin
- Subject: Is this program secure?
- Message-ID: <2333@itexjct.jct.ac.il>
- Date: 27 Jul 92 18:36:51 GMT
- Sender: news@wang.com
- Reply-To: warren@itexjct.jct.ac.il
- Organization: Mail to News Gateway at Wang Labs
- Lines: 189
-
- The problem: people anonymously ftp files into our upload directory -
- these files are owned by ftp. Also, sometimes people who have
- accounts ftp stuff into our uploads dir using their own login - these
- files are owned by the user.
-
- The people who maintain the ftp archives log in as 'archives' rather
- than ftp (to minimize the damage that they can do) and need the files
- to be owned by archives so that they can do things like chmod them.
-
- So I wrote this program which makes sure the user has an euid of
- archives or root and that the file is underneath ~ftp, and if so
- chowns it to archives. The program will be suid root, so I'd like to
- be sure there are no holes (like allowing someone to make a symbolic
- link to /etc, chown passwd and ...). I think the tactic of starting
- from the file and constantly moving up one directory until I hit
- either root or ~ftp will work, but if I've done something stupid, I'd
- rather be told about it now before a hacker finds out.
-
- And if I've re-invented the wheel, please let me know.
-
- /*
- * If the user is USER or root, and the file is underneath ~ftp,
- * chown it to USER.
- */
-
- #include <stdio.h>
- #include <varargs.h>
- #include <pwd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-
- #ifdef BSD
- #define strrchr rindex
- #endif
-
- char *strrchr(), *strcpy(), *strncpy(), *strcat(), *malloc();
- #define TRUE 1
- #define FALSE 0
-
- #define USER "archives"
- #define FTP "ftp"
-
- char *argv0;
-
- main(argc, argv)
- char **argv;
- {
- struct passwd *pw;
- struct stat home_st, root_st;
- int arg, uid = getuid(), user_uid, user_gid;
- char *home_dir;
-
- argv0 = argv[0];
- if (argc <= 1) {
- fprintf(stderr, "usage: %s files\n", argv[0]);
- exit(1);
- }
-
- /*
- * Check that program is suid root. Won't do anything if it isn't
- * so might as well give up.
- */
- if (geteuid() != 0)
- fatal("program is not suid root");
-
- /*
- * Get USER's pw to find uid and gid that files will get changed to
- */
- if ( !(pw = getpwnam(USER)))
- fatal("user %s unknown", USER);
- user_uid = pw->pw_uid;
- user_gid = pw->pw_gid;
-
- /*
- * Get FTP's pw to find it's home dir. Get its stat.
- */
- if ( !(pw = getpwnam(FTP)))
- fatal("user %s unknown", FTP);
- if (stat(home_dir = pw->pw_dir, &home_st))
- fatal("cannot stat dir %s", home_dir);
-
- /*
- * Also get root's stat.
- */
- if (stat("/", &root_st))
- fatal("cannot stat /");
-
- /*
- * Check that user is USER or root.
- */
- if (uid != 0 && uid != user_uid)
- fatal("you are not logged in as %s or root", USER);
-
- /*
- * Change the files if underneath ~ftp.
- */
- for (arg = 1; arg < argc; arg++)
- if ( !underneath(&home_st, &root_st, argv[arg]))
- fprintf(stderr, "%s: file %s is not underneath %s\n",
- argv0, argv[arg], home_dir);
- else if (chown(argv[arg], user_uid, user_gid))
- perror(argv[arg]);
- }
-
- /*
- * Is file underneath home_dir?
- *
- * Get the dir that the file is in
- * if it's identical to home_dir it's underneath
- * if it's identical to / it's not.
- * if it's not identical to either, append /.. and try again
- */
- int underneath(home_st, root_st, file)
- char *file;
- struct stat *home_st, *root_st;
- {
- char *dir;
- struct stat st;
-
- /*
- * If file contains a slash, its dir is the file truncated at the last
- * slash. Otherwise its dir is ..
- */
- if (dir = strrchr(file, '/')) {
- int len = dir - file;
-
- if ( !(dir = malloc(len + 1)))
- fatal("cannot alloc for %s", file);
- strncpy(dir, file, len);
- dir[len] = '\0';
- } else {
- if ( !(dir = malloc(3)))
- fatal("cannot alloc for ..", file);
- strcpy(dir, "..");
- }
-
- /*
- * Keep moving to .. until we hit either the root dir or the home dir.
- */
- #define SAME_INODE(p, q) ((p)->st_ino==(q)->st_ino && (p)->st_dev==(q)->st_dev)
- for (;;) {
- char *new_dir;
-
- if (stat(dir, &st))
- fatal("cannot stat dir %s", dir);
-
- if (SAME_INODE(&st, root_st)) {
- free(dir);
- return FALSE;
- }
-
- if (SAME_INODE(&st, home_st)) {
- free(dir);
- return TRUE;
- }
-
- /*
- * Append /..
- */
- if ( !(new_dir = malloc(strlen(dir) + 4)))
- fatal("cannot alloc for %s/..", file);
- strcpy(new_dir, dir);
- strcat(new_dir, "/..");
- free(dir);
- dir = new_dir;
- }
- }
-
- fatal(va_alist)
- va_dcl
- {
- va_list args;
- char *fmt;
- extern char *argv0;
-
- va_start(args);
- fmt = va_arg(args, char *);
- (void) fprintf(stderr, "%s: ", argv0);
- (void) vfprintf(stderr, fmt, args);
- (void) fprintf(stderr, "\n");
- va_end(args);
-
- exit(1);
- }
- --
- /|/-\/-\ The entire kitchen Jerusalem
- |__/__/_/ is a very Czarish apple.
- |warren@ But the Kibo
- / nysernet.org is not all that paranoid.
-