mark@applix.UUCP (Mark Fox) (06/17/87)
Does anybody know why access checks file accessibility using the real [ug]id in place of the effective [ug]id? It seems to me that access should agree with open(2) as far as whether a program has write or read access to a file. Inside a set-uid program that assumption obviously doesn't work while uid != euid. Should access have been implemented the other way or should another version of access (eaccess?) exist or am I brain damaged? Is there a better way of implementing eaccess than using open/close, assuming I don't have the file already opened? -- Mark Fox Applix Inc., 112 Turnpike Road, Westboro, MA 01581, (617) 870-0300 uucp: seismo!harvard!m2c!applix!mark
chris@mimsy.UUCP (06/17/87)
In article <530@applix.UUCP> mark@applix.UUCP (Mark Fox) writes: >Does anybody know why access checks file accessibility using the real [ug]id >in place of the effective [ug]id? As it says (or should say) in the manual entry, access is designed for use by setuid and setgid programs. The access call determines whether the real user has whatever permissions; if a program wants to know whether the program has those permissions, it should just try the operation. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: seismo!mimsy!chris
ark@alice.UUCP (06/18/87)
In article <530@applix.UUCP>, mark@applix.UUCP writes: > Does anybody know why access checks file accessibility using the real [ug]id > in place of the effective [ug]id? It seems to me that access should agree > with open(2) as far as whether a program has write or read access to a file. > Inside a set-uid program that assumption obviously doesn't work while > uid != euid. The whole purpose of access(2) is to allow a set-uid program to determine whether the real user can access a file. It's easy to see if the effective user can do it -- just try it and see.
john@xanth.UUCP (06/19/87)
In article <530@applix.UUCP>, mark@applix.UUCP (Mark Fox) writes: > Does anybody know why access checks file accessibility using the real [ug]id > in place of the effective [ug]id? It seems to me that access should agree > with open(2) as far as whether a program has write or read access to a file. > Inside a set-uid program that assumption obviously doesn't work while > uid != euid. Well, the reason that access was added was to make it possible for a set[ug]id program to be able to discern whether or not the real user running it would have access to a certain file. > Is there a better way of implementing eaccess than using open/close, assuming > I don't have the file already opened? Not really. It doesn't even change the access or update times until you do a read or write, respectively, so as long as you don't open O_TRUNC or O_CREAT, you'll be ok. -- John Owens Old Dominion University - Norfolk, Virginia, USA john@ODU.EDU old arpa: john%odu.edu@RELAY.CS.NET +1 804 440 4529 old uucp: {seismo,harvard,sun,hoptoad}!xanth!john
trt@rti.UUCP (Thomas Truscott) (06/19/87)
NEVER EVER USE THE "ACCESS" SYSTEM CALL! The "access" system call was added in UNIX (tm AT&T) V7 to make life easier for SUID programs. It does that, and it also makes life buggier. I have seen two ways in which "access" is used, and both are wrong. 1. To determine, in a SUID program, if the RUID can access a file in a certain way. Say you have a mail program which is SUID root and you ask it to include the file "harmless" in your letter. So the mail program does: if (access("harmless", 04) != 0) burp("Sorry, you do not have read permission for that."); /* LOOPHOLE HERE */ fp = fopen("harmless", "r"); ... read the file into the letter ... This use of "access" is a pathetic attempt at security. A Bad Guy can arrange that "harmless" is a generally readable file at the time of the "access" and then, when "LOOPHOLE" is reached, replace it with a link to "secretfile". The mail program will unknowingly interpolate the secret file into the Bad Guy's letter. The ploy may only work one time in a thousand, but that is too often. 2. To determine, in a program, if a file exists, is readable, etc. This is by far the most common use for "access". It is much more painful to write #include <sys/types.h> #include <sys/stat.h> struct stat sb; if (stat("foo", &sb)) burp("no such file"); than to write if (access("foo", 0)) burp("no such file"); But wait a minute! What if the program is SUID!! Then "access" will be checking the wrong permissions! And even if the program itself is not SUID, it might someday be invoked via one. This use of "access" is an accident waiting to happen. Even ignoring the above problems, it is difficult to use "access" correctly. The manual page points out the glitches of "writable" directories and "executable" files which aren't really. The manual page neglects to mention that if the RUID is root, *all* files appear to be "executable" independent of the permission bits. Vnode (NFS) filesystems have even more features. They have the above glitches, plus the pathname to the file is checked as the EUID, not as the RUID. So a file may appear to be readable by the RUID, yet is not, or vice versa. (I guess this is just SunOS thumbing its nose at "UNIX semantics"). It is time to drop this misguided system call. Tom Truscott
mark@ems.UUCP (06/20/87)
In article <1341@xanth.UUCP> john@xanth.UUCP (John Owens) writes: >In article <530@applix.UUCP>, mark@applix.UUCP (Mark Fox) writes: >> Does anybody know why access checks file accessibility using the real [ug]id >> in place of the effective [ug]id? It seems to me that access should agree >> with open(2) as far as whether a program has write or read access to a file. >> Inside a set-uid program that assumption obviously doesn't work while >> uid != euid. > >Well, the reason that access was added was to make it possible for a >set[ug]id program to be able to discern whether or not the real user >running it would have access to a certain file. The problem with this is that some of the standard System V subroutine libraries use the access call to determine the accessability for suid/sgid programs. An example that comes to mine here is a situation that bit me a while back. The tmpnam call apparently uses the access call to determine whether a temporary file can be created. This can lead to problems if you want to direct temporary files to a directory that is local to a suid/guid program that is executed via uux. Given this scenario, the id=uucp, but the euid=demo (in my case). The temporary directory is owned by demo. However when tmpnam attempts to create a file in /demo/tmp it fails, becase the id(uucp) does not have access the /demo/tmp. Anyways this can be gotten around in a number of ways, including (what we did), writing a new version of access that uses the euid to check for access permissions. As somebody suggested, this could be included in a library and named something like eaccess().
simon@its63b.ed.ac.uk (Simon Brown) (06/22/87)
In article <530@applix.UUCP> mark@applix.UUCP (Mark Fox) writes: >Does anybody know why access checks file accessibility using the real [ug]id >in place of the effective [ug]id? It seems to me that access should agree >with open(2) as far as whether a program has write or read access to a file. Nah, I always thought that access exists to allow you to check whether someone *should* be able to use a file, not whether they actually *can* or not. After all, surely the easist way to check if you can physically open a file is to try opening it and seeing what happens, No? %{ Simon! %} -- ---------------------------------- | Simon Brown | UUCP: seismo!mcvax!ukc!its63b!simon | Department of Computer Science | JANET: simon@uk.ac.ed.its63b | University of Edinburgh, | ARPA: simon%its63b.ed.ac.uk@cs.ucl.ac.uk | Scotland, UK. | ---------------------------------- "Life's like that, you know"
simon@its63b.ed.ac.uk (Simon Brown) (06/22/87)
In article <488@its63b.ed.ac.uk> simon@its63b.ed.ac.uk (Simon Brown) writes: >In article <530@applix.UUCP> mark@applix.UUCP (Mark Fox) writes: >>Does anybody know why access checks file accessibility using the real [ug]id >>in place of the effective [ug]id? It seems to me that access should agree >>with open(2) as far as whether a program has write or read access to a file. > So, as a followup - try three guesses as to how the BSD4.2 test(1) program implements "test -r", "test -w", etc...? Yes, dead right, it uses access!!!! Great, eh? Even worse, it uses it even when trying to check write-access in a directory, which will ALWAYS fail 'cos you can't write in a directory directly. Of course, when you say "test -w directory" what you REALLY want to know is whether you can create/delete files there, but of course test(1) is *far* too stupid to realize this! Anyonw know if this is fixed in 4.3 (being too lazy to check for myself)? %{ Simon! %} -- ---------------------------------- | Simon Brown | UUCP: seismo!mcvax!ukc!its63b!simon | Department of Computer Science | JANET: simon@uk.ac.ed.its63b | University of Edinburgh, | ARPA: simon%its63b.ed.ac.uk@cs.ucl.ac.uk | Scotland, UK. | ---------------------------------- "Life's like that, you know"
jfh@killer.UUCP (John Haugh) (06/22/87)
In article <1341@xanth.UUCP>, john@xanth.UUCP (John Owens) writes: > In article <530@applix.UUCP>, mark@applix.UUCP (Mark Fox) writes: > > Does anybody know why access checks file accessibility using the real [ug]id > > in place of the effective [ug]id? It seems to me that access should agree > > with open(2) as far as whether a program has write or read access to a file. > > Inside a set-uid program that assumption obviously doesn't work while > > uid != euid. > Well, the reason that access was added was to make it possible for a > set[ug]id program to be able to discern whether or not the real user > running it would have access to a certain file. Access() lets the user specify the type of access they plan on requesting, and then says wether the user himself would be allowed, regardles of setuid of setgid privileges. > > Is there a better way of implementing eaccess than using open/close, assuming > > I don't have the file already opened? > > Not really. It doesn't even change the access or update times until > you do a read or write, respectively, so as long as you don't open > O_TRUNC or O_CREAT, you'll be ok. Yes, there is. You only need use getuid(), geteuid(), stat() in order to find out if you can access a file. You only need look at st_mode to determine if the file is accessible. Should be fairly simple to write, just three if-then-else's aught to do it. - John. Disclaimer: No disclaimer. Whatcha gonna do, sue me? "It's never too late to have a happy childhood"
john@xanth.UUCP (John Owens) (07/01/87)
In article <1027@killer.UUCP>, jfh@killer.UUCP (John Haugh) writes: > Access() lets the user specify the type of access they plan on requesting, > and then says wether the user himself would be allowed, regardles of setuid > of setgid privileges. Isn't that what I said? > Yes, there is. You only need use getuid(), geteuid(), stat() in order > to find out if you can access a file. You only need look at st_mode to > determine if the file is accessible. Should be fairly simple to write, > just three if-then-else's aught to do it. Sorry, but if directories followed to get to the file were accessible by the effective [ug]ids, but not the real ids, this could show accessibility when access() correctly would not. Also, this method won't show denied write access on a read-only filesystem. (It also wouldn't take in account groupmasters if you're on a version of UNIX that used them? Did anyone ever use this besides 2.9BSD - they were kind of neat, but not useful enough for us to put into 4.[23]BSD.) It's your Summary: line, saying that you can't open for execute, that gives the lie to my suggestion. If you really need to check for execute access, but don't want to execute the program, you'll need to fork and have the child setgid(getegid()), setuid(geteuid()) and do an access. [If your version of UNIX lets to set[ug]id back to their original values regardless of the effective uid, you can avoid the fork. I don't know if I'm remembering that feature correctly or not.] I don't disagree that an eaccess() would be useful, I just think that it's not necessary. Certainly the fork is extremely inconvenient, but how many times do you want to know whether or not you have execute access to a file without trying to execute it? The shells need to know this, but only for files that fail exec with ENOEXEC and that are readable anyway; therefore, they can use access on read, followed by stat. Corrections and contradictions welcome. [But not self-contradictions :-) ] -- John Owens Old Dominion University - Norfolk, Virginia, USA john@ODU.EDU old arpa: john%odu.edu@RELAY.CS.NET +1 804 440 4529 old uucp: {seismo,harvard,sun,hoptoad}!xanth!john
mark@ems.UUCP (Mark H. Colburn) (07/03/87)
After all the discussion about an access call that uses EFFECTIVE user ids rather than real, including my own two cents worth, here is a copy of code that was written by one of the people here to resolve the exact problem that we have been talking about. The routine is called access in order to overlay the system access call when it is linked into the system, so that tempnam and tmpnam work as I wanted them to for the project. The code is trivial, but I thought that some of you might be interested. /* * McGraw-Hill Demo System, Version 2.0 * * access.c - determine accessibility of a file using "effective" user ID. * * Author: Andrew C. Esh, EMS/McGraw-Hill 06/23/86 * * HISTORY * */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> unsigned short getuid(); access (path, amode) char *path; int amode; { struct stat buf; int tempmode = 0; if (stat(path, &buf) == -1) { return(-1); } if (geteuid() == buf.st_uid) tempmode = (buf.st_mode & 0x01c0) >> 6; if (getegid() == buf.st_gid) tempmode |= (buf.st_mode & 0x038) >> 3; tempmode |= buf.st_mode & 0x07; if ((amode & tempmode) != amode) { return(-1); } return(0); }
mangler@cit-vax.Caltech.Edu (System Mangler) (07/06/87)
In article <489@its63b.ed.ac.uk>, simon@its63b.ed.ac.uk (Simon Brown) writes: > So, as a followup - try three guesses as to how the BSD4.2 test(1) program > implements "test -r", "test -w", etc...? Yes, dead right, it uses access!!!! Nope. It uses open(). But SunOS 3.X apparently did switch to access(), because while "test -w /dev/rmt8" used to be a useful way to determine if the write ring was in, now it always returns true. Probably a change imported from System V... Don Speck speck@vlsi.caltech.edu {seismo,rutgers,ames}!cit-vax!speck
mpl@sfsup.UUCP (07/07/87)
In article <348@ems.UUCP>, mark@ems.UUCP writes: > After all the discussion about an access call that uses EFFECTIVE [text deleted] > if (geteuid() == buf.st_uid) > tempmode = (buf.st_mode & 0x01c0) >> 6; first of all, why not say tempmode = (buf.st_mode >> 6 & 07); rather than making the reader guess that 0x01c0 is 7 <<'ed 6 times > if (getegid() == buf.st_gid) > tempmode |= (buf.st_mode & 0x038) >> 3; > tempmode |= buf.st_mode & 0x07; secondly, these bits shouldn't be or'ed in. In other words, a file which is owned by me and mode 066 can be read or written by anyone BUT me. This is *not* the behavior this code exhibits. A (hopefully correct) solution is: access(path, amode) char *path; int amode; { struct stat buf; int shifter; /* how many bits to shift right */ if (stat(path, &buf)) return -1; if (geteuid() == buf.st_uid) shifter = 6; else if (getegid() == buf.st_gid) shifter = 3; else shifter = 0; amode &= 07; /* mask off only the bits of interest */ if ((amode & (buf.st_mode >> shifter)) != amode) return -1; return 0; }
john@xanth.UUCP (07/08/87)
In article <3158@cit-vax.Caltech.Edu>, mangler@cit-vax.Caltech.Edu (System Mangler) writes: > because while "test -w /dev/rmt8" used to be a useful way to determine > if the write ring was in, now it always returns true. Probably a change > imported from System V... "What you have just seen was performed by an experienced professional. You should not try this at home ...." Seriously, that's a pretty destructive way to check for a write ring; it will write an EOF mark on the current point of the tape, then rewind the tape. Fine, if that's what you're going to do.... Doesn't mt status show you whether or not writes are enabled as one of the status bits? -- John Owens Old Dominion University - Norfolk, Virginia, USA john@ODU.EDU old arpa: john%odu.edu@RELAY.CS.NET +1 804 440 4529 old uucp: {seismo,harvard,sun,hoptoad}!xanth!john
iwelch@agsm.ucla.edu (Ivo Welch) (11/14/90)
On Mach, a BSD 4.3 clone with a gcc compiler: I am trying to establish if a file has the executable attribute set. access() works just fine if I am not the su, or if I su name. However, if I just su, (i.e. become root), all files appear to access() to be executable. Is this a bug on my machine, or am I doing something wrong? /ivo welch ivo@next.agsm.ucla.edu
hugo@saturn.ucsc.edu (Hugo Calendar) (11/14/90)
In article <681@mara.cognet.ucla.edu> iwelch@agsm.ucla.edu (Ivo Welch) writes: >On Mach, a BSD 4.3 clone with a gcc compiler: > >I am trying to establish if a file has the executable attribute set. access() >works just fine if I am not the su, or if I su name. However, if I just su, >(i.e. become root), all files appear to access() to be executable. > >Is this a bug on my machine, or am I doing something wrong? > >/ivo welch ivo@next.agsm.ucla.edu I just tried this out on machines running Mach, AIX, AOS and SunOS 4.1, and I get the behavior you describe under all of those operating systems. This must be a bug/inaccuracy in access () since I can't execute a file that's not executable, even as root. I understand that root was once able to execute _any_ file, but that that caused a lot of problems, and this "feature" was removed; and thus root wasn't allowed to execute a file unless it had at lease one of the execute bits set. Perhaps access () was never modified when root lost the ability to execute files w/o an execute bit set. I tried executing a file as root from the c-shell, and through the execl () call, and in both cases root couldn't execute a file unless it had an execute bit set. This means that it's not just the c-shell that won't let you execute non-executable files, but it's the OS itself. I'd recommend using the status () call to determind if you can execute a file. -Hugo --- Hugo Calendar ...!ucbvax!ucscc!spica!hugo CmpEng/Math Undergraduate 215 Weeks Avenue hugo@saturn.ucsc.edu IBM RT AIX/Mach Sys Admin Santa Cruz, CA 95060 hugo@ucscd.bitnet Work Phone (408) 459-3224 USA (408) 425-5479 Above at University of California, Santa Cruz -- Hugo Calendar ...!ucbvax!ucscc!spica!hugo CmpEng/Math Undergraduate 215 Weeks Avenue hugo@saturn.ucsc.edu IBM RT AIX/Mach Sys Admin Santa Cruz, CA 95060 hugo@ucscd.bitnet Work Phone (408) 459-3224 USA (408) 425-5479 Above at University of California, Santa Cruz
barmar@think.com (Barry Margolin) (11/14/90)
In article <681@mara.cognet.ucla.edu> iwelch@agsm.ucla.edu (Ivo Welch) writes: >I am trying to establish if a file has the executable attribute set. access() >works just fine if I am not the su, or if I su name. However, if I just su, >(i.e. become root), all files appear to access() to be executable. Unix files don't have an "executable attribute". The protection mode includes execute permission bits, though. The superuser implicitly has permission to do whatever it wants (i.e. it ignores the protection modes of files -- that's one of the things that makes it "super"), including executing files that it doesn't have execute access to according to the mode. The access() call determines whether the user (identified by the process's real-userid and real-groupid) has permission to perform the specified operations, so access() is behaving properly. -- Barry Margolin, Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
gwyn@smoke.brl.mil (Doug Gwyn) (11/16/90)
In article <1990Nov14.082556.17480@Think.COM> barmar@think.com (Barry Margolin) writes: >The superuser implicitly has permission to do whatever it wants ... >including executing files that it doesn't have execute access to >according to the mode. Since when? access() is of very little practical use.