[php-src] Issue #21971: Harden open_basedir restrictions in various extensions
[email protected] (LamentXU123)
| Newsgroups | php.bugs |
|---|---|
| Message-ID | <[email protected]> |
Issue: https://github.com/php/php-src/issues/21971
Author: LamentXU123
### Description
Now, several extensions in the php codebase has functions with the ability to read/write outside the open_basedir restrictions. While they all requires some level of file
That is because, the open_basedir restrictions only works on PHP streams, and some of our extensions read/write without it, and therefore bypassing the open_basedir check.
Examples found while auditing similar `open_basedir` behavior:
* `ext/dba`: non-stream DBA handlers such as `gdbm`, `qdbm`, or `lmdb` open paths through their backend libraries, e.g. `gdbm_open()`, `dpopen()`, `mdb_env_open()`.
* `ext/gettext`: `bindtextdomain()` resolves a directory and then passes it to libc/gettext, which later loads `.mo` files from that location.
* `ext/openssl`: SSL context options such as `cafile`, `capath`, and `dh_param` may be passed to OpenSSL APIs such as `SSL_CTX_load_verify_locations()` or `BIO_new_file()`.
* `ext/gd`: FreeType font loading can pass the resolved font path to `FT_New_Face()` after locating it with `access()`.
IMO, all of them are supposed to be fixed, that they should align with the expected open_basedir behavior. I know that the above only work with conditions (e.g. know dba keys for dpopen, or being a .mo file for bindtextdomain) but all of them should only works under the restrictions of open_basedir due to serious safety concerns. Which is simply by adding
```c
#include "main/fopen_wrappers.h"
if (php_check_open_basedir(path)) {
RETURN_FALSE;
}
/* then call native/library open */
```
I don't think this requires a RFC so I would like to directly open this issue to discuss about this. cc @iluuu1994 . Thanks!
Below are some example payloads:
```
<?php
ini_set('open_basedir', __DIR__ . '/allowed');
$db = dba_open('/tmp/outside.gdbm', 'r', 'gdbm');
var_dump(dba_fetch('secret', $db));
```
and
```php
<?php
ini_set('open_basedir', __DIR__ . '/allowed');
bindtextdomain('leak', '/tmp/outside-locale');
textdomain('leak');
echo gettext('secret_key'), "\n";
```