Re: createdb compares strategy as case-sensitive

2024-04-21 Thread Tomas Vondra
On 4/21/24 17:10, Tom Lane wrote:
> Tomas Vondra  writes:
>> On 4/21/24 00:19, Tom Lane wrote:
>>> I'm not suggesting that this is an interesting security vulnerability,
>>> because if you can control the arguments to createdb it's probably
>>> game over long since.  But wrapping the arguments is good for
>>> delivering on-point error messages.  So I'd add a fmtId() call to
>>> LOCALE_PROVIDER too.
> 
>> OK, the attached 0001 patch does these three things - adds the fmtId()
>> for locale_provider, make the comparison case-insensitive for strategy
>> and also removes the comma from the hint.
> 
> LGTM.
> 

Pushed, after tweaking the commit message a bit.

>> The createdb vs. CREATE DATABASE difference made me look if we have any
>> regression tests for CREATE DATABASE, and we don't. I guess it would be
>> good to have some, so I added a couple, for some of the parameters, see
>> 0002. But there's a problem with the locale stuff - this seems to work
>> in plain "make check", but pg_upgrade creates the clusters with
>> different providers etc. which changes the expected output. I'm not sure
>> there's a good way to deal with this ...
> 
> Probably not worth the trouble, really.
> 

Agreed.



regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company




Re: createdb compares strategy as case-sensitive

2024-04-21 Thread Tom Lane
Tomas Vondra  writes:
> On 4/21/24 00:19, Tom Lane wrote:
>> I'm not suggesting that this is an interesting security vulnerability,
>> because if you can control the arguments to createdb it's probably
>> game over long since.  But wrapping the arguments is good for
>> delivering on-point error messages.  So I'd add a fmtId() call to
>> LOCALE_PROVIDER too.

> OK, the attached 0001 patch does these three things - adds the fmtId()
> for locale_provider, make the comparison case-insensitive for strategy
> and also removes the comma from the hint.

LGTM.

> The createdb vs. CREATE DATABASE difference made me look if we have any
> regression tests for CREATE DATABASE, and we don't. I guess it would be
> good to have some, so I added a couple, for some of the parameters, see
> 0002. But there's a problem with the locale stuff - this seems to work
> in plain "make check", but pg_upgrade creates the clusters with
> different providers etc. which changes the expected output. I'm not sure
> there's a good way to deal with this ...

Probably not worth the trouble, really.

regards, tom lane




Re: createdb compares strategy as case-sensitive

2024-04-21 Thread Tomas Vondra


On 4/21/24 00:19, Tom Lane wrote:
> Tomas Vondra  writes:
>> On 4/20/24 22:40, Tom Lane wrote:
>>> Seems reasonable.  The alternative could be to remove createdb.c's use
>>> of fmtId() here, but I don't think that's actually better.
> 
>> Why? It seems to me this is quite close to e.g. LOCALE_PROVIDER, and we
>> don't do fmtId() for that. So why should we do that for STRATEGY?
> 
> Hah, nothing like being randomly inconsistent with adjacent code.
> Every other argument handled by createdb gets wrapped by either
> fmtId or appendStringLiteralConn.
> 
> I think the argument for this is it ensures that the switch value as
> accepted by createdb is the argument that CREATE DATABASE will see.
> Compare
> 
> $ createdb --strategy="foo bar" mydb
> createdb: error: database creation failed: ERROR:  invalid create database 
> strategy "foo bar"
> HINT:  Valid strategies are "wal_log", and "file_copy".
> 
> $ createdb --locale-provider="foo bar" mydb
> createdb: error: database creation failed: ERROR:  syntax error at or near ";"
> LINE 1: CREATE DATABASE mydb LOCALE_PROVIDER foo bar;
> ^
> 
> I'm not suggesting that this is an interesting security vulnerability,
> because if you can control the arguments to createdb it's probably
> game over long since.  But wrapping the arguments is good for
> delivering on-point error messages.  So I'd add a fmtId() call to
> LOCALE_PROVIDER too.
> 
> BTW, someone's taken the Oxford comma too far in that HINT.
> Nobody writes a comma when there are only two alternatives.
> 

OK, the attached 0001 patch does these three things - adds the fmtId()
for locale_provider, make the comparison case-insensitive for strategy
and also removes the comma from the hint.

The createdb vs. CREATE DATABASE difference made me look if we have any
regression tests for CREATE DATABASE, and we don't. I guess it would be
good to have some, so I added a couple, for some of the parameters, see
0002. But there's a problem with the locale stuff - this seems to work
in plain "make check", but pg_upgrade creates the clusters with
different providers etc. which changes the expected output. I'm not sure
there's a good way to deal with this ...

regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL CompanyFrom e7a18c780591673592b2a5eb1e129fcceb17f0fe Mon Sep 17 00:00:00 2001
From: Tomas Vondra 
Date: Sun, 21 Apr 2024 14:19:32 +0200
Subject: [PATCH 1/2] createdb: compare strategy case-insensitive

When specifying the createdb strategy, the documentation suggests valid
options are FILE_COPY and WAL_LOG, but the code does case-sensitive
comparison and accepts only "file_copy" and "wal_log" as valid.

Fixed by doing a case-insensitive comparison using pg_strcasecmp(), same
as for other string parameters.

While at it, apply fmtId() to a nearby "locale_provider". This already
did the comparison in case-insensitive way, but the value would not be
double-quoted, confusing the parser and the error message.

Backpatch to 15, where the strategy was introduced.

Backpatch-through: 15
---
 src/backend/commands/dbcommands.c |  6 +++---
 src/bin/scripts/createdb.c|  2 +-
 src/bin/scripts/t/020_createdb.pl | 10 ++
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 8229dfa1f22..cd06d1270c5 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -1018,15 +1018,15 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 		char	   *strategy;
 
 		strategy = defGetString(dstrategy);
-		if (strcmp(strategy, "wal_log") == 0)
+		if (pg_strcasecmp(strategy, "wal_log") == 0)
 			dbstrategy = CREATEDB_WAL_LOG;
-		else if (strcmp(strategy, "file_copy") == 0)
+		else if (pg_strcasecmp(strategy, "file_copy") == 0)
 			dbstrategy = CREATEDB_FILE_COPY;
 		else
 			ereport(ERROR,
 	(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 	 errmsg("invalid create database strategy \"%s\"", strategy),
-	 errhint("Valid strategies are \"wal_log\", and \"file_copy\".")));
+	 errhint("Valid strategies are \"wal_log\" and \"file_copy\".")));
 	}
 
 	/* If encoding or locales are defaulted, use source's setting */
diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index 007061e756f..30426860efa 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -237,7 +237,7 @@ main(int argc, char *argv[])
 		appendStringLiteralConn(, lc_ctype, conn);
 	}
 	if (locale_provider)
-		appendPQExpBuffer(, " LOCALE_PROVIDER %s", locale_provider);
+		appendPQExpBuffer(, " LOCALE_PROVIDER %s", fmtId(locale_provider));
 	if (icu_locale)
 	{
 		appendPQExpBufferStr(, " ICU_LOCALE ");
diff --git a/src/bin/scripts/t/020_createdb.pl b/src/bin/scripts/t/020_createdb.pl
index 512b55c48a9..4a0e2c883a1 100644
--- a/src/bin/scripts/t/020_createdb.pl
+++ b/src/bin/scripts/t/020_createdb.pl
@@ -255,11 +255,21 

Re: createdb compares strategy as case-sensitive

2024-04-20 Thread Tom Lane
Tomas Vondra  writes:
> On 4/20/24 22:40, Tom Lane wrote:
>> Seems reasonable.  The alternative could be to remove createdb.c's use
>> of fmtId() here, but I don't think that's actually better.

> Why? It seems to me this is quite close to e.g. LOCALE_PROVIDER, and we
> don't do fmtId() for that. So why should we do that for STRATEGY?

Hah, nothing like being randomly inconsistent with adjacent code.
Every other argument handled by createdb gets wrapped by either
fmtId or appendStringLiteralConn.

I think the argument for this is it ensures that the switch value as
accepted by createdb is the argument that CREATE DATABASE will see.
Compare

$ createdb --strategy="foo bar" mydb
createdb: error: database creation failed: ERROR:  invalid create database 
strategy "foo bar"
HINT:  Valid strategies are "wal_log", and "file_copy".

$ createdb --locale-provider="foo bar" mydb
createdb: error: database creation failed: ERROR:  syntax error at or near ";"
LINE 1: CREATE DATABASE mydb LOCALE_PROVIDER foo bar;
^

I'm not suggesting that this is an interesting security vulnerability,
because if you can control the arguments to createdb it's probably
game over long since.  But wrapping the arguments is good for
delivering on-point error messages.  So I'd add a fmtId() call to
LOCALE_PROVIDER too.

BTW, someone's taken the Oxford comma too far in that HINT.
Nobody writes a comma when there are only two alternatives.

regards, tom lane




Re: createdb compares strategy as case-sensitive

2024-04-20 Thread Tomas Vondra



On 4/20/24 22:40, Tom Lane wrote:
> Tomas Vondra  writes:
>> While doing some testing with createdb, I noticed it only accepts
>> file_copy/wal_log as valid strategies, not FILE_COPY/WAL_LOG (which is
>> what the docs say). The same thing applies to CREATE DATABASE.
> 
> Hmm, actually it does work in CREATE DATABASE:
> 
> regression=# create database foo STRATEGY = FILE_COPY;
> CREATE DATABASE
> 
> but it fails in createdb because that does
> 
>   if (strategy)
>   appendPQExpBuffer(, " STRATEGY %s", fmtId(strategy));
> 
> and fmtId will double-quote the strategy if it's upper-case, and then
> the backend grammar doesn't case-fold it, and kaboom.
> 

Oh, right. I should have tested CREATE DATABASE instead of just assuming
it has the same issue ...

>> The problem is that createdb() does the check using strcmp() which is
>> case-sensitive. IMHO this should do pg_strcasecmp() which is what we do
>> for other string parameters nearby.
> 
> Seems reasonable.  The alternative could be to remove createdb.c's use
> of fmtId() here, but I don't think that's actually better.
> 

Why? It seems to me this is quite close to e.g. LOCALE_PROVIDER, and we
don't do fmtId() for that. So why should we do that for STRATEGY?


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company




Re: createdb compares strategy as case-sensitive

2024-04-20 Thread Tom Lane
Tomas Vondra  writes:
> While doing some testing with createdb, I noticed it only accepts
> file_copy/wal_log as valid strategies, not FILE_COPY/WAL_LOG (which is
> what the docs say). The same thing applies to CREATE DATABASE.

Hmm, actually it does work in CREATE DATABASE:

regression=# create database foo STRATEGY = FILE_COPY;
CREATE DATABASE

but it fails in createdb because that does

if (strategy)
appendPQExpBuffer(, " STRATEGY %s", fmtId(strategy));

and fmtId will double-quote the strategy if it's upper-case, and then
the backend grammar doesn't case-fold it, and kaboom.

> The problem is that createdb() does the check using strcmp() which is
> case-sensitive. IMHO this should do pg_strcasecmp() which is what we do
> for other string parameters nearby.

Seems reasonable.  The alternative could be to remove createdb.c's use
of fmtId() here, but I don't think that's actually better.

regards, tom lane