Xref: utzoo comp.lang.c:7703 comp.unix.questions:5903 comp.unix.wizards:6788 Path: utzoo!mnetor!uunet!oddjob!hao!gatech!stratus!strick From: strick@stratus.UUCP (henry strickland) Newsgroups: comp.lang.c,comp.unix.questions,comp.unix.wizards Subject: Re: [braindamaged?] use of access(2) -- long note Message-ID: <1056@stratus.UUCP> Date: 1 Mar 88 23:42:50 GMT References: <59@vsi.UUCP> Reply-To: strick@stratus.UUCP (henry strickland) Distribution: comp Organization: the techwood toaster pastry users group Lines: 111 Keywords: access(2), permissions, setuid/setgid In article <59@vsi.UUCP> friedl@vsi.UUCP (Stephen J. Friedl) writes: > > .... > > The problem is with the access(2) system call. Reading the >manual page of this function makes you think that this is a >handy-dandy "does the file exist?" function and it is used this >way by a lot of programs. The curious/insighful person might >wonder why they would make a system call like this when stat(2) >can trivially do the same job -- access(2) should be access(3). >I wondered for a long time too. > > Access(2) should almost never be used. It does what >stat(2) does except it uses the REAL userid and not the EFFECTIVE >userid like every other system call uses. It is designed for use >by setuid/setgid programs to verify the permissions of the >"underlying" user. (* The question was kind of vague (if it was a question: it's more of a proclamation: "Access(2) should almost never be used.") I think the following answers some aspect of your problem; particularly the { setuid(geteuid()) ; setgid(getegid()) } line may be what you need. *) I also feel that access() should almost never be used, but for quite different reasons. First (and I think you realized it) you're not using access() for what it was intended. /* Like you explained, it was intended for programs such as "mail" to make sure the REAL user/group has permission to write/read stuff from where the user has said to write/read the stuff from. *** There are security holes in this approach. *** These holes are why I say one should rarely use access(). The correct thing to do is { setuid(getuid()) ; getgid(getgid()) } before doing the i/o. This allows the real unix permission checking to do its job. This is the safe approach. ( Even as we speak there are major UNIX "supermicros" being sold that run mail as root and don't use EITHER the flawed access() or the better setuid,setgid approach. I've told them about it, but they haven't fixed it, to my knowledge. But unless you're a real hack-unix guru and think you know ALL the tricks, don't ever assume your machine doesn't have one of these real nasty rootholes. BTW, I think berkeley /bin/mail is an example of setuid-root code done correctly. ) If you later will need the capabilities of your effective uid and gid, fork a child to do the job, and wait for it to die. Then continue. */ But back to your problem. How you should be doing things is also with the real unix permission checking. If you want to know if your effective uid/gid has permission to read a file or directory, you can just try opening it. But if you want to be using access(), do { setuid(geteuid()) ; setgid(getegid()) } (and then, if you like, use access). It seems to me that your entire program could work by doing this at the top of the program and then just forgetting about it. Or if you do need to open/creat some of the real user's files, do all of that open/creating BEFORE doing the above line. Be sure the program has no shell escapes, etc! Opening the real user's files early and eliminating shell escapes seems far less drastic than any of the things you enumerated in your posting. If your program is going to be needing a combination of real and effective permissions at many points throughout, you can either -- keep the origional process with the different real/effective permissions and do a lot of forking when the real permits are needed (or when you give the user a shell escape, etc.) or else -- fork off a child with a pipe to it so that you can give it commands, and let the child run as the real userid: { setuid(getuid()) ; getgid(getgid()) } Then the parent can run as the effective id: { setuid(geteuid()) ; setgid(getegid()) } and you've got either when you need it. Depending on how complex your application is, this could get really ugly. I'm sure that there is a real solution to your problem, without resorting to any of the things your enumerated, or else it would have been fixed already. I think that access() is a late addition to unix (when did it come?). You're right, anything can be done without it, but using stat() as you suggested may be as bad as access(). -- Henry Strickland gatech!strick 404-676-1313