From: Matteo Beccati Date: Thu, 11 Jun 2015 21:41:56 +0000 (+0200) Subject: Fix bug #69362 (PDO-pgsql fails to connect if password contains a leading single... X-Git-Tag: php-5.5.27RC1~9^2~15 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7c0b8f872e3c15d50b1dc2d35be3674c24f82bf6;p=php Fix bug #69362 (PDO-pgsql fails to connect if password contains a leading single quote) --- diff --git a/NEWS b/NEWS index cbebd97f78..80c2f9e7d8 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ - PDO_pgsql: . Fixed bug #69752 (PDOStatement::execute() leaks memory with DML Statements when closeCuror() is u). (Philip Hofstetter) + . Fixed bug #69362 (PDO-pgsql fails to connect if password contains a + leading single quote). (Matteo) - SimpleXML: . Refactored the fix for bug #66084 (simplexml_load_string() mangles empty diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index a5c2d18ac9..b2c6189ee3 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -27,6 +27,7 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/php_string.h" #include "pdo/php_pdo.h" #include "pdo/php_pdo_driver.h" #include "ext/standard/file.h" @@ -60,6 +61,17 @@ static char * _pdo_pgsql_trim_message(const char *message, int persistent) return tmp; } +static char * _pdo_pgsql_escape_credentials(char *str TSRMLS_DC) +{ + int len; + + if (str) { + return php_addcslashes(str, strlen(str), &len, 0, "\\'", sizeof("\\'") TSRMLS_CC); + } + + return NULL; +} + int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC) /* {{{ */ { pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; @@ -1055,7 +1067,7 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ pdo_pgsql_db_handle *H; int ret = 0; char *conn_str, *p, *e; - char *tmp_pass; + char *tmp_user, *tmp_pass; long connect_timeout = 30; H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); @@ -1063,7 +1075,7 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ H->einfo.errcode = 0; H->einfo.errmsg = NULL; - + /* PostgreSQL wants params in the connect string to be separated by spaces, * if the PDO standard semicolons are used, we convert them to spaces */ @@ -1077,42 +1089,27 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC); } - if (dbh->password) { - if (dbh->password[0] != '\'' && dbh->password[strlen(dbh->password) - 1] != '\'') { - char *pwd = dbh->password; - int pos = 1; - - tmp_pass = safe_emalloc(2, strlen(dbh->password), 3); - tmp_pass[0] = '\''; - - while (*pwd != '\0') { - if (*pwd == '\\' || *pwd == '\'') { - tmp_pass[pos++] = '\\'; - } - - tmp_pass[pos++] = *pwd++; - } - - tmp_pass[pos++] = '\''; - tmp_pass[pos] = '\0'; - } else { - tmp_pass = dbh->password; - } - } + /* escape username and password, if provided */ + tmp_user = _pdo_pgsql_escape_credentials(dbh->username TSRMLS_CC); + tmp_pass = _pdo_pgsql_escape_credentials(dbh->password TSRMLS_CC); /* support both full connection string & connection string + login and/or password */ - if (dbh->username && dbh->password) { - spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, tmp_pass, connect_timeout); - } else if (dbh->username) { - spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout); - } else if (dbh->password) { - spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, tmp_pass, connect_timeout); + if (tmp_user && tmp_pass) { + spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=%ld", dbh->data_source, tmp_user, tmp_pass, connect_timeout); + } else if (tmp_user) { + spprintf(&conn_str, 0, "%s user='%s' connect_timeout=%ld", dbh->data_source, tmp_user, connect_timeout); + } else if (tmp_pass) { + spprintf(&conn_str, 0, "%s password='%s' connect_timeout=%ld", dbh->data_source, tmp_pass, connect_timeout); } else { spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout); } H->server = PQconnectdb(conn_str); - if (dbh->password && tmp_pass != dbh->password) { + + if (tmp_user) { + efree(tmp_user); + } + if (tmp_pass) { efree(tmp_pass); } diff --git a/ext/pdo_pgsql/tests/bug69362.phpt b/ext/pdo_pgsql/tests/bug69362.phpt new file mode 100644 index 0000000000..33212a8863 --- /dev/null +++ b/ext/pdo_pgsql/tests/bug69362.phpt @@ -0,0 +1,63 @@ +--TEST-- +PDO PgSQL Bug #69362 (PDO-pgsql fails to connect if password contains a leading single quote) +--SKIPIF-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + +$user = 'pdo_test_'.rand(5, 400); +$pass = 'testpass'; + +// Assume that if we can't create or drop a user, this test needs to be skipped +try { + $db->exec("DROP USER IF EXISTS $user"); + $db->exec("CREATE USER $user WITH PASSWORD '$pass'"); +} catch (PDOException $e) { + die("skip You need CREATEUSER permissions to run the test"); +} + +// Peer authentication might prevent the test from properly running +try { + $testConn = new PDO($dsn, $user, $pass); +} catch (PDOException $e) { + echo "skip ".$e->getMessage(); +} + +$db->exec("DROP USER $user"); + +?> +--FILE-- +setAttribute(PDO::ATTR_EMULATE_PREPARES, true); +$rand = rand(5, 400); +$user = "pdo_test_$rand"; +$template = "CREATE USER $user WITH PASSWORD '%s'"; +$dropUser = "DROP USER $user"; +$testQuery = 'SELECT 1 as verification'; + +// Create temp user with leading single quote +$sql = sprintf($template, "''mypassword"); +$pdo->query($sql); +$testConn = new PDO($conf['ENV']['PDOTEST_DSN'], $user, "'mypassword"); +$result = $testConn->query($testQuery)->fetch(); +$check = $result[0]; +var_dump($check); + +// Remove the user +$pdo->query($dropUser); + +?> +--EXPECT-- +int(1) +