diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 23ea5e58a256..242690c46342 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,67 @@ 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] != '\'') { + 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 + * 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) { 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) +