Enable authorizer-feature in PDO_SQLITE

php.internals

Mario Wolff

19 years ago
Hello list, i've extended sqlite_driver to get access to the authorizer-feature of sqlite. This is my first contact with the Zend-API and my last c skill is more than a bit outdated. Could someone review/cleanup my code to get it merged to the distribution? Hint and comment welcome! Thanks, Mario Wolff PS: Patch applys against 5.2.0!

Wez Furlong

19 years ago
Two comments about the patch: - could you create a unified diff and post that instead? - static local variable usage is not thread safe; state should be stored in the pdo_dbh_t structure. And one concern: I deliberately left this feature unimplemented so far because the authorization callback will happen "a lot", and frequent callbacks into PHP script will make things slower. I suggest that you adjust your patch to cache the function callback information in the pdo_dbh_t to reduce some of that overhead, and run some benchmarks for a simple authorizer function in PHP that always returns true vs the same script with no authorizer, so that we get a feel for what kind of impact that has for various common queries. One other thing that is important is to ensure that the PHP safe_mode/open_basedir checks have higher priority than the PHP script callback. If safe_mode/open_basedir decide that the path is not accessible, then the PHP script must not be able to override that. However, if safe_mode/open_basedir say that access is ok, the PHP script can optionally override that decision. --Wez. On 11/16/06, Mario Wolff <wolfshoehle@googlemail.com> wrote:

Mario Wolff

19 years ago
@Wez: Whoop, just found you as maintainer in Pecl and send the same request direct to you, sorry! 2006/11/16, Wez Furlong <kingwez@gmail.com>:
> Two comments about the patch: > > - could you create a unified diff and post that instead?
Here it is!
> - static local variable usage is not thread safe; state should be > stored in the pdo_dbh_t structure.
Makes sense, even for cleanup. pdo_sqlite_request_shutdown cant free the callback-zval right now.
> And one concern: > > I deliberately left this feature unimplemented so far because the > authorization callback will happen "a lot", and frequent callbacks > into PHP script will make things slower.
It is only called at compiletime of a query! I think thats not as hard! Maybe it could be an optional feature?
> I suggest that you adjust your patch to cache the function callback > information in the pdo_dbh_t to reduce some of that overhead, and run > some benchmarks for a simple authorizer function in PHP that always > returns true vs the same script with no authorizer, so that we get a > feel for what kind of impact that has for various common queries.
The Sqlite-Api keeps the user-authorizer in the userdata-void-pointer. The static definition is only ment for clean up!
> One other thing that is important is to ensure that the PHP > safe_mode/open_basedir checks have higher priority than the PHP script > callback. If safe_mode/open_basedir decide that the path is not > accessible, then the PHP script must not be able to override that. > However, if safe_mode/open_basedir say that access is ok, the PHP > script can optionally override that decision.
Have a look on my authorizer, the user-authorizer is only called if your rules gives SQLITE_OK. The user-authorizer is called from a default authorizer, not from the sqlite-api!
> --Wez.
Regards, Mario

Mario Wolff

19 years ago
2006/11/16, Wez Furlong <kingwez@gmail.com>:
> I suggest that you adjust your patch to cache the function callback > information in the pdo_dbh_t to reduce some of that overhead, and run > some benchmarks for a simple authorizer function in PHP that always > returns true vs the same script with no authorizer, so that we get a > feel for what kind of impact that has for various common queries.
Is the pdo_sqlite_db_handle not the better place to store? I'm not so deep in the architecture to decide!

Mario Wolff

19 years ago
2006/11/16, Mario Wolff <wolfshoehle@googlemail.com>:
> 2006/11/16, Wez Furlong <kingwez@gmail.com>: > > I suggest that you adjust your patch to cache the function callback > > information in the pdo_dbh_t to reduce some of that overhead, and run > > some benchmarks for a simple authorizer function in PHP that always > > returns true vs the same script with no authorizer, so that we get a > > feel for what kind of impact that has for various common queries. > > Is the pdo_sqlite_db_handle not the better place to store? I'm not so > deep in the architecture to decide!
New patches attached! static-stuff removed. user_authorizer cleanup done.

Mario Wolff

19 years ago
Short test script: <?php $data = array( 'one', 'two', 'three', 'four', 'five', 'six'); $db = new PDO( 'sqlite::memory:'); echo "register authorizer\n"; $db->sqliteSetAuthorizer('auth'); $db->exec( "CREATE TABLE strings( a)"); $insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)'); foreach ( $data as $str) { $insert->execute( array( $str)); } $insert = null; echo "unregister authorizer\n"; $db->sqliteSetAuthorizer(); function auth($type,$arga,$argb,$argc,$argd ){ echo "$type\t$arga\t$argb\t$argc\t$argd\n"; return true; } print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( )); ?> gives: register authorizer SQLITE_INSERT sqlite_master main SQLITE_CREATE_TABLE strings main SQLITE_UPDATE sqlite_master type main SQLITE_UPDATE sqlite_master name main SQLITE_UPDATE sqlite_master tbl_name main SQLITE_UPDATE sqlite_master rootpage main SQLITE_UPDATE sqlite_master sql main SQLITE_READ sqlite_master ROWID main SQLITE_READ sqlite_master name main SQLITE_READ sqlite_master rootpage main SQLITE_READ sqlite_master sql main SQLITE_READ sqlite_master tbl_name main SQLITE_INSERT strings main unregister authorizer Array ( [0] => Array ( [sqlite_version( *)] => 3.3.7 [0] => 3.3.7 ) )

Wez Furlong

19 years ago
I think it would be better to pass in the pdo_dbh_t as the autharg to the C level callback and then use that to determine if any of the expensive work needs to be done in the callback. static int authorizer(....) { pdo_dbh_t *db; /* keep the current safemode / basedir checks "cheap" and fast */ if (existing safe_mode and open_base dir return SQLITE_DENY) { return SQLITE_DENY; } db = (pdo_dbh_t*)autharg; if (db->user_authorizer) { TSRMLS_FETCH(); ... free stuff } } You should take a look at how the pdo_sqlite_fci stuff works and adopt that for the authorizer callback, as it will help improve runtime performance there. It would also be best to register constants for the various SQLITE_XXX codes rather than creating strings and having the PHP code check against strings. This will also improve performance at runtime. --Wez. On 11/16/06, Mario Wolff <wolfshoehle@googlemail.com> wrote:

Mario Wolff

19 years ago
2006/11/16, Wez Furlong <kingwez@gmail.com>:
> I think it would be better to pass in the pdo_dbh_t as the autharg to > the C level callback and then use that to determine if any of the > expensive work needs to be done in the callback.
Saves the sqlite_set_authorizer recall in SetAuthorizer, Looks simpler and cleaner. -> done
> static int authorizer(....) > { > pdo_dbh_t *db; > > /* keep the current safemode / basedir checks "cheap" and fast */ > if (existing safe_mode and open_base dir return SQLITE_DENY) { > return SQLITE_DENY; > } > > db = (pdo_dbh_t*)autharg; > if (db->user_authorizer) { > TSRMLS_FETCH();
What is this? I'm not familiar with the hole php-stuff, sorry! Done by: + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; ... + dbh=(pdo_dbh_t*)autharg; + PDO_CONSTRUCT_CHECK; + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + if( H->user_authorizer ){ OK?
> ... > > free stuff > } > } > > You should take a look at how the pdo_sqlite_fci stuff works and adopt > that for the authorizer callback, as it will help improve runtime > performance there.
Can't find usefull stuff to adopt. Can you hint me further?
> It would also be best to register constants for the various SQLITE_XXX codes > rather than creating strings and having the PHP code check against strings. > This will also improve performance at runtime.
I like talking parameters! But you are right, performance comes first! -> done
> > Short test script:
Modified: <?php $data = array( 'one', 'two', 'three', 'four', 'five', 'six'); $db = new PDO( 'sqlite::memory:'); echo "register authorizer\n"; $db->sqliteSetAuthorizer('auth'); $db->exec( "CREATE TABLE strings( a)"); $insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)'); foreach ( $data as $str) { $insert->execute( array( $str)); } $insert = null; if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){ if( $delete->execute(array( "six" ))){ echo "delete done!\n"; } $delete = null; }else{ echo "delete not prepared\n"; } echo "unregister authorizer\n"; $db->sqliteSetAuthorizer(); if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){ if( $delete->execute(array( "six" ))){ echo "delete done!\n"; } $delete = null; }else{ echo "delete not prepared\n"; } function auth($type,$arga,$argb,$argc,$argd ){ echo "$type\t$arga\t$argb\t$argc\t$argd\n"; if( $type==SQLITE_DELETE ){ return SQLITE_DENY; } return SQLITE_OK; } print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( )); print_r( $db->query( 'SELECT * from strings;')->fetchAll( )); ?>
> > gives:
now: register authorizer 18 sqlite_master main 2 strings main 23 sqlite_master type main 23 sqlite_master name main 23 sqlite_master tbl_name main 23 sqlite_master rootpage main 23 sqlite_master sql main 20 sqlite_master ROWID main 20 sqlite_master name main 20 sqlite_master rootpage main 20 sqlite_master sql main 20 sqlite_master tbl_name main 18 strings main 9 strings main delete not prepared unregister authorizer delete done! Array ( [0] => Array ( [sqlite_version( *)] => 3.3.7 [0] => 3.3.7 ) ) Array ( [0] => Array ( [a] => one [0] => one ) [1] => Array ( [a] => two [0] => two ) [2] => Array ( [a] => three [0] => three ) [3] => Array ( [a] => four [0] => four ) [4] => Array ( [a] => five [0] => five ) ) Regards, Mario

Mario Wolff

19 years ago
Any suggestions or meanings on it? Thanks, Mario 2006/11/17, Mario Wolff <wolfshoehle@googlemail.com>:

Mario Wolff

19 years ago
Any suggestions or meanings on it? Thanks, Mario Short test script: <?php $data = array( 'one', 'two', 'three', 'four', 'five', 'six'); $db = new PDO( 'sqlite::memory:'); echo "register authorizer\n"; $db->sqliteSetAuthorizer('auth'); $db->exec( "CREATE TABLE strings( a)"); $insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)'); foreach ( $data as $str) { $insert->execute( array( $str)); } $insert = null; if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){ if( $delete->execute(array( "six" ))){ echo "delete done!\n"; } $delete = null; }else{ echo "delete not prepared\n"; } echo "unregister authorizer\n"; $db->sqliteSetAuthorizer(); if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){ if( $delete->execute(array( "six" ))){ echo "delete done!\n"; } $delete = null; }else{ echo "delete not prepared\n"; } function auth($type,$arga,$argb,$argc,$argd ){ echo "$type\t$arga\t$argb\t$argc\t$argd\n"; if( $type==SQLITE_DELETE ){ return SQLITE_DENY; } return SQLITE_OK; } print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( )); print_r( $db->query( 'SELECT * from strings;')->fetchAll( )); ?> gives: register authorizer 18 sqlite_master main 2 strings main 23 sqlite_master type main 23 sqlite_master name main 23 sqlite_master tbl_name main 23 sqlite_master rootpage main 23 sqlite_master sql main 20 sqlite_master ROWID main 20 sqlite_master name main 20 sqlite_master rootpage main 20 sqlite_master sql main 20 sqlite_master tbl_name main 18 strings main 9 strings main delete not prepared unregister authorizer delete done! Array ( [0] => Array ( [sqlite_version( *)] => 3.3.7 [0] => 3.3.7 ) ) Array ( [0] => Array ( [a] => one [0] => one ) [1] => Array ( [a] => two [0] => two ) [2] => Array ( [a] => three [0] => three ) [3] => Array ( [a] => four [0] => four ) [4] => Array ( [a] => five [0] => five ) )

Wez Furlong

19 years ago
Hi Mario, Sorry that I didn't get back to you--I often get too busy to follow up on PHP related mail so you need to poke. I'm currently doing some work on PDO and so I'll look at integrating your patch this week; if I don't spot any problems, I'll commit it, otherwise I'll get back to you to discuss it. Thanks for putting in the effort! --Wez. On 12/11/06, Mario Wolff <wolfshoehle@googlemail.com> wrote: