Xref: utzoo comp.unix.questions:6276 comp.unix.wizards:7394 Path: utzoo!utgpu!water!watmath!clyde!att-cb!att-ih!gargoyle!oddjob!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.unix.questions,comp.unix.wizards Subject: mkdir and access(2) Keywords: access(2), permissions, setuid/setgid Message-ID: <10811@mimsy.UUCP> Date: 26 Mar 88 20:32:55 GMT References: <59@vsi.UUCP> <1056@stratus.UUCP> <70@vsi.UUCP> <305@wsccs.UUCP> <368@wsccs.UUCP> Distribution: comp Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 72 In article <368@wsccs.UUCP> terry@wsccs.UUCP (terry) writes: >... for instance, if mknod() had to be done by root, as the only user >capable of modifying a file system, as is the case in a number of recent >UNIX implimentations. Your "mkdir" command would have to be SUID root, >but still be able to tell who actually ran it. This is precisely the problem. When a setuid program is running and needs to make a new directory, it must (on these systems) run the `mkdir' program. That setuid program's permissions are in fact those of the EFFECTIVE user, not the real user; but mkdir will check only the permissions of the real user. Often this is not what is desired. Example: % ls -l foo -rwsr-xr-x 1 jane misc 12345 Mar 26 14:27 foo % cat foo.c ... int mkdir(dirname) /* not quite right */ char *dirname; { char buf[1000]; (void) sprintf(buf, "/bin/mkdir %s", dirname); if (system(buf) == 0) return (0); errno = EPERM; /* wrong! must scan output from /bin/mkdir */ return (-1); } ... realuser = uid_to_name(getuid()); if (chdir("/tmp/jane/foousers") < 0) ... if (stat(realuser, &st) < 0 && errno == ENOENT) err = mkdir(realuser); ... % ls -ldg /tmp/jane/foousers drwxr-xr-x 3 jane misc 512 Mar 25 17:34 foousers % whoami joe % ./foo mkdir: joe: Not owner (or is it Permission denied?) % There is a way around it, but it is ugly: if (stat(realuser, &st) < 0 && errno == ENOENT) { /* BEGIN mkdir */ int pid, w, status; switch (pid = fork()) { case -1: /* error */ ... case 0: /* child */ (void) setgid(getgid()); (void) setuid(getuid()); _exit(mkdir(realuser) == 0 ? 0 : errno); /*NOTREACHED*/ default: /* parent */ while ((w = wait(&status)) != pid) if (w == -1) uh oh. break; } err = status == 0 ? 0 : -1; if (err) errno = status >> 8; /* END mkdir */ } The above can be done. It just is not done often enough. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris