Thread: [PoC] Let libpq reject unexpected authentication requests

[PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
Hello,

TL;DR: this patch lets you specify exactly one authentication method in
the connection string, and libpq will fail the connection if the server
doesn't use that method.

(This is not intended for PG15. I'm generally anxious about posting
experimental work during a commitfest, but there's been enough
conversation about this topic recently that I felt like it'd be useful
to have code to point to.)

== Proposal and Alternatives ==

$subject keeps coming up in threads. I think my first introduction to
it was after the TLS injection CVE, and then it came up again in the
pluggable auth thread. It's hard for me to generalize based on "sound
bites", but among the proposals I've seen are

1. reject plaintext passwords
2. reject a configurable list of unacceptable methods
3. allow client and server to negotiate a method

All of them seem to have merit. I'm personally motivated by the case
brought up by the CVE: if I'm expecting client certificate
authentication, it's not acceptable for the server to extract _any_
information about passwords from my system, whether they're plaintext,
hashed, or SCRAM-protected. So I chose not to implement option 1. And
option 3 looked like a lot of work to take on in an experiment without
a clear consensus.

Here is my take on option 2, then: you get to choose exactly one method
that the client will accept. If you want to use client certificates,
use require_auth=cert. If you want to force SCRAM, use
require_auth=scram-sha-256. If the server asks for something different,
libpq will fail. If the server tries to get away without asking you for
authentication, libpq will fail. There is no negotiation.

== Why Force Authn? ==

I think my decision to fail if the server doesn't authenticate might be
controversial. It doesn't provide additional protection against active
attack unless you're using a mutual authentication method (SCRAM),
because you can't prove that the server actually did anything with its
side of the handshake. But this approach grew on me for a few reasons:

- When using SCRAM, it allows the client to force a server to
authenticate itself, even when channel bindings aren't being used. (I
really think it's weird that we let the server get away with that
today.)

- In cases where you want to ensure that your actions are logged for
later audit, you can be reasonably sure that you're connecting to a
database that hasn't been configured with a `trust` setting.

- For cert authentication, it ensures that the server asked for a cert
and that you actually sent one. This is more forward-looking; today, we
always ask for a certificate from the client even if we don't use it.
But if implicit TLS takes off, I'd expect to see more middleware, with
more potential for misconfiguration.

== General Thoughts ==

I like that this approach fits nicely into the existing code. The
majority of the patch just beefs up check_expected_areq(). The new flag
that tracks whether or not we've authenticated is scattered around more
than I would like, but I'm hopeful that some of the pluggable auth
conversations will lead to nice refactoring opportunities for those
internal helpers.

There's currently no way to prohibit client certificates from being
sent. If my use case is "servers shouldn't be able to extract password
info if the client expects certificates", then someone else may very
well say "servers shouldn't be able to extract my client certificate if
I wanted to use SCRAM". Likewise, this feature won't prevent a GSS
authenticated channel -- but we do have gssencmode=disable, so I'm less
concerned there.

I made the assumption that a GSS encrypted channel authenticates both
parties to each other, but I don't actually know what guarantees are
made there. I have not tested SSPI.

I'm not a fan of the multiple spellings of "password" ("ldap", "pam",
et al). My initial thought was that it'd be nice to match the client
setting to the HBA setting in the server. But I don't think it's really
all that helpful; worst-case, it pretends to do something it can't,
since the client can't determine what the plaintext password is
actually used for on the backend. And if someone devises (say) a SASL
scheme for proxied LDAP authentication, that spelling becomes obsolete.

Speaking of obsolete, the current implementation assumes that any SASL
exchange must be for SCRAM. That won't be anywhere close to future-
proof.

Thanks,
--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Tom Lane
Date:
Jacob Champion <pchampion@vmware.com> writes:
> $subject keeps coming up in threads. I think my first introduction to
> it was after the TLS injection CVE, and then it came up again in the
> pluggable auth thread. It's hard for me to generalize based on "sound
> bites", but among the proposals I've seen are

> 1. reject plaintext passwords
> 2. reject a configurable list of unacceptable methods
> 3. allow client and server to negotiate a method

> All of them seem to have merit.

Agreed.

> Here is my take on option 2, then: you get to choose exactly one method
> that the client will accept. If you want to use client certificates,
> use require_auth=cert. If you want to force SCRAM, use
> require_auth=scram-sha-256. If the server asks for something different,
> libpq will fail. If the server tries to get away without asking you for
> authentication, libpq will fail. There is no negotiation.

Seems reasonable, but I bet that for very little more code you could
accept a comma-separated list of allowed methods; libpq already allows
comma-separated lists for some other connection options.  That seems
like it'd be a useful increment of flexibility.

            regards, tom lane



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Fri, Mar 04, 2022 at 08:19:26PM -0500, Tom Lane wrote:
> Jacob Champion <pchampion@vmware.com> writes:
>> Here is my take on option 2, then: you get to choose exactly one method
>> that the client will accept. If you want to use client certificates,
>> use require_auth=cert. If you want to force SCRAM, use
>> require_auth=scram-sha-256. If the server asks for something different,
>> libpq will fail. If the server tries to get away without asking you for
>> authentication, libpq will fail. There is no negotiation.

Fine by me to put all the control on the client-side, that makes the
whole much simpler to reason about.

> Seems reasonable, but I bet that for very little more code you could
> accept a comma-separated list of allowed methods; libpq already allows
> comma-separated lists for some other connection options.  That seems
> like it'd be a useful increment of flexibility.

Same impression here, so +1 for supporting a comma-separated list of
values here.  This is already handled in parse_comma_separated_list(),
now used for multiple hosts and hostaddrs.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Andrew Dunstan
Date:
On 3/4/22 20:19, Tom Lane wrote:
> Jacob Champion <pchampion@vmware.com> writes:
>> $subject keeps coming up in threads. I think my first introduction to
>> it was after the TLS injection CVE, and then it came up again in the
>> pluggable auth thread. It's hard for me to generalize based on "sound
>> bites", but among the proposals I've seen are
>> 1. reject plaintext passwords
>> 2. reject a configurable list of unacceptable methods
>> 3. allow client and server to negotiate a method
>> All of them seem to have merit.
> Agreed.
>
>> Here is my take on option 2, then: you get to choose exactly one method
>> that the client will accept. If you want to use client certificates,
>> use require_auth=cert. If you want to force SCRAM, use
>> require_auth=scram-sha-256. If the server asks for something different,
>> libpq will fail. If the server tries to get away without asking you for
>> authentication, libpq will fail. There is no negotiation.
> Seems reasonable, but I bet that for very little more code you could
> accept a comma-separated list of allowed methods; libpq already allows
> comma-separated lists for some other connection options.  That seems
> like it'd be a useful increment of flexibility.
>
>             


Just about necessary I guess, since you can specify that a client cert
is required in addition to some other auth method, so for such cases you
might want something like "required_auth=cert,scram-sha-256"? Or do we
need a way of specifying the combination?


cheers


andrew


--
Andrew Dunstan
EDB: https://www.enterprisedb.com




Re: [PoC] Let libpq reject unexpected authentication requests

From
Tom Lane
Date:
Andrew Dunstan <andrew@dunslane.net> writes:
> On 3/4/22 20:19, Tom Lane wrote:
>> Seems reasonable, but I bet that for very little more code you could
>> accept a comma-separated list of allowed methods; libpq already allows
>> comma-separated lists for some other connection options.  That seems
>> like it'd be a useful increment of flexibility.

> Just about necessary I guess, since you can specify that a client cert
> is required in addition to some other auth method, so for such cases you
> might want something like "required_auth=cert,scram-sha-256"? Or do we
> need a way of specifying the combination?

I'd view the comma as strictly meaning OR, so that you might need some
notation like "required_auth=cert+scram-sha-256" if you want to demand
ANDed conditions.  It might be better to handle TLS-specific
conditions orthogonally to the authentication exchange, though.

            regards, tom lane



Re: [PoC] Let libpq reject unexpected authentication requests

From
Laurenz Albe
Date:
On Sat, 2022-03-05 at 01:04 +0000, Jacob Champion wrote:
> TL;DR: this patch lets you specify exactly one authentication method in
> the connection string, and libpq will fail the connection if the server
> doesn't use that method.
> 
> (This is not intended for PG15. I'm generally anxious about posting
> experimental work during a commitfest, but there's been enough
> conversation about this topic recently that I felt like it'd be useful
> to have code to point to.)
> 
> == Proposal and Alternatives ==
> 
> $subject keeps coming up in threads. I think my first introduction to
> it was after the TLS injection CVE, and then it came up again in the
> pluggable auth thread. It's hard for me to generalize based on "sound
> bites", but among the proposals I've seen are
> 
> 1. reject plaintext passwords
> 2. reject a configurable list of unacceptable methods
> 3. allow client and server to negotiate a method
> 
> All of them seem to have merit. I'm personally motivated by the case
> brought up by the CVE: if I'm expecting client certificate
> authentication, it's not acceptable for the server to extract _any_
> information about passwords from my system, whether they're plaintext,
> hashed, or SCRAM-protected. So I chose not to implement option 1. And
> option 3 looked like a lot of work to take on in an experiment without
> a clear consensus.
> 
> Here is my take on option 2, then: you get to choose exactly one method
> that the client will accept.

I am all for the idea, but you implemented the reverse of proposal 2.

Wouldn't it be better to list the *rejected* authentication methods?
Then we could have "password" on there by default.

Yours,
Laurenz Albe




Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Mon, 2022-03-07 at 11:44 +0100, Laurenz Albe wrote:
> I am all for the idea, but you implemented the reverse of proposal 2.

(This email was caught in my spam filter; sorry for the delay.)

> Wouldn't it be better to list the *rejected* authentication methods?
> Then we could have "password" on there by default.

Specifying the allowed list rather than the denied list tends to have
better security properties.

In the case I'm pursuing (the attack vector from the CVE), the end user
expects certificates to be used. Any other authentication method --
plaintext, hashed, SCRAM, Kerberos -- is unacceptable; it shouldn't be
possible for the server to extract any information about the client
environment other than the cert. And I don't want to have to specify
the whole list of things that _aren't_ allowed, and keep that list
updated as we add new fancy auth methods, if I just want certs to be
used. So that's my argument for making the methods opt-in rather than
opt-out.

But that doesn't help your case; you want to choose a good default, and
I agree that's important. Since there are arguments already for
accepting a OR in the list, and -- if we couldn't find a good
orthogonal method for certs, like Tom suggested -- an AND, maybe it
wouldn't be so bad to accept a NOT as well?

    require_auth=cert                # certs only
    require_auth=cert+scram-sha-256  # SCRAM wrapped by certs
    require_auth=cert,scram-sha-256  # SCRAM or certs (or both)
    require_auth=!password           # anything but plaintext
    require_auth=!password,!md5      # no plaintext or MD5

But it doesn't ever make sense to mix them:

    require_auth=cert,!password      # error: !password is useless
    require_auth=!password,password  # error: nonsense

--Jacob

Re: [PoC] Let libpq reject unexpected authentication requests

From
Laurenz Albe
Date:
On Wed, 2022-03-23 at 21:31 +0000, Jacob Champion wrote:
> On Mon, 2022-03-07 at 11:44 +0100, Laurenz Albe wrote:
> > I am all for the idea, but you implemented the reverse of proposal 2.
> >
> > Wouldn't it be better to list the *rejected* authentication methods?
> > Then we could have "password" on there by default.
> 
> Specifying the allowed list rather than the denied list tends to have
> better security properties.
> 
> In the case I'm pursuing (the attack vector from the CVE), the end user
> expects certificates to be used. Any other authentication method --
> plaintext, hashed, SCRAM, Kerberos -- is unacceptable;

That makes sense.

> But that doesn't help your case; you want to choose a good default, and
> I agree that's important. Since there are arguments already for
> accepting a OR in the list, and -- if we couldn't find a good
> orthogonal method for certs, like Tom suggested -- an AND, maybe it
> wouldn't be so bad to accept a NOT as well?
> 
>     require_auth=cert                # certs only
>     require_auth=cert+scram-sha-256  # SCRAM wrapped by certs
>     require_auth=cert,scram-sha-256  # SCRAM or certs (or both)
>     require_auth=!password           # anything but plaintext
>     require_auth=!password,!md5      # no plaintext or MD5

Great, if there is a !something syntax, then I have nothing left to wish.
It may not be the most secure way do do it, but it sure is convenient.

Yours,
Laurenz Albe




Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Sat, Mar 05, 2022 at 01:04:05AM +0000, Jacob Champion wrote:
> the connection string, and libpq will fail the connection if the server
> doesn't use that method.
>
> (This is not intended for PG15. I'm generally anxious about posting
> experimental work during a commitfest, but there's been enough
> conversation about this topic recently that I felt like it'd be useful
> to have code to point to.)

Jacob, do you still have plans to work on this patch?
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Wed, Jun 1, 2022 at 12:55 AM Michael Paquier <michael@paquier.xyz> wrote:
>
> Jacob, do you still have plans to work on this patch?

Yes, definitely. That said, the more the merrier if there are others
interested in taking a shot at it. There are a large number of
alternative implementation proposals.

Thanks,
--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
v2 rebases over latest, removes the alternate spellings of "password",
and implements OR operations with a comma-separated list. For example:

- require_auth=cert means that the server must ask for, and the client
must provide, a client certificate.
- require_auth=password,md5 means that the server must ask for a
plaintext password or an MD5 hash.
- require_auth=scram-sha-256,gss means that one of SCRAM, Kerberos
authentication, or GSS transport encryption must be successfully
negotiated.
- require_auth=scram-sha-256,cert means that either a SCRAM handshake
must be completed, or the server must request a client certificate. It
has a potential pitfall in that it allows a partial SCRAM handshake,
as long as a certificate is requested and sent.

AND and NOT, discussed upthread, are not yet implemented. I tied
myself up in knots trying to make AND generic, so I think I"m going to
tackle NOT first, instead. The problem with AND is that it only makes
sense when one (and only one) of the options is a form of transport
authentication. (E.g. password+md5 never makes sense.) And although
cert+<something> and gss+<something> could be useful, the latter case
is already handled by gssencmode=require, and the gssencmode option is
more powerful since you can disable it (or set it to don't-care).

I'm not generally happy with how the "cert" option is working. With
the other methods, if you don't include a method in the list, then the
connection fails if the server tries to negotiate it. But if you don't
include the cert method in the list, we don't forbid the server from
asking for a cert, because the server always asks for a client
certificate via TLS whether it needs one or not. Behaving in the
intuitive way here would effectively break all use of TLS.

So I think Tom's recommendation that the cert method be handled by an
orthogonal option was a good one, and if that works then maybe we
don't need an AND syntax at all. Presumably I can just add an option
that parallels gssencmode, and then the current don't-care semantics
can be explicitly controlled. Skipping AND also means that I don't
have to create a syntax that can handle AND and NOT at the same time,
which I was dreading.

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Tue, Jun 07, 2022 at 02:22:28PM -0700, Jacob Champion wrote:
> v2 rebases over latest, removes the alternate spellings of "password",
> and implements OR operations with a comma-separated list. For example:
>
> - require_auth=cert means that the server must ask for, and the client
> must provide, a client certificate.

Hmm..  Nya.

> - require_auth=password,md5 means that the server must ask for a
> plaintext password or an MD5 hash.
> - require_auth=scram-sha-256,gss means that one of SCRAM, Kerberos
> authentication, or GSS transport encryption must be successfully
> negotiated.

Makes sense.

> - require_auth=scram-sha-256,cert means that either a SCRAM handshake
> must be completed, or the server must request a client certificate. It
> has a potential pitfall in that it allows a partial SCRAM handshake,
> as long as a certificate is requested and sent.

Er, this one could be a problem protocol-wise for SASL, because that
would mean that the authentication gets completed but that the
exchange has begun and is not finished?

> AND and NOT, discussed upthread, are not yet implemented. I tied
> myself up in knots trying to make AND generic, so I think I"m going to
> tackle NOT first, instead. The problem with AND is that it only makes
> sense when one (and only one) of the options is a form of transport
> authentication. (E.g. password+md5 never makes sense.) And although
> cert+<something> and gss+<something> could be useful, the latter case
> is already handled by gssencmode=require, and the gssencmode option is
> more powerful since you can disable it (or set it to don't-care).

I am on the edge regarding NOT as well, and I am unsure of the actual
benefits we could get from it as long as one can provide a white list
of auth methods.  If we don't see an immediate benefit in that, I'd
rather choose a minimal, still useful, design.  As a whole, I would
vote with adding only support for OR and a comma-separated list like
your proposal.

> I'm not generally happy with how the "cert" option is working. With
> the other methods, if you don't include a method in the list, then the
> connection fails if the server tries to negotiate it. But if you don't
> include the cert method in the list, we don't forbid the server from
> asking for a cert, because the server always asks for a client
> certificate via TLS whether it needs one or not. Behaving in the
> intuitive way here would effectively break all use of TLS.

Agreed.  Looking at what you are doing with allowed_auth_method_cert,
this makes the code harder to follow, which is risky for any
security-related feature, and that's different than the other methods
where we have the AUTH_REQ_* codes.  This leads to extra complications
in the shape of ssl_cert_requested and ssl_cert_sent, as well.  This
strongly looks like what we do for channel binding as it has
requirements separated from the actual auth methods but has dependency
with them, so a different parameter, as suggested, would make sense.
If we are not sure about this part, we could discard it in the first
instance of the patch.

> So I think Tom's recommendation that the cert method be handled by an
> orthogonal option was a good one, and if that works then maybe we
> don't need an AND syntax at all. Presumably I can just add an option
> that parallels gssencmode, and then the current don't-care semantics
> can be explicitly controlled. Skipping AND also means that I don't
> have to create a syntax that can handle AND and NOT at the same time,
> which I was dreading.

I am not convinced that we have any need for the AND grammar within
one parameter, as that's basically the same thing as defining multiple
connection parameters, isn't it?  This is somewhat a bit similar to
the interactions of channel binding with this new parameter and what
you have implemented.  For example, channel_binding=require
require_auth=md5 would imply both and should fail, even if it makes
little sense because MD5 has no idea of channel binding.  One
interesting case comes down to stuff like channel_binding=require
require_auth="md5,scram-sha-256", where I think that we should still
fail even if the server asks for MD5 and enforce an equivalent of an
AND grammar through the parameters.  This reasoning limits the
dependencies between each parameter and treats the areas where these
are checked independently, which is what check_expected_areq() does
for channel binding.  So that's more robust at the end.

Speaking of which, I would add tests to check some combinations of
channel_binding and require_auth.

+           appendPQExpBuffer(&conn->errorMessage,
+                             libpq_gettext("auth method \"%s\" required, but %s\n"),
+                             conn->require_auth, reason);
This one is going to make translation impossible.  One way to tackle
this issue is to use "auth method \"%s\" required: %s".

+   {"require_auth", NULL, NULL, NULL,
+       "Require-Auth", "", 14, /* sizeof("scram-sha-256") == 14 */
+   offsetof(struct pg_conn, require_auth)},
We could have an environment variable for that.

+/*
+ * Convenience macro for checking the allowed_auth_methods bitmask. Caller must
+ * ensure that type is not greater than 31 (high bit of the bitmask).
+ */
+#define auth_allowed(conn, type) \
+   (((conn)->allowed_auth_methods & (1 << (type))) != 0)
Better to add a compile-time check with StaticAssertDecl() then?  Or
add a note about that in pqcomm.h?

+      else if (auth_allowed(conn, AUTH_REQ_GSS) && conn->gssenc)
+      {
This field is only available under ENABLE_GSS, so this would fail to
compile when building without it?

+           method = parse_comma_separated_list(&s, &more);
+           if (method == NULL)
+               goto oom_error;
This should free the malloc'd copy of the element parsed, no?  That
means a free at the end of the while loop processing the options.

+           /*
+            * Sanity check; a duplicated method probably indicates a typo in a
+            * setting where typos are extremely risky.
+            */
Not sure why this is a problem.  Fine by me to check that, but this is
an OR list, so specifying one element twice means the same as once.

I get that it is not the priority yet as long as the design is not
completely clear, but having some docs would be nice :)
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Wed, Jun 8, 2022 at 9:58 PM Michael Paquier <michael@paquier.xyz> wrote:
> > - require_auth=scram-sha-256,cert means that either a SCRAM handshake
> > must be completed, or the server must request a client certificate. It
> > has a potential pitfall in that it allows a partial SCRAM handshake,
> > as long as a certificate is requested and sent.
>
> Er, this one could be a problem protocol-wise for SASL, because that
> would mean that the authentication gets completed but that the
> exchange has begun and is not finished?

I think it's already a problem, if you're not using channel_binding.
The cert behavior here makes it even less intuitive.

> > AND and NOT, discussed upthread, are not yet implemented. I tied
> > myself up in knots trying to make AND generic, so I think I"m going to
> > tackle NOT first, instead. The problem with AND is that it only makes
> > sense when one (and only one) of the options is a form of transport
> > authentication. (E.g. password+md5 never makes sense.) And although
> > cert+<something> and gss+<something> could be useful, the latter case
> > is already handled by gssencmode=require, and the gssencmode option is
> > more powerful since you can disable it (or set it to don't-care).
>
> I am on the edge regarding NOT as well, and I am unsure of the actual
> benefits we could get from it as long as one can provide a white list
> of auth methods.  If we don't see an immediate benefit in that, I'd
> rather choose a minimal, still useful, design.  As a whole, I would
> vote with adding only support for OR and a comma-separated list like
> your proposal.

Personally I think the ability to provide a default of `!password` is
very compelling. Any allowlist we hardcode won't be future-proof (see
also my response to Laurenz upthread [1]).

> > I'm not generally happy with how the "cert" option is working. With
> > the other methods, if you don't include a method in the list, then the
> > connection fails if the server tries to negotiate it. But if you don't
> > include the cert method in the list, we don't forbid the server from
> > asking for a cert, because the server always asks for a client
> > certificate via TLS whether it needs one or not. Behaving in the
> > intuitive way here would effectively break all use of TLS.
>
> Agreed.  Looking at what you are doing with allowed_auth_method_cert,
> this makes the code harder to follow, which is risky for any
> security-related feature, and that's different than the other methods
> where we have the AUTH_REQ_* codes.  This leads to extra complications
> in the shape of ssl_cert_requested and ssl_cert_sent, as well.  This
> strongly looks like what we do for channel binding as it has
> requirements separated from the actual auth methods but has dependency
> with them, so a different parameter, as suggested, would make sense.
> If we are not sure about this part, we could discard it in the first
> instance of the patch.

I'm pretty motivated to provide the ability to say "I want cert auth
only, nothing else." Using a separate parameter would mean we'd need
something like `require_auth=none`, but I think that makes a certain
amount of sense.

> I am not convinced that we have any need for the AND grammar within
> one parameter, as that's basically the same thing as defining multiple
> connection parameters, isn't it?  This is somewhat a bit similar to
> the interactions of channel binding with this new parameter and what
> you have implemented.  For example, channel_binding=require
> require_auth=md5 would imply both and should fail, even if it makes
> little sense because MD5 has no idea of channel binding.  One
> interesting case comes down to stuff like channel_binding=require
> require_auth="md5,scram-sha-256", where I think that we should still
> fail even if the server asks for MD5 and enforce an equivalent of an
> AND grammar through the parameters.  This reasoning limits the
> dependencies between each parameter and treats the areas where these
> are checked independently, which is what check_expected_areq() does
> for channel binding.  So that's more robust at the end.

Agreed.

> Speaking of which, I would add tests to check some combinations of
> channel_binding and require_auth.

Sounds good.

> +           appendPQExpBuffer(&conn->errorMessage,
> +                             libpq_gettext("auth method \"%s\" required, but %s\n"),
> +                             conn->require_auth, reason);
> This one is going to make translation impossible.  One way to tackle
> this issue is to use "auth method \"%s\" required: %s".

Yeah, I think I have a TODO somewhere about that somewhere. I'm
confused about your suggested fix though; can you elaborate?

> +   {"require_auth", NULL, NULL, NULL,
> +       "Require-Auth", "", 14, /* sizeof("scram-sha-256") == 14 */
> +   offsetof(struct pg_conn, require_auth)},
> We could have an environment variable for that.

I think that'd be a good idea. It'd be nice to have the option of
forcing a particular auth type across a process tree.

> +/*
> + * Convenience macro for checking the allowed_auth_methods bitmask. Caller must
> + * ensure that type is not greater than 31 (high bit of the bitmask).
> + */
> +#define auth_allowed(conn, type) \
> +   (((conn)->allowed_auth_methods & (1 << (type))) != 0)
> Better to add a compile-time check with StaticAssertDecl() then?  Or
> add a note about that in pqcomm.h?

If we only passed constants, that would work, but we also determine
the type at runtime, from the server message. Or am I missing
something?

> +      else if (auth_allowed(conn, AUTH_REQ_GSS) && conn->gssenc)
> +      {
> This field is only available under ENABLE_GSS, so this would fail to
> compile when building without it?

Yes, thank you for the catch. Will fix.

> +           method = parse_comma_separated_list(&s, &more);
> +           if (method == NULL)
> +               goto oom_error;
> This should free the malloc'd copy of the element parsed, no?  That
> means a free at the end of the while loop processing the options.

Good catch again, thanks!

> +           /*
> +            * Sanity check; a duplicated method probably indicates a typo in a
> +            * setting where typos are extremely risky.
> +            */
> Not sure why this is a problem.  Fine by me to check that, but this is
> an OR list, so specifying one element twice means the same as once.

Since this is likely to be a set-and-forget sort of option, and it
needs to behave correctly across server upgrades, I'd personally
prefer that the client tell me immediately if I've made a silly
mistake. Even for something relatively benign like this (but arguably,
it's more important for the NOT case).

> I get that it is not the priority yet as long as the design is not
> completely clear, but having some docs would be nice :)

Agreed; I will tackle that soon.

Thanks!
--Jacob

[1] https://www.postgresql.org/message-id/a14b1f89dcde75fb20afa7a1ffd2c2587b8d1a08.camel%40vmware.com



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Thu, Jun 09, 2022 at 04:29:38PM -0700, Jacob Champion wrote:
> On Wed, Jun 8, 2022 at 9:58 PM Michael Paquier <michael@paquier.xyz> wrote:
>> Er, this one could be a problem protocol-wise for SASL, because that
>> would mean that the authentication gets completed but that the
>> exchange has begun and is not finished?
>
> I think it's already a problem, if you're not using channel_binding.
> The cert behavior here makes it even less intuitive.

Ah right.  I forgot about the part where we need to have the backend
publish the set of supported machanisms.  If we don't get back
SCRAM-SHA-256-PLUS we are currently complaining after the exchange has
been initialized, true.  Maybe I should look at the RFC more closely.
The backend is very strict regarding that and needs to return an error
back to the client only when the exchange is done, but I don't recall
all the bits about the client handling.

> Personally I think the ability to provide a default of `!password` is
> very compelling. Any allowlist we hardcode won't be future-proof (see
> also my response to Laurenz upthread [1]).

Hm, perhaps.  You could use that as a default at application level,
but the default set in libpq should be backward-compatible (aka allow
everything, even trust where the backend just sends AUTH_REQ_OK).
This is unfortunate but there is a point in not breaking any user's
application, as well, particularly with ldap, and note that we have to
keep a certain amount of things backward-compatible as a very common
practice of packaging with Postgres is to have libpq link to binaries
across *multiple* major versions (Debian is one if I recall), with the
newest version of libpq used for all.  One argument in favor of
!password would be to control whether one does not want to use ldap,
but I'd still see most users just specify one or more methods in line
with their HBA policy as an approved list.

> I'm pretty motivated to provide the ability to say "I want cert auth
> only, nothing else." Using a separate parameter would mean we'd need
> something like `require_auth=none`, but I think that makes a certain
> amount of sense.

If the default of require_auth is backward-compatible and allows
everything, using a different parameter for the certs won't matter
anyway?

>> +           appendPQExpBuffer(&conn->errorMessage,
>> +                             libpq_gettext("auth method \"%s\" required, but %s\n"),
>> +                             conn->require_auth, reason);
>> This one is going to make translation impossible.  One way to tackle
>> this issue is to use "auth method \"%s\" required: %s".
>
> Yeah, I think I have a TODO somewhere about that somewhere. I'm
> confused about your suggested fix though; can you elaborate?

My suggestion is to reword the error message so as the reason and the
main error message can be treated as two independent things.  You are
right to apply two times libpq_gettext(), once to "reason" and once to
the main string.

>> +/*
>> + * Convenience macro for checking the allowed_auth_methods bitmask. Caller must
>> + * ensure that type is not greater than 31 (high bit of the bitmask).
>> + */
>> +#define auth_allowed(conn, type) \
>> +   (((conn)->allowed_auth_methods & (1 << (type))) != 0)
>> Better to add a compile-time check with StaticAssertDecl() then?  Or
>> add a note about that in pqcomm.h?
>
> If we only passed constants, that would work, but we also determine
> the type at runtime, from the server message. Or am I missing
> something?

My point would be to either register a max flag in the set of
AUTH_REQ_* in pqcomm.h so as we never go above 32 with an assertion to
make sure that this would never overflow, but I would add a comment in
pqcomm.h telling that we also do bitwise operations, relying on the
number of AUTH_REQ_* flags, and that we'd better be careful once the
number of flags gets higher than 32.  There is some margin, still that
could be easily forgotten.

>> +           /*
>> +            * Sanity check; a duplicated method probably indicates a typo in a
>> +            * setting where typos are extremely risky.
>> +            */
>> Not sure why this is a problem.  Fine by me to check that, but this is
>> an OR list, so specifying one element twice means the same as once.
>
> Since this is likely to be a set-and-forget sort of option, and it
> needs to behave correctly across server upgrades, I'd personally
> prefer that the client tell me immediately if I've made a silly
> mistake. Even for something relatively benign like this (but arguably,
> it's more important for the NOT case).

This is just a couple of lines.  Fine by me to keep it if you feel
that's better in the long run.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Mon, Jun 13, 2022 at 10:00 PM Michael Paquier <michael@paquier.xyz> wrote:
> > Personally I think the ability to provide a default of `!password` is
> > very compelling. Any allowlist we hardcode won't be future-proof (see
> > also my response to Laurenz upthread [1]).
>
> Hm, perhaps.  You could use that as a default at application level,
> but the default set in libpq should be backward-compatible (aka allow
> everything, even trust where the backend just sends AUTH_REQ_OK).
> This is unfortunate but there is a point in not breaking any user's
> application, as well, particularly with ldap, and note that we have to
> keep a certain amount of things backward-compatible as a very common
> practice of packaging with Postgres is to have libpq link to binaries
> across *multiple* major versions (Debian is one if I recall), with the
> newest version of libpq used for all.

I am 100% with you on this, but there's been a lot of chatter around
removing plaintext password support from libpq. I would much rather
see them rejected by default than removed entirely. !password would
provide an easy path for that.

> > I'm pretty motivated to provide the ability to say "I want cert auth
> > only, nothing else." Using a separate parameter would mean we'd need
> > something like `require_auth=none`, but I think that makes a certain
> > amount of sense.
>
> If the default of require_auth is backward-compatible and allows
> everything, using a different parameter for the certs won't matter
> anyway?

If you want cert authentication only, allowing "everything" will let
the server extract a password and then you're back at square one.
There needs to be a way to prohibit all explicit authentication
requests.

> My suggestion is to reword the error message so as the reason and the
> main error message can be treated as two independent things.  You are
> right to apply two times libpq_gettext(), once to "reason" and once to
> the main string.

Ah, thanks for the clarification. Done that way in v3.

> My point would be to either register a max flag in the set of
> AUTH_REQ_* in pqcomm.h so as we never go above 32 with an assertion to
> make sure that this would never overflow, but I would add a comment in
> pqcomm.h telling that we also do bitwise operations, relying on the
> number of AUTH_REQ_* flags, and that we'd better be careful once the
> number of flags gets higher than 32.  There is some margin, still that
> could be easily forgotten.

Makes sense; done.

v3 also removes "cert" from require_auth while I work on a replacement
connection option, fixes the bugs/suggestions pointed out upthread,
and adds a documentation first draft. I tried combining this with the
NOT work but it was too much to juggle, so that'll wait for a v4+,
along with require_auth=none and "cert mode".

Thanks for the detailed review!
--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
"David G. Johnston"
Date:
On Thu, Jun 9, 2022 at 4:30 PM Jacob Champion <jchampion@timescale.com> wrote:
On Wed, Jun 8, 2022 at 9:58 PM Michael Paquier <michael@paquier.xyz> wrote:

> One
> interesting case comes down to stuff like channel_binding=require
> require_auth="md5,scram-sha-256", where I think that we should still
> fail even if the server asks for MD5 and enforce an equivalent of an
> AND grammar through the parameters.  This reasoning limits the
> dependencies between each parameter and treats the areas where these
> are checked independently, which is what check_expected_areq() does
> for channel binding.  So that's more robust at the end.

Agreed.


That just makes me want to not implement OR'ing...

The existing set of individual parameters doesn't work as an API for try-and-fallback.

Something like would be less problematic when it comes to setting multiple related options:

--auth-try "1;sslmode=require;channel_binding=require;method=scram-sha-256;sslcert=/tmp/machine.cert;sslkey=/tmp/machine.key"
--auth-try "2;sslmode=require;method=cert;sslcert=/tmp/me.cert;sslkey=/tmp/me.key"
--auth-try "3;sslmode=prefer;method=md5"

Absent that radical idea, require_auth probably shouldn't change any behavior that exists today without having specified require_auth and having the chosen method happen anyway.  So whatever happens today with an md5 password prompt while channel_binding is set to require (not in the mood right now to figure out how to test that on a compiled against HEAD instance).

David J.

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Wed, Jun 22, 2022 at 5:56 PM David G. Johnston
<david.g.johnston@gmail.com> wrote:
> That just makes me want to not implement OR'ing...
>
> The existing set of individual parameters doesn't work as an API for try-and-fallback.
>
> Something like would be less problematic when it comes to setting multiple related options:
>
> --auth-try
"1;sslmode=require;channel_binding=require;method=scram-sha-256;sslcert=/tmp/machine.cert;sslkey=/tmp/machine.key"
> --auth-try "2;sslmode=require;method=cert;sslcert=/tmp/me.cert;sslkey=/tmp/me.key"
> --auth-try "3;sslmode=prefer;method=md5"

I think that's a fair point, and your --auth-try example definitely
illustrates why having require_auth try to do everything is probably
not a viable strategy. My arguments for keeping OR in spite of that
are

- the default is effectively an OR of all available methods (plus "none");
- I think NOT is a important case in practice, which is effectively a
negative OR ("anything but this/these"); and
- not providing an explicit, positive OR to complement the above seems
like it would be a frustrating user experience once you want to get
just a little bit more creative.

It's also low-hanging fruit that doesn't require multiple connections
to the server per attempt (which I think your --auth-try proposal
might, if I understand it correctly).

> Absent that radical idea, require_auth probably shouldn't change any behavior that exists today without having
specifiedrequire_auth and having the chosen method happen anyway.  So whatever happens today with an md5 password
promptwhile channel_binding is set to require (not in the mood right now to figure out how to test that on a compiled
againstHEAD instance). 

I think the newest tests in v3 should enforce that, but let me know if
I've missed something.

--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Thu, Jun 23, 2022 at 10:33 AM Jacob Champion <jchampion@timescale.com> wrote:
> - I think NOT is a important case in practice, which is effectively a
> negative OR ("anything but this/these")

Both NOT (via ! negation) and "none" are implemented in v4.

Examples:

# The server must use SCRAM.
require_auth=scram-sha-256
# The server must use SCRAM or Kerberos.
require_auth=scram-sha-256,gss,sspi
# The server may optionally use SCRAM.
require_auth=none,scram-sha-256
# The server must not use any application-level authentication.
require_auth=none
# The server may optionally use authentication, except plaintext
# passwords.
require_auth=!password
# The server may optionally use authentication, except weaker password
# challenges.
require_auth=!password,!md5
# The server must use an authentication method.
require_auth=!none
# The server must use a non-plaintext authentication method.
require_auth=!none,!password

Note that `require_auth=none,scram-sha-256` allows the server to
abandon a SCRAM exchange early, same as it can today. That might be a
bit surprising.

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Fri, Jun 24, 2022 at 12:17 PM Jacob Champion <jchampion@timescale.com> wrote:
> Both NOT (via ! negation) and "none" are implemented in v4.

v5 adds a second patch which implements a client-certificate analogue
to gssencmode; I've named it sslcertmode. This takes the place of the
require_auth=[!]cert setting implemented previously.

As I mentioned upthread, I think sslcertmode=require is the weakest
feature here, since the server always sends a certificate request if
you are using TLS. It would potentially be more useful if we start
expanding TLS setups and middlebox options, but I still only see it as
a troubleshooting feature for administrators. By contrast,
sslcertmode=disable lets you turn off the use of the certificate, no
matter what libpq is able to find in your environment or home
directory. That seems more immediately useful.

With this addition, I'm wondering if GSS encrypted transport should be
removed from the definition/scope of require_auth=gss. We already have
gssencmode to control that, and it would remove an ugly special case
from the patch.

I'll add this patchset to the commitfest.

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Mon, Jun 27, 2022 at 12:05 PM Jacob Champion <jchampion@timescale.com> wrote:
> v5 adds a second patch which implements a client-certificate analogue
> to gssencmode; I've named it sslcertmode.

...and v6 fixes check-world, because I always forget about postgres_fdw.

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Peter Eisentraut
Date:
On 27.06.22 23:40, Jacob Champion wrote:
> -HINT:  Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host,
hostaddr,port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count,
tcp_user_timeout,sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, sslsni, requirepeer,
ssl_min_protocol_version,ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs,
use_remote_estimate,fdw_startup_cost, fdw_tuple_cost, extensions, updatable, truncatable, fetch_size, batch_size,
async_capable,parallel_commit, keep_connections
 
> +HINT:  Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host,
hostaddr,port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count,
tcp_user_timeout,sslmode, sslcompression, sslcert, sslkey, sslcertmode, sslrootcert, sslcrl, sslcrldir, sslsni,
requirepeer,require_auth, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib,
target_session_attrs,use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, truncatable,
fetch_size,batch_size, async_capable, parallel_commit, keep_connections
 

It's not strictly related to your patch, but maybe this hint has 
outlived its usefulness?  I mean, we don't list all available tables 
when you try to reference a table that doesn't exist.  And unordered on 
top of that.





Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Wed, Jun 29, 2022 at 6:36 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> It's not strictly related to your patch, but maybe this hint has
> outlived its usefulness?  I mean, we don't list all available tables
> when you try to reference a table that doesn't exist.  And unordered on
> top of that.

Yeah, maybe it'd be better to tell the user the correct context for an
otherwise-valid option ("the 'sslcert' option may only be applied to
USER MAPPING"), and avoid the option dump entirely?

--

v7, attached, fixes configuration on Windows.

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Thu, Jun 30, 2022 at 04:26:54PM -0700, Jacob Champion wrote:
> Yeah, maybe it'd be better to tell the user the correct context for an
> otherwise-valid option ("the 'sslcert' option may only be applied to
> USER MAPPING"), and avoid the option dump entirely?

Yes, that would be nice.  Now, this HINT has been an annoyance in the
context of the regression tests when adding features entirely
unrelated to postgres_fdw, at least for me.  I would be more tempted
to get rid of it entirely, FWIW.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Peter Eisentraut
Date:
I'm wondering about making the list of things you can specify in 
require_auth less confusing and future proof.

For example, before long someone is going to try putting "ldap" into 
require_auth.  The fact that the methods in pg_hba.conf are not what 
libpq sees is not something that was really exposed to users until now. 
"none" vs. "trust" takes advantage of that.  But then I think we could 
also make "password" clearer, which surely sounds like any kind of 
password, encrypted or not, and that's also how pg_hba.conf behaves. 
The protocol specification calls that "AuthenticationCleartextPassword"; 
maybe we could pick a name based on that.

And then, what if we add a new method in the future, and someone puts 
that into their connection string.  Old clients will just refuse to 
parse that.  Ok, that effectively gives you the same behavior as 
rejecting the server's authentication offer.  But what about the negated 
version?  Also, what if we add new SASL methods.  How would we modify 
this code to be able to pick and choose and also have backward and 
forward compatible behavior?

In general, I like this.  We just need to think about the above things a 
bit more.



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Thu, Sep 8, 2022 at 6:25 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> For example, before long someone is going to try putting "ldap" into
> require_auth.  The fact that the methods in pg_hba.conf are not what
> libpq sees is not something that was really exposed to users until now.
> "none" vs. "trust" takes advantage of that.  But then I think we could
> also make "password" clearer, which surely sounds like any kind of
> password, encrypted or not, and that's also how pg_hba.conf behaves.
> The protocol specification calls that "AuthenticationCleartextPassword";
> maybe we could pick a name based on that.

Sounds fair. "cleartext"? "plaintext"? "plain" (like SASL's PLAIN)?

> And then, what if we add a new method in the future, and someone puts
> that into their connection string.  Old clients will just refuse to
> parse that.  Ok, that effectively gives you the same behavior as
> rejecting the server's authentication offer.  But what about the negated
> version?

I assume the alternative behavior you're thinking of is to ignore
negated "future methods"? I think the danger with that (for a feature
that's supposed to be locking communication down) is that it's not
possible to differentiate between a maybe-future method and a typo. If
I want "!password" because my intention is to disallow a plaintext
exchange, I really don't want "!pasword" to silently allow anything.

> Also, what if we add new SASL methods.  How would we modify
> this code to be able to pick and choose and also have backward and
> forward compatible behavior?

On the SASL front: In the back of my head I'd been considering adding
a "sasl:" prefix to "scram-sha-256", so that we have a namespace for
new SASL methods. That would also give us a jumping-off point in the
future if we decide to add SASL method negotiation to the protocol.
What do you think about that?

Backwards compatibility will, I think, be handled trivially by a newer
client. The only way to break backwards compatibility would be to
remove support for a method, which I assume would be independent of
this feature.

Forwards compatibility doesn't seem like something this feature can
add by itself (old clients can't speak new methods). Though we could
backport new method names to allow them to be used in negations, if
maintaining that aspect of compatibility is worth the effort.

> In general, I like this.  We just need to think about the above things a
> bit more.

Thanks!

--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Peter Eisentraut
Date:
On 08.09.22 20:18, Jacob Champion wrote:
> Sounds fair. "cleartext"? "plaintext"? "plain" (like SASL's PLAIN)?

> On the SASL front: In the back of my head I'd been considering adding
> a "sasl:" prefix to "scram-sha-256", so that we have a namespace for
> new SASL methods. That would also give us a jumping-off point in the
> future if we decide to add SASL method negotiation to the protocol.
> What do you think about that?

After thinking about this a bit more, I think it would be best if the 
words used here match exactly with what is used in pg_hba.conf.  That's 
the only thing the user cares about: reject "password", reject "trust", 
require "scram-sha-256", etc.  How this maps to the protocol and that 
some things are SASL or not is not something they have needed to care 
about and don't really need to know for this.  So I would suggest to 
organize it that way.

Another idea:  Maybe instead of the "!" syntax, use two settings, 
require_auth and reject_auth?  Might be simpler?




Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Fri, Sep 16, 2022 at 7:56 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> On 08.09.22 20:18, Jacob Champion wrote:
> After thinking about this a bit more, I think it would be best if the
> words used here match exactly with what is used in pg_hba.conf.  That's
> the only thing the user cares about: reject "password", reject "trust",
> require "scram-sha-256", etc.  How this maps to the protocol and that
> some things are SASL or not is not something they have needed to care
> about and don't really need to know for this.  So I would suggest to
> organize it that way.

I tried that in v1, if you'd like to see what that ends up looking
like. As a counterexample, I believe `cert` auth looks identical to
`trust` on the client side. (The server always asks for a client
certificate even if it doesn't use it. Otherwise, this proposal would
probably have looked different.) And `ldap` auth is indistinguishable
from `password`, etc. In my opinion, how it maps to the protocol is
more honest to the user than how it maps to HBA, because the auth
request sent by the protocol determines your level of risk.

I also like `none` over `trust` because you don't have to administer a
server to understand what it means. That's why I was on board with
your proposal to change the name of `password`. And you don't have to
ignore the natural meaning of client-side "trust", which IMO means
"trust the server." There's opportunity for confusion either way,
unfortunately, but naming them differently may help make it clear that
they _are_ different.

This problem overlaps a bit with the last remaining TODO in the code.
I treat gssenc tunnels as satisfying require_auth=gss. Maybe that's
useful, because it kind of maps to how HBA treats it? But it's not
consistent with the TLS side of things, and it overlaps with
gssencmode=require, complicating the relationship with the new
sslcertmode.

> Another idea:  Maybe instead of the "!" syntax, use two settings,
> require_auth and reject_auth?  Might be simpler?

Might be. If that means we have to handle the case where both are set
to something, though, it might make things harder.

We can error out if they conflict, which adds a decent but not huge
amount of complication. Or we can require that only one is set, which
is both easy and overly restrictive. But either choice makes it harder
to adopt a `reject password` default, as many people seem to be
interested in doing. Because if you want to override that default,
then you have to first unset reject_auth and then set require_auth, as
opposed to just saying require_auth=something and being done with it.
I'm not sure that's worth it. Thoughts?

I'm happy to implement proofs of concept for that, or any other ideas,
given the importance of getting this "right enough" the first time.
Just let me know.

Thanks,
--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Fri, Sep 16, 2022 at 1:29 PM Jacob Champion <jchampion@timescale.com> wrote:
> I'm happy to implement proofs of concept for that, or any other ideas,
> given the importance of getting this "right enough" the first time.
> Just let me know.

v8 rebases over the postgres_fdw HINT changes; there are no functional
differences.

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Peter Eisentraut
Date:
On 21.09.22 17:33, Jacob Champion wrote:
> On Fri, Sep 16, 2022 at 1:29 PM Jacob Champion <jchampion@timescale.com> wrote:
>> I'm happy to implement proofs of concept for that, or any other ideas,
>> given the importance of getting this "right enough" the first time.
>> Just let me know.
> 
> v8 rebases over the postgres_fdw HINT changes; there are no functional
> differences.

So let's look at the two TODO comments you have:

          * TODO: how should !auth_required interact with an incomplete
          * SCRAM exchange?

What specific combination of events are you thinking of here?


             /*
              * If implicit GSS auth has already been performed via GSS
              * encryption, we don't need to have performed an
              * AUTH_REQ_GSS exchange.
              *
              * TODO: check this assumption. What mutual auth guarantees
              * are made in this case?
              */

I don't understand the details involved here, but I would be surprised 
if this assumption is true.  For example, does GSS encryption deal with 
user names and a user name map?  I don't see how these can be 
equivalent.  In any case, it seems to me that it would be safer to *not* 
make this assumption at first and then have someone more knowledgeable 
make the argument that it would be safe.




Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Wed, Sep 21, 2022 at 3:36 PM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> So let's look at the two TODO comments you have:
>
>           * TODO: how should !auth_required interact with an incomplete
>           * SCRAM exchange?
>
> What specific combination of events are you thinking of here?

Let's say the client is using `require_auth=!password`. If the server
starts a SCRAM exchange. but doesn't finish it, the connection will
still succeed with the current implementation (because it satisfies
the "none" case). This is also true for a client setting of
`require_auth=scram-sha-256,none`. I think this is potentially
dangerous, but it mirrors the current behavior of libpq and I'm not
sure that we should change it as part of this patch.

>              /*
>               * If implicit GSS auth has already been performed via GSS
>               * encryption, we don't need to have performed an
>               * AUTH_REQ_GSS exchange.
>               *
>               * TODO: check this assumption. What mutual auth guarantees
>               * are made in this case?
>               */
>
> I don't understand the details involved here, but I would be surprised
> if this assumption is true.  For example, does GSS encryption deal with
> user names and a user name map?

To my understanding, yes. There are explicit tests for it.

> In any case, it seems to me that it would be safer to *not*
> make this assumption at first and then have someone more knowledgeable
> make the argument that it would be safe.

I think I'm okay with that, regardless. Here's one of the wrinkles:
right now, both of the following connstrings work:

    require_auth=gss gssencmode=require
    require_auth=gss gssencmode=prefer

If we don't treat gssencmode as providing GSS auth, then the first
case will always fail, because there will be no GSS authentication
packet over an encrypted connection. Likewise, the second case will
almost always fail, unless the server doesn't support gssencmode at
all (so why are you using prefer?).

If you're okay with those limitations, I will rip out the code. The
reason I'm not too worried about it is, I don't think it makes much
sense to be strict about your authentication requirements while at the
same time leaving the choice of transport encryption up to the server.

Thanks,
--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Peter Eisentraut
Date:
On 22.09.22 01:37, Jacob Champion wrote:
> On Wed, Sep 21, 2022 at 3:36 PM Peter Eisentraut
> <peter.eisentraut@enterprisedb.com> wrote:
>> So let's look at the two TODO comments you have:
>>
>>            * TODO: how should !auth_required interact with an incomplete
>>            * SCRAM exchange?
>>
>> What specific combination of events are you thinking of here?
> 
> Let's say the client is using `require_auth=!password`. If the server
> starts a SCRAM exchange. but doesn't finish it, the connection will
> still succeed with the current implementation (because it satisfies
> the "none" case). This is also true for a client setting of
> `require_auth=scram-sha-256,none`. I think this is potentially
> dangerous, but it mirrors the current behavior of libpq and I'm not
> sure that we should change it as part of this patch.

It might be worth reviewing that behavior for other reasons, but I think 
semantics of your patch are correct.

>> In any case, it seems to me that it would be safer to *not*
>> make this assumption at first and then have someone more knowledgeable
>> make the argument that it would be safe.
> 
> I think I'm okay with that, regardless. Here's one of the wrinkles:
> right now, both of the following connstrings work:
> 
>      require_auth=gss gssencmode=require
>      require_auth=gss gssencmode=prefer
> 
> If we don't treat gssencmode as providing GSS auth, then the first
> case will always fail, because there will be no GSS authentication
> packet over an encrypted connection. Likewise, the second case will
> almost always fail, unless the server doesn't support gssencmode at
> all (so why are you using prefer?).
> 
> If you're okay with those limitations, I will rip out the code. The
> reason I'm not too worried about it is, I don't think it makes much
> sense to be strict about your authentication requirements while at the
> same time leaving the choice of transport encryption up to the server.

The way I understand what you explained here is that it would be more 
sensible to leave that code in.  I would be okay with that.




Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Thu, Sep 22, 2022 at 4:52 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> On 22.09.22 01:37, Jacob Champion wrote:
> > I think this is potentially
> > dangerous, but it mirrors the current behavior of libpq and I'm not
> > sure that we should change it as part of this patch.
>
> It might be worth reviewing that behavior for other reasons, but I think
> semantics of your patch are correct.

Sounds good. v9 removes the TODO and adds a better explanation.

> > If you're okay with those [GSS] limitations, I will rip out the code. The
> > reason I'm not too worried about it is, I don't think it makes much
> > sense to be strict about your authentication requirements while at the
> > same time leaving the choice of transport encryption up to the server.
>
> The way I understand what you explained here is that it would be more
> sensible to leave that code in.  I would be okay with that.

I've added a comment there explaining the gssencmode interaction. That
leaves no TODOs inside the code itself.

I removed the commit message note about not being able to prevent
unexpected client cert requests or GSS encryption, since we've decided
to handle those cases outside of require_auth.

I'm not able to test SSPI easily at the moment; if anyone is able to
try it out, that'd be really helpful. There's also the question of
SASL forwards compatibility -- if someone adds a new SASL mechanism,
the code will treat it like scram-sha-256 until it's changed, and
there will be no test to catch it. Should we leave that to the future
mechanism implementer to fix, or add a mechanism check now so the
client is safe even if they forget?

Thanks!
--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Peter Eisentraut
Date:
On 23.09.22 02:02, Jacob Champion wrote:
> On Thu, Sep 22, 2022 at 4:52 AM Peter Eisentraut
> <peter.eisentraut@enterprisedb.com> wrote:
>> On 22.09.22 01:37, Jacob Champion wrote:
>>> I think this is potentially
>>> dangerous, but it mirrors the current behavior of libpq and I'm not
>>> sure that we should change it as part of this patch.
>>
>> It might be worth reviewing that behavior for other reasons, but I think
>> semantics of your patch are correct.
> 
> Sounds good. v9 removes the TODO and adds a better explanation.

I'm generally okay with these patches now.

> I'm not able to test SSPI easily at the moment; if anyone is able to
> try it out, that'd be really helpful. There's also the question of
> SASL forwards compatibility -- if someone adds a new SASL mechanism,
> the code will treat it like scram-sha-256 until it's changed, and
> there will be no test to catch it. Should we leave that to the future
> mechanism implementer to fix, or add a mechanism check now so the
> client is safe even if they forget?

I think it would be good to put some provisions in place here, even if 
they are elementary.  Otherwise, there will be a significant burden on 
the person who implements the next SASL method (i.e., you ;-) ) to 
figure that out then.

I think you could just stick a string list of allowed SASL methods into 
PGconn.

By the way, I'm not sure all the bit fiddling is really worth it.  An 
array of integers (or unsigned char or whatever) would work just as 
well.  Especially if you are going to have a string list for SASL 
anyway.  You're not really saving any bits or bytes either way in the 
normal case.

Minor comments:

Pasting together error messages like with auth_description() isn't going 
to work.  You either need to expand the whole message in 
check_expected_areq(), or perhaps rephrase the message like

libpq_gettext("auth method \"%s\" required, but server requested \"%s\"\n"),
                              conn->require_auth,
                              auth_description(areq)

and make auth_description() just return a single word not subject to 
translation.

spurious whitespace change in fe-secure-openssl.c

whitespace error in patch:

.git/rebase-apply/patch:109: tab in indent.
                         via TLS, nor GSS authentication via its 
encrypted transport.)

In the 0002 patch, the configure test needs to be added to meson.build.




Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On 10/5/22 06:33, Peter Eisentraut wrote:
> I think it would be good to put some provisions in place here, even if 
> they are elementary.  Otherwise, there will be a significant burden on 
> the person who implements the next SASL method (i.e., you ;-) ) to 
> figure that out then.

Sounds good, I'll work on that. v10 does not yet make changes in this area.

> I think you could just stick a string list of allowed SASL methods into 
> PGconn.
> 
> By the way, I'm not sure all the bit fiddling is really worth it.  An 
> array of integers (or unsigned char or whatever) would work just as 
> well.  Especially if you are going to have a string list for SASL 
> anyway.  You're not really saving any bits or bytes either way in the 
> normal case.

Yeah, with the SASL case added in, the bitmasks might not be long for
this world. It is nice to be able to invert the whole thing, but a
separate boolean saying "invert the list" could accomplish the same goal
and I think we'll need to have that for the SASL mechanism list anyway.

> Minor comments:
> 
> Pasting together error messages like with auth_description() isn't going 
> to work.  You either need to expand the whole message in 
> check_expected_areq(), or perhaps rephrase the message like
> 
> libpq_gettext("auth method \"%s\" required, but server requested \"%s\"\n"),
>                               conn->require_auth,
>                               auth_description(areq)
> 
> and make auth_description() just return a single word not subject to 
> translation.

Right. Michael tried to warn me about that upthread, but I only ended up
fixing one of the two error cases for some reason. I've merged the two
into one code path for v10.

Quick error messaging bikeshed: do you prefer

    auth method "!password,!md5" requirement failed: ...

or

    auth method requirement "!password,!md5" failed: ...

?

> spurious whitespace change in fe-secure-openssl.c

Fixed.

> whitespace error in patch:
> 
> .git/rebase-apply/patch:109: tab in indent.
>                          via TLS, nor GSS authentication via its 
> encrypted transport.)

Fixed.

> In the 0002 patch, the configure test needs to be added to meson.build.

Added.

Thanks,
--Jacob
Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Wed, Oct 12, 2022 at 9:40 AM Jacob Champion <jchampion@timescale.com> wrote:
> On 10/5/22 06:33, Peter Eisentraut wrote:
> > I think it would be good to put some provisions in place here, even if
> > they are elementary.  Otherwise, there will be a significant burden on
> > the person who implements the next SASL method (i.e., you ;-) ) to
> > figure that out then.
>
> Sounds good, I'll work on that. v10 does not yet make changes in this area.

v11 makes an attempt at this (see 0003), using the proposed string list.

Personally I'm not happy with the amount of complexity it adds in
exchange for flexibility we can't use yet. Maybe there's a way to
simplify it, but I think the two-tiered approach of the patch has to
remain, unless we find a way to move SASL mechanism selection to a
different part of the code. I'm not sure that'd be helpful.

Maybe I should just add a basic Assert here, to trip if someone adds a
new SASL mechanism, and point that lucky person to this thread with a
comment?

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Aleksander Alekseev
Date:
Hi Jacob,

> v11 makes an attempt at this (see 0003), using the proposed string list.

I noticed that this patchset stuck a bit so I decided to take a look.

In 0001:

```
+                    conn->auth_required = false;
+                    conn->allowed_auth_methods = -1;
...
+    uint32        allowed_auth_methods;    /* bitmask of acceptable
AuthRequest codes */
```

Assigning a negative number to uint32 doesn't necessarily work on all
platforms. I suggest using PG_UINT32_MAX.

In 0002:

```
+          <term><literal>require</literal></term>
+          <listitem>
+           <para>
+            the server <emphasis>must</emphasis> request a certificate. The
+            connection will fail if the server authenticates the client despite
+            not requesting or receiving one.
```

The commit message IMO has a better description of "require". I
suggest adding the part about "This doesn't add any additional
security ..." to the documentation.

```
+ * hard-coded certificate via sslcert, so we don't actually set any
certificates
+ * here; we just it to record whether or not the server has actually asked for
```

Something is off with the wording here in the "we just it to ..." part.

The patchset seems to be in very good shape except for these few
nitpicks. I'm inclined to change its status to "Ready for Committer"
as soon as the new version will pass cfbot unless there are going to
be any objections from the community.

-- 
Best regards,
Aleksander Alekseev



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On 11/11/22 05:52, Aleksander Alekseev wrote:
> I noticed that this patchset stuck a bit so I decided to take a look.

Thanks!

> Assigning a negative number to uint32 doesn't necessarily work on all
> platforms. I suggest using PG_UINT32_MAX.

Hmm -- on which platforms is "-1 converted to unsigned" not equivalent
to the maximum value? Are they C-compliant?

> The commit message IMO has a better description of "require". I
> suggest adding the part about "This doesn't add any additional
> security ..." to the documentation.

Sounds good; see what you think of v12.

> ```
> + * hard-coded certificate via sslcert, so we don't actually set any
> certificates
> + * here; we just it to record whether or not the server has actually asked for
> ```
> 
> Something is off with the wording here in the "we just it to ..." part.

Fixed.

> The patchset seems to be in very good shape except for these few
> nitpicks. I'm inclined to change its status to "Ready for Committer"
> as soon as the new version will pass cfbot unless there are going to
> be any objections from the community.

Thank you! I expect a maintainer will need to weigh in on the
cost/benefit of 0003 either way.

--Jacob
Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Aleksander Alekseev
Date:
Hi Jacob,

> > Assigning a negative number to uint32 doesn't necessarily work on all
> > platforms. I suggest using PG_UINT32_MAX.
>
> Hmm -- on which platforms is "-1 converted to unsigned" not equivalent
> to the maximum value? Are they C-compliant?

I did a little more research and I think you are right. What happens
according to the C standard:

"""
the value is converted to unsigned by adding to it one greater than the largest
number that can be represented in the unsigned integer type
"""

so this is effectively -1 + (PG_UINT32_MAX + 1).

-- 
Best regards,
Aleksander Alekseev



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On 11/11/22 22:57, Aleksander Alekseev wrote:
> I did a little more research and I think you are right. What happens
> according to the C standard:

Thanks for confirming! (I personally prefer -1 to a *MAX macro, because
it works regardless of the length of the type.)

--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Thu, Oct 20, 2022 at 11:36:34AM -0700, Jacob Champion wrote:
> Maybe I should just add a basic Assert here, to trip if someone adds a
> new SASL mechanism, and point that lucky person to this thread with a
> comment?

I am beginning to look at the last version proposed, which has been
marked as RfC.  Does this patch need a refresh in light of a9e9a9f and
0873b2d?  The changes for libpq_append_conn_error() should be
straight-forward.

The CF bot is still happy.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Tue, Nov 15, 2022 at 11:07 PM Michael Paquier <michael@paquier.xyz> wrote:
> I am beginning to look at the last version proposed, which has been
> marked as RfC.  Does this patch need a refresh in light of a9e9a9f and
> 0873b2d?  The changes for libpq_append_conn_error() should be
> straight-forward.

Updated in v13, thanks!

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Peter Eisentraut
Date:
On 16.11.22 18:26, Jacob Champion wrote:
> On Tue, Nov 15, 2022 at 11:07 PM Michael Paquier <michael@paquier.xyz> wrote:
>> I am beginning to look at the last version proposed, which has been
>> marked as RfC.  Does this patch need a refresh in light of a9e9a9f and
>> 0873b2d?  The changes for libpq_append_conn_error() should be
>> straight-forward.
> 
> Updated in v13, thanks!

What is the status of this patch set?  Michael had registered himself as 
committer and then removed himself again.  So I hadn't been paying much 
attention myself.  Was there anything left to discuss?




Re: [PoC] Let libpq reject unexpected authentication requests

From
Aleksander Alekseev
Date:
Hi Peter,

> > Updated in v13, thanks!
>
> What is the status of this patch set?  Michael had registered himself as
> committer and then removed himself again.  So I hadn't been paying much
> attention myself.  Was there anything left to discuss?

Previously I marked the patch as RfC. Although it's been a few months
ago and I don't recall all the details, it should have been in good
shape (in my personal opinion at least). The commits a9e9a9f and
0873b2d Michael referred to are message refactorings so I doubt Jacob
had serious problems with them.

Of course, I'll take another fresh look and let you know my findings in a bit.

-- 
Best regards,
Aleksander Alekseev



Re: [PoC] Let libpq reject unexpected authentication requests

From
Aleksander Alekseev
Date:
Hi Peter,

> > What is the status of this patch set?  Michael had registered himself as
> > committer and then removed himself again.  So I hadn't been paying much
> > attention myself.  Was there anything left to discuss?
>
> Previously I marked the patch as RfC. Although it's been a few months
> ago and I don't recall all the details, it should have been in good
> shape (in my personal opinion at least). The commits a9e9a9f and
> 0873b2d Michael referred to are message refactorings so I doubt Jacob
> had serious problems with them.
>
> Of course, I'll take another fresh look and let you know my findings in a bit.

The code is well written, documented and test-covered. All the tests
pass. To my knowledge there are no open questions left. I think the
patch is as good as it will ever get.

-- 
Best regards,
Aleksander Alekseev



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Tue, Jan 31, 2023 at 5:20 AM Aleksander Alekseev
<aleksander@timescale.com> wrote:
> To my knowledge there are no open questions left. I think the
> patch is as good as it will ever get.

A committer will need to decide whether they're willing to maintain
0003 or not, as mentioned with the v11 post. Which I suppose is the
last open question, but not one I can answer from here.

Thanks!
--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Tue, Jan 31, 2023 at 02:03:54PM +0300, Aleksander Alekseev wrote:
>> What is the status of this patch set?  Michael had registered himself as
>> committer and then removed himself again.  So I hadn't been paying much
>> attention myself.  Was there anything left to discuss?

Yes, sorry about not following up on that.  I was registered as such
for a few weeks, but I have not been able to follow up.  It did not
seem fair for this patch to wait on only me, which is why I have
removed my name, at least temporarily, so as somebody may be able to
come back to it before me.  I am not completely sure whether I will be
able to come back and dive deeply into this thread soon, TBH :/

> Previously I marked the patch as RfC. Although it's been a few months
> ago and I don't recall all the details, it should have been in good
> shape (in my personal opinion at least). The commits a9e9a9f and
> 0873b2d Michael referred to are message refactorings so I doubt Jacob
> had serious problems with them.
>
> Of course, I'll take another fresh look and let you know my findings in a bit.

(There were a few things around certificate handling that need careful
consideration, at least that was my impression.)
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On 2/16/23 10:57, Jacob Champion wrote:
> v14 rebases over the test and solution conflicts from 9244c11afe2.

Since we're to the final CF for PG16, here's a rough summary.

This patchset provides two related features: 1) the ability for a client
to explicitly allow or deny particular methods of in-band authentication
(that is, things like password exchange), and 2) the ability to withhold
a client certificate from a server that asks for it.

Feature 1 was originally proposed to mitigate abuse where a successful
MITM attack can then be used to fish for client credentials [1]. It also
lets users disable undesirable authentication types (like plaintext) by
default, which seems to be a common interest. Both features came up
again in the context of proxies such as postgres_fdw, where it's
sometimes important that users authenticate using only their credentials
and not piggyback on the authority of the proxy host [2]. And another
use case for feature 2 just came up independently [3], to fix
connections where the default client certificate isn't valid for a
particular server.

Since this is all client-side, it's compatible with existing servers.
Also since it's client-side, it can't prevent connections from being
established by an eager server; it can only drop the connection once it
sees that its requirement was not met, similar to how we handle
target_session_attrs. That means it can't prevent a login trigger from
being processed on behalf of a confused proxy. (I think that would
require server-side support.)

0001 and 0002 are the core features. 0003 is a more future-looking
refactoring of the internals, to make it easier to handle more SASL
mechanisms, but it's not required and contains some unexercised code.

Thanks,
--Jacob

[1]
https://www.postgresql.org/message-id/fcc3ebeb7f05775b63f3207ed52a54ea5d17fb42.camel%40vmware.com
[2]
https://www.postgresql.org/message-id/20230123015255.h3jro3yyitlsqykp%40awork3.anarazel.de
[3]
https://www.postgresql.org/message-id/CAAWbhmh_QqCnRVV8ct3gJULReQjWxLTaTBqs%2BfV7c7FpH0zbew%40mail.gmail.com



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Tue, Feb 28, 2023 at 03:38:21PM -0800, Jacob Champion wrote:
> 0001 and 0002 are the core features. 0003 is a more future-looking
> refactoring of the internals, to make it easier to handle more SASL
> mechanisms, but it's not required and contains some unexercised code.

I was refreshing my mind with 0001 yesterday, and except for the two
parts where we need to worry about AUTH_REQ_OK being sent too early
and the business with gssenc, this is a rather straight-forward.  It
also looks like the the participants of the thread are OK with the
design you are proposing (list of keywords, potentially negative
patterns).  I think that I can get this part merged for this CF, at
least, not sure about the rest :p
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Fri, Mar 3, 2023 at 6:35 PM Michael Paquier <michael@paquier.xyz> wrote:
> I was refreshing my mind with 0001 yesterday, and except for the two
> parts where we need to worry about AUTH_REQ_OK being sent too early
> and the business with gssenc, this is a rather straight-forward.  It
> also looks like the the participants of the thread are OK with the
> design you are proposing (list of keywords, potentially negative
> patterns).  I think that I can get this part merged for this CF, at
> least, not sure about the rest :p

Thanks! Is there anything that would make the sslcertmode patch more
palatable? Or any particular areas of concern?

--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Mon, Mar 06, 2023 at 04:02:25PM -0800, Jacob Champion wrote:
> On Fri, Mar 3, 2023 at 6:35 PM Michael Paquier <michael@paquier.xyz> wrote:
>> I was refreshing my mind with 0001 yesterday, and except for the two
>> parts where we need to worry about AUTH_REQ_OK being sent too early
>> and the business with gssenc, this is a rather straight-forward.  It
>> also looks like the the participants of the thread are OK with the
>> design you are proposing (list of keywords, potentially negative
>> patterns).  I think that I can get this part merged for this CF, at
>> least, not sure about the rest :p
>
> Thanks! Is there anything that would make the sslcertmode patch more
> palatable? Or any particular areas of concern?

I have been reviewing 0001, finishing with the attached, and that's
nice work.  My notes are below.

pqDropServerData() is in charge of cleaning up the transient data of a
connection between different attempts.  Shouldn't client_finished_auth
be reset to false there?  No parameters related to the connection
parameters should be reset in this code path, but this state is
different.  It does not seem possible that we could reach
pqDropServerData() after client_finished_auth has been set to true,
but that feels safer.  I was tempted first to do that as well in
makeEmptyPGconn(), but we do a memset(0) there, so there is no point
in doing that anyway ;)

require_auth needs a cleanup in freePGconn().

+       case AUTH_REQ_SCM_CREDS:
+           return libpq_gettext("server requested UNIX socket credentials");
I am not really cool with the fact that this would fail and that we
offer no options to control that.  Now, this involves servers up to
9.1, which is also a very good to rip of this code entirely.  For now,
I think that we'd better allow this option, and discuss the removal of
that in a separate thread.

pgindent has been complaining on the StaticAssertDecl() in fe-auth.c:
src/interfaces/libpq/fe-auth.c: Error@847: Unbalanced parens
Warning@847: Extra )
Warning@847: Extra )
Warning@848: Extra )

From what I can see, this comes from the use of {0} within the
expression itself.  I don't really want to dig into why pg_bsd_indent
thinks this is a bad idea, so let's just move the StaticAssertDecl() a
bit, like in the attached.  The result is the same.

As of the "sensitive" cases of the patch:
- I don't really think that we have to care much of the cases like
"none,scram" meaning that a SASL exchange hastily downgraded to
AUTH_REQ_OK by the server would be a success, as "none" means that the
client is basically OK with trust-level.  This said, "none" could be a
dangerous option in some cases, while useful in others.
- SSPI is the default connection setup for the TAP tests on Windows.
We could stick a small test somewhere, perhaps, certainly not in
src/test/authentication/.
- SASL/SCRAM is indeed a problem on its own.  My guess is that we
should let channel_binding do the job for SASL, or introduce a new
option to decide which sasl mechanisms are authorized.  At the end,
using "scram-sha-256" as the keyword is fine by me as we use that even
for HBA files, so that's quite known now, I hope.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On 3/8/23 22:35, Michael Paquier wrote:
> I have been reviewing 0001, finishing with the attached, and that's
> nice work.  My notes are below.

Thanks!

> pqDropServerData() is in charge of cleaning up the transient data of a
> connection between different attempts.  Shouldn't client_finished_auth
> be reset to false there?  No parameters related to the connection
> parameters should be reset in this code path, but this state is
> different.  It does not seem possible that we could reach
> pqDropServerData() after client_finished_auth has been set to true,
> but that feels safer.

Yeah, that seems reasonable.

> +       case AUTH_REQ_SCM_CREDS:
> +           return libpq_gettext("server requested UNIX socket credentials");
> I am not really cool with the fact that this would fail and that we
> offer no options to control that.  Now, this involves servers up to
> 9.1, which is also a very good to rip of this code entirely.  For now,
> I think that we'd better allow this option, and discuss the removal of
> that in a separate thread.

Fair enough.

> pgindent has been complaining on the StaticAssertDecl() in fe-auth.c:
> src/interfaces/libpq/fe-auth.c: Error@847: Unbalanced parens
> Warning@847: Extra )
> Warning@847: Extra )
> Warning@848: Extra )
> 
> From what I can see, this comes from the use of {0} within the
> expression itself.  I don't really want to dig into why pg_bsd_indent
> thinks this is a bad idea, so let's just move the StaticAssertDecl() a
> bit, like in the attached.  The result is the same.

Works for me. I wonder if

   sizeof(((PGconn*) 0)->allowed_auth_methods)

would make pgindent any happier? That'd let you keep the assertion local
to auth_method_allowed, but it looks scarier. :)

> As of the "sensitive" cases of the patch:
> - I don't really think that we have to care much of the cases like
> "none,scram" meaning that a SASL exchange hastily downgraded to
> AUTH_REQ_OK by the server would be a success, as "none" means that the
> client is basically OK with trust-level.  This said, "none" could be a
> dangerous option in some cases, while useful in others.

Yeah. I think a server shouldn't be allowed to abandon a SCRAM exchange
partway through, but that's completely independent of this patchset.

> - SSPI is the default connection setup for the TAP tests on Windows.

Oh, I don't think I ever noticed that.

> We could stick a small test somewhere, perhaps, certainly not in
> src/test/authentication/.

Where were you thinking? (Would it be so bad to have a tiny
t/005_sspi.pl that's just skipped on *nix?)

> - SASL/SCRAM is indeed a problem on its own.  My guess is that we
> should let channel_binding do the job for SASL, or introduce a new
> option to decide which sasl mechanisms are authorized.  At the end,
> using "scram-sha-256" as the keyword is fine by me as we use that even
> for HBA files, so that's quite known now, I hope.

Did you have any thoughts about the 0003 generalization attempt?

>     -+  if (conn->require_auth)
>     ++  if (conn->require_auth && conn->require_auth[0])

Thank you for that catch. I guess we should test somewhere that
`require_auth=` behaves normally?

>      +                  reason = libpq_gettext("server did not complete authentication"),
>     -+                  result = false;
>     ++                      result = false;
>      +              }

This reindentation looks odd.

nit: some of the new TAP test names have been rewritten with commas,
others with colons.

Thanks,
--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Fri, Mar 10, 2023 at 02:32:17PM -0800, Jacob Champion wrote:
> On 3/8/23 22:35, Michael Paquier wrote:
> Works for me. I wonder if
>
>    sizeof(((PGconn*) 0)->allowed_auth_methods)
>
> would make pgindent any happier? That'd let you keep the assertion local
> to auth_method_allowed, but it looks scarier. :)

I can check that, now it's not bad to keep the assertion as it is,
either.

>> As of the "sensitive" cases of the patch:
>> - I don't really think that we have to care much of the cases like
>> "none,scram" meaning that a SASL exchange hastily downgraded to
>> AUTH_REQ_OK by the server would be a success, as "none" means that the
>> client is basically OK with trust-level.  This said, "none" could be a
>> dangerous option in some cases, while useful in others.
>
> Yeah. I think a server shouldn't be allowed to abandon a SCRAM exchange
> partway through, but that's completely independent of this patchset.

Agreed.

>> We could stick a small test somewhere, perhaps, certainly not in
>> src/test/authentication/.
>
> Where were you thinking? (Would it be so bad to have a tiny
> t/005_sspi.pl that's just skipped on *nix?)

Hmm, OK.  It may be worth having a 005_sspi.pl in
src/test/authentication/ specifically for Windows.  This patch gives
at least one reason to do so.  Looking at pg_regress.c, we have that:
    if (config_auth_datadir)
    {
#ifdef ENABLE_SSPI
        if (!use_unix_sockets)
            config_sspi_auth(config_auth_datadir, user);
#endif
        exit(0);
    }

So applying a check on $use_unix_sockets should be OK, I hope.

>> - SASL/SCRAM is indeed a problem on its own.  My guess is that we
>> should let channel_binding do the job for SASL, or introduce a new
>> option to decide which sasl mechanisms are authorized.  At the end,
>> using "scram-sha-256" as the keyword is fine by me as we use that even
>> for HBA files, so that's quite known now, I hope.
>
> Did you have any thoughts about the 0003 generalization attempt?

Not yet, unfortunately.

> >     -+  if (conn->require_auth)
> >     ++  if (conn->require_auth && conn->require_auth[0])
>
> Thank you for that catch. I guess we should test somewhere that
> `require_auth=` behaves normally?

Yeah, that seems like an idea.  That would be cheap enough.

>>      +                  reason = libpq_gettext("server did not complete authentication"),
>>     -+                  result = false;
>>     ++                      result = false;
>>      +              }
>
> This reindentation looks odd.

That's because the previous line has a comma.  So the reindent is
right, not the code.

> nit: some of the new TAP test names have been rewritten with commas,
> others with colons.

Indeed, I thought to have caught all of them, but you wrote a lot of
tests :)

Could you send a new patch with all these adjustments?  That would
help a lot.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Fri, Mar 10, 2023 at 3:09 PM Michael Paquier <michael@paquier.xyz> wrote:
> >>      +                  reason = libpq_gettext("server did not complete authentication"),
> >>     -+                  result = false;
> >>     ++                      result = false;
> >>      +              }
> >
> > This reindentation looks odd.
>
> That's because the previous line has a comma.  So the reindent is
> right, not the code.

Whoops. :(

> Could you send a new patch with all these adjustments?  That would
> help a lot.

Will do!

Thanks,
--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Fri, Mar 10, 2023 at 3:16 PM Jacob Champion <jchampion@timescale.com> wrote:
> > Could you send a new patch with all these adjustments?  That would
> > help a lot.
>
> Will do!

Here's a v16:
- updated 0001 patch message
- all test names should have commas rather than colons now
- new test for an empty require_auth
- new SSPI suite (note that it doesn't run by default on Cirrus, due
to the use of PG_TEST_USE_UNIX_SOCKETS)
- fixed errant comma at EOL

Thanks,
--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Mon, Mar 13, 2023 at 12:38:10PM -0700, Jacob Champion wrote:
> Here's a v16:
> - updated 0001 patch message
> - all test names should have commas rather than colons now
> - new test for an empty require_auth
> - new SSPI suite (note that it doesn't run by default on Cirrus, due
> to the use of PG_TEST_USE_UNIX_SOCKETS)
> - fixed errant comma at EOL

0001 was looking fine enough seen from here, so applied it after
tweaking a few comments.  That's enough to cover most of the needs of
this thread.

0002 looks pretty simple as well, I think that's worth a look for this
CF.  I am not sure about 0003, to be honest, as I am wondering if
there could be a better solution than tying more the mechanism names
with the expected AUTH_REQ_* values..
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Mon, Mar 13, 2023 at 10:39 PM Michael Paquier <michael@paquier.xyz> wrote:
> 0001 was looking fine enough seen from here, so applied it after
> tweaking a few comments.  That's enough to cover most of the needs of
> this thread.

Thank you very much!

> 0002 looks pretty simple as well, I think that's worth a look for this
> CF.

Cool. v17 just rebases the set over HEAD, then, for cfbot.

> I am not sure about 0003, to be honest, as I am wondering if
> there could be a better solution than tying more the mechanism names
> with the expected AUTH_REQ_* values..

Yeah, I'm not particularly excited about the approach I took. It'd be
easier if we had a second SASL method to verify the implementation...
I'd also proposed just adding an Assert, as a third option, to guide
the eventual SASL implementer back to this conversation?

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Tue, Mar 14, 2023 at 12:14:40PM -0700, Jacob Champion wrote:
> On Mon, Mar 13, 2023 at 10:39 PM Michael Paquier <michael@paquier.xyz> wrote:
>> 0002 looks pretty simple as well, I think that's worth a look for this
>> CF.
>
> Cool. v17 just rebases the set over HEAD, then, for cfbot.

I have looked at 0002, and I am on board with using a separate
connection parameter for this case, orthogonal to require_auth, with
the three value "allow", "disable" and "require".  So that's one thing
:)

-      # Function introduced in OpenSSL 1.0.2.
+      # Functions introduced in OpenSSL 1.0.2. LibreSSL doesn't have all of these.
       ['X509_get_signature_nid'],
+      ['SSL_CTX_set_cert_cb'],

From what I can see, X509_get_signature_nid() is in LibreSSL, but not
SSL_CTX_set_cert_cb().  Perhaps that's worth having two different
comments?

+           <para>
+            a certificate may be sent, if the server requests one and it has
+            been provided via <literal>sslcert</literal>
+           </para>

It seems to me that this description is not completely exact.  The
default is to look at ~/.postgresql/postgresql.crt, so sslcert is not
mandatory.  There could be a certificate even without sslcert set.

+           libpq_append_conn_error(conn, "sslcertmode value \"%s\" invalid when SSL support is not compiled in",
+                                   conn->sslcertmode);

This string could be combined with the same one used for sslmode,
saving a bit in translation effortm by making the connection parameter
name a value of the string ("%s value \"%s\" invalid ..").  The second
string where HAVE_SSL_CTX_SET_CERT_CB is not set could be refactored
the same way, I guess.

+        * figure out if a certficate was actually requested, so "require" is
s/certficate/certificate/.

contrib/sslinfo/ has ssl_client_cert_present(), that we could use in
the tests to make sure that the client has actually sent a
certificate?  How about adding some of these tests to 003_sslinfo.pl
for the "allow" and "require" cases?  Even for "disable", we could
check check that ssl_client_cert_present() returns false?  That would
make four tests if everything is covered:
- "allow" without a certificate sent.
- "allow" with a certificate sent.
- "disable".
- "require"

+       if (!conn->ssl_cert_requested)
+       {
+           libpq_append_conn_error(conn, "server did not request a certificate");
+           return false;
+       }
+       else if (!conn->ssl_cert_sent)
+       {
+           libpq_append_conn_error(conn, "server accepted connection without a valid certificate");
+           return false;
+       }
Perhaps useless question: should this say "SSL certificate"?

freePGconn() is missing a free(sslcertmode).
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Tue, Mar 21, 2023 at 11:01 PM Michael Paquier <michael@paquier.xyz> wrote:
> -      # Function introduced in OpenSSL 1.0.2.
> +      # Functions introduced in OpenSSL 1.0.2. LibreSSL doesn't have all of these.
>        ['X509_get_signature_nid'],
> +      ['SSL_CTX_set_cert_cb'],
>
> From what I can see, X509_get_signature_nid() is in LibreSSL, but not
> SSL_CTX_set_cert_cb().  Perhaps that's worth having two different
> comments?

I took a stab at that in v18. I diverged a bit between Meson and
Autoconf, which you may not care for.

> +           <para>
> +            a certificate may be sent, if the server requests one and it has
> +            been provided via <literal>sslcert</literal>
> +           </para>
>
> It seems to me that this description is not completely exact.  The
> default is to look at ~/.postgresql/postgresql.crt, so sslcert is not
> mandatory.  There could be a certificate even without sslcert set.

Reworded.

> +           libpq_append_conn_error(conn, "sslcertmode value \"%s\" invalid when SSL support is not compiled in",
> +                                   conn->sslcertmode);
>
> This string could be combined with the same one used for sslmode,
> saving a bit in translation effortm by making the connection parameter
> name a value of the string ("%s value \"%s\" invalid ..").

Done.

> +        * figure out if a certficate was actually requested, so "require" is
> s/certficate/certificate/.

Heh, fixed. I need new glasses, clearly.

> contrib/sslinfo/ has ssl_client_cert_present(), that we could use in
> the tests to make sure that the client has actually sent a
> certificate?  How about adding some of these tests to 003_sslinfo.pl
> for the "allow" and "require" cases?

Added; see what you think.

> +       if (!conn->ssl_cert_requested)
> +       {
> +           libpq_append_conn_error(conn, "server did not request a certificate");
> +           return false;
> +       }
> +       else if (!conn->ssl_cert_sent)
> +       {
> +           libpq_append_conn_error(conn, "server accepted connection without a valid certificate");
> +           return false;
> +       }
> Perhaps useless question: should this say "SSL certificate"?

I have no objection, so done that way.

> freePGconn() is missing a free(sslcertmode).

Argh, I keep forgetting that. Fixed, thanks!

--Jacob

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Thu, Mar 23, 2023 at 03:40:55PM -0700, Jacob Champion wrote:
> On Tue, Mar 21, 2023 at 11:01 PM Michael Paquier <michael@paquier.xyz> wrote:
>> contrib/sslinfo/ has ssl_client_cert_present(), that we could use in
>> the tests to make sure that the client has actually sent a
>> certificate?  How about adding some of these tests to 003_sslinfo.pl
>> for the "allow" and "require" cases?
>
> Added; see what you think.

That's a pretty good test design, covering all 4 cases.  Nice.

>> freePGconn() is missing a free(sslcertmode).
>
> Argh, I keep forgetting that. Fixed, thanks!

I have spent a couple of hours looking at the whole again today,
testing that with OpenSSL to make sure that everything was OK.  Apart
from a few tweaks, that seemed pretty good.  So, applied.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
On Thu, Mar 23, 2023 at 10:18 PM Michael Paquier <michael@paquier.xyz> wrote:
> I have spent a couple of hours looking at the whole again today,
> testing that with OpenSSL to make sure that everything was OK.  Apart
> from a few tweaks, that seemed pretty good.  So, applied.

Thank you!

--Jacob



Re: [PoC] Let libpq reject unexpected authentication requests

From
Michael Paquier
Date:
On Fri, Mar 24, 2023 at 09:30:06AM -0700, Jacob Champion wrote:
> On Thu, Mar 23, 2023 at 10:18 PM Michael Paquier <michael@paquier.xyz> wrote:
>> I have spent a couple of hours looking at the whole again today,
>> testing that with OpenSSL to make sure that everything was OK.  Apart
>> from a few tweaks, that seemed pretty good.  So, applied.
>
> Thank you!

Please note that the CF entry has been marked as committed.  We should
really do something about having a cleaner separation between SASL,
the mechanisms and the AUTH_REQ_* codes, in the long term, though
honestly I don't know yet what would be the most elegant and the least
error-prone approach.  And for anything that touches authentication,
simpler means better.
--
Michael

Attachment

Re: [PoC] Let libpq reject unexpected authentication requests

From
Jacob Champion
Date:
Michael Paquier <michael@paquier.xyz> wrote:
> Please note that the CF entry has been marked as committed.  We should
> really do something about having a cleaner separation between SASL,
> the mechanisms and the AUTH_REQ_* codes, in the long term, though
> honestly I don't know yet what would be the most elegant and the least
> error-prone approach.  And for anything that touches authentication,
> simpler means better.

I've taken another shot at this over on the OAuth thread [1], for
those who are still interested; see v40-0002. It's more code than my
previous attempt, but I think it does a clearer job of separating the
two concerns.

Thanks,
--Jacob

[1] https://postgr.es/m/CAOYmi+=FzVg+C-pQHCwjW0qU-POHmzZaD2z3CdsACj==14H8kQ@mail.gmail.com