[comp.unix.questions] access

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.