From 809c3cc96f75c2c824f51c4a99bc2f7230854430 Mon Sep 17 00:00:00 2001 From: Will Fitch Date: Thu, 20 Sep 2012 16:07:04 -0400 Subject: [PATCH 1/3] Bug #62479 Fix bug where spaces in passwords would fail Change-Id: I53679937d69b0a8897961900ebf33d197b8ce018 --- ext/pdo_pgsql/pgsql_driver.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 23ea5e58a256..38e99cfd19fa 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -1037,8 +1037,8 @@ static struct pdo_dbh_methods pgsql_methods = { static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */ { pdo_pgsql_db_handle *H; - int ret = 0; - char *conn_str, *p, *e; + int ret = 0, password_len = 0; + char *conn_str, *p, *e, *tmp_pass = NULL; long connect_timeout = 30; H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); @@ -1056,23 +1056,44 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ *p = ' '; } + /* If the password is defined, we need to account for special chars */ + if (dbh->password) { + password_len = strlen(dbh->password); + /* If the password isn't already quoted, let's quote it */ + if (dbh->password[0] != '\'' && dbh->password[password_len - 1] != '\'') { + tmp_pass = emalloc(sizeof(dbh->password) + 3); + snprintf(tmp_pass, sizeof(dbh->password) + 3, "'%s'", dbh->password); + } else { + /* Our default is to just use what password has been provided - + * assuming it is already surrounded by quotes. This keeps BC for + * users who already use workarounds + */ + tmp_pass = estrdup(dbh->password); + } + } + if (driver_options) { connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 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, dbh->password, connect_timeout); + 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, dbh->password, connect_timeout); + 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); + /* Free the tmp password created above */ + if (dbh->password) { + efree(tmp_pass); + } + efree(conn_str); if (PQstatus(H->server) != CONNECTION_OK) { From b6cf3263b1daef9ec40155282ba6a1de3583c679 Mon Sep 17 00:00:00 2001 From: Will Fitch Date: Fri, 28 Sep 2012 16:41:59 -0400 Subject: [PATCH 2/3] Bug #62479 Updated to escape single quotes --- ext/pdo_pgsql/pgsql_driver.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 38e99cfd19fa..242690c46342 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -1059,10 +1059,33 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ /* If the password is defined, we need to account for special chars */ if (dbh->password) { password_len = strlen(dbh->password); + /* If the password isn't already quoted, let's quote it */ if (dbh->password[0] != '\'' && dbh->password[password_len - 1] != '\'') { - tmp_pass = emalloc(sizeof(dbh->password) + 3); - snprintf(tmp_pass, sizeof(dbh->password) + 3, "'%s'", dbh->password); + char *source, *target, *end; + int new_password_length = 0; + tmp_pass = (char *) safe_emalloc(2, password_len, 1); + source = dbh->password; + end = source + password_len; + target = tmp_pass; + *target++ = '\''; + + while (source < end) { + switch (*source) { + case '\'': + *target++ = '\\'; + default: + *target++ = *source; + break; + } + + source++; + } + + *target++ = '\''; + *target++ = 0; + new_password_length = target - tmp_pass; + tmp_pass = (char *) erealloc(tmp_pass, new_password_length); } else { /* Our default is to just use what password has been provided - * assuming it is already surrounded by quotes. This keeps BC for From af1f577fd11f2e22980f60fdc13b40b96f5e3384 Mon Sep 17 00:00:00 2001 From: Will Fitch Date: Fri, 28 Sep 2012 17:59:25 -0400 Subject: [PATCH 3/3] Bug #62479 Added test for passwords with spaces and single quotes --- ext/pdo_pgsql/tests/bug62479.phpt | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 ext/pdo_pgsql/tests/bug62479.phpt diff --git a/ext/pdo_pgsql/tests/bug62479.phpt b/ext/pdo_pgsql/tests/bug62479.phpt new file mode 100644 index 000000000000..9b1e454c6092 --- /dev/null +++ b/ext/pdo_pgsql/tests/bug62479.phpt @@ -0,0 +1,56 @@ +--TEST-- +PDO PgSQL Bug #62479 (PDO-psql cannot connect if password contains spaces) +--SKIPIF-- +query($testQuery); +$testQuery = "DROP USER pdo_$rand"; +$db->query($testQuery); +?> +--FILE-- +setAttribute(PDO::ATTR_EMULATE_PREPARES, true); +$rand = rand(5, 5); +$user = "pdo_$rand"; +$template = "CREATE USER $user WITH PASSWORD '%s'"; +$dropUser = "DROP USER $user"; +$testQuery = 'SELECT 1 as verification'; + +// Create temp user with space in password +$sql = sprintf($template, 'my password'); +$pdo->query($sql); + +$testConn = new PDO($config['ENV']['PDOTEST_DSN'], $user, "my password"); +$result = $testConn->query($testQuery); +var_dump($result->fetch()[0]); + +// Remove the user +$pdo->query($dropUser); + +// Create a user with a space and single quote +$sql = sprintf($template, "my pass\'word"); +$pdo->query($sql); + +$testConn = new PDO($config['ENV']['PDOTEST_DSN'], $user, "my pass'word"); +$result = $testConn->query($testQuery); +var_dump($result->fetch()[0]); + +// Remove the user +$pdo->query($dropUser); + +?> +--EXPECTF-- +int(1) +int(1) +