Overriding PHP file operations - Extended access control

php.internals

Øyvind Stegard

22 years ago
Hi, Short story I need to patch PHP to allow me to override all file operations done from PHP scripts. I need this to do some extended access control on the files in the environment where PHP will be running. Looking for hints on wise places to put this functionality. Longer story I am currently working on a project to integrate the use of PHP into an existing web application framework (written in Java). This application framework uses a more advanced file security model than that of standard UNIX permissions (actually, it makes use of WebDAV/ACLs internally, for published resources). I need to make some kind of hook in the PHP source code, which enables me to "override"/control all file operations, so that I can validate them against the ACLs defined for the files in the application framework. I then need to be able to deny/allow the file-operation after checking this. (Users publishing PHP scripts should, of course, not be able to completely bypass the ACL permissions with the use of fopen's, includes, etc. from their scripts.) The UNIX permissions are of no help, since all published files are owned by the WebDAV server process owner (not the same as the PHP process owner). PHP will be running with safe_mode enabled (probably invoked via FastCGI) and with a generally very restrictive configuration. The aim is to only provide a basic way for users to publish PHP-generated pages, in addition to other common resources (xml/html etc.) I am currently playing with the PHP 4.3.7 source code (testing as CGI under Apache 1.3.31). After reading through parts of it (hopefully the parts relevant to my task, including important ones like php.h, zend.h zend_API.h, TSRM.h), I have implemented a simple test in main/safe_mode.c: php_checkuid() This seems to catch all the file operations made from my testing scripts when safe_mode is enabled (safe_mode_gid="0"). I am able to conditionally deny access by returning 1 from the function mentioned above. My preliminary plan is therefore to use this as my entry point for the extended file access checks. It is important that I can be certain that `php_checkuid()' in safe_mode.c will catch all possible file operation scenarios from PHP-scripts (assuming PHP is configured properly). Is this a good place to put this ? I have also looked in other files: main/fopen_wrappers.c main/streams.c ext/standard/php_fopen* ext/standard/file* As you might understand, I am looking for the best place to implement all of this, with the ability to cleanly deny access (with standard PHP error message/exception handling, preferably) . My experience with the PHP source is quite limited at the moment, but the scope of this project is solely concerned with file access operations and security. If anyone can comment on or provide some hints on this, URLs etc., it would be greatly appreciated. Thank you. Regards, Øyvind S.
-- < Øyvind Stegard <oyvind.stegard@usit.uio.no> < USIT, University of Oslo

Sara Golemon

22 years ago
> main/safe_mode.c: php_checkuid() >
That's one good place as all local file system operations *SHOULD* go through this point (if we've done our jobs right). But anytime you change PHP versions you'll need to reapply your patch, possibly rewrite parts of it if the internals have changed noticably.
> I have also looked in other files: > main/fopen_wrappers.c > main/streams.c >
The streams layer is the right place to put hooks like this, and lets you tell one type of file operation (open, read, stat, etc...) but it may be more than you need. You can do it in a couple of ways: 1) You can build your "extended security" thingy as a PHP extension which, when loaded removes the plainfiles wrapper and replaces it with your own version (which may passthru to the original version after checks/logging). This option has the plus of being completely modular from version to version avoiding the need to create wonky hacks everytime, and can even be quickly put-in/taken-out on an as-needed basis. (Hint: This is the route I'd go if I were doing this) 2) You could simply override/modify the plainfiles methods in the streams code. In PHP4 you'll find the plainfiles wrapper in amongst the code in main/streams.c, in PHP5 it's moved into its own file in the main/streams/ directory. This route would be easier, but would require re-doing it everytime you upgrade versions. -Sara P.S. - All of the above assumes that you're ONLY talking about local filesystem access and not remote resources (http, ftp, etc...) or stdio pipes (stdin, stdout, stderr, etc...). If you want to hook ALL streams I/O then I'd probably recommend replacing php_stream_locate_wrapper() in streams.c which is called in response to EVERY user generated stream function (well okay, not socket streams like tcp, udp, unix, those are a bit "special", but it does still include http://, and ftp:// actions).

Wez Furlong

22 years ago
Yeah, what she said ;) But with one additional note: some places in the streams internals make assumptions by comparing the wrapper for the stream with the address of the plain_files_wrapper, so if you do hook it, you'll either need to poke the methods from your extension or patch the PHP sources. If you're wondering what kinds of assumptions, the biggest one that springs to mind is the (probably now infamous) greedy fread/fgets handling, where the behaviour is greedy for local files, but packet based for everything else. --Wez. On Wed, 21 Jul 2004 13:53:22 -0700, Sara Golemon <pollita@php.net> wrote:

Sara Golemon

22 years ago
> If you're wondering what kinds of assumptions, the biggest one that > springs to mind is the (probably now infamous) greedy fread/fgets > handling, where the behaviour is greedy for local files, but packet > based for everything else. >
Thanks Wez, I had a lingering suspicion such shortcuts would exist but wasn't actually aware that the greediness fix did a bypass for local files. Does that cause any issues on networked filesystems? (NFS, SMB, NCP, etc...) Can't think why it would, but.... -Sara

Wez Furlong

22 years ago
Dunno. </short_and_to_the_point> Probably depends on your OS. --Wez. On Wed, 21 Jul 2004 14:23:38 -0700, Sara Golemon <pollita@php.net> wrote: