Re: [PATCH] Accept IP addresses in server certificate SANs
At Thu, 16 Dec 2021 01:13:57 +, Jacob Champion wrote in > This patch arose because I was writing tests for the NSS implementation > that used a server cert with both DNS names and IP addresses, and then > they failed when I ran those tests against the OpenSSL implementation. > NSS supports this functionality natively. Anecdotally, I've heard from > at least one client group who is utilizing IP-based certificates in > their cloud deployments. It seems uncommon but still useful. > > There are two open questions I have; they're based on NSS > implementation details that I did not port here: > > - NSS allows an IPv4 SAN to match an IPv6 mapping of that same address, > and vice-versa. I chose not to implement that behavior, figuring it > is easy enough for people to issue a certificate with both addresses. > Is that okay? > - If a certificate contains only iPAddress SANs, and none of them > match, I fall back to check the certificate Common Name. OpenSSL will > not do this (its X509_check_ip considers only SANs). NSS will only do > this if the client's address is itself a DNS name. The spec says that > we can't fall back to Common Name if the SANs contain any DNS > entries, but it's silent on the subject of IP addresses. What should > the behavior be? > > The patchset roadmap: > > - 0001 moves inet_net_pton() to src/port, since libpq will need it. > - 0002 implements the new functionality and adds tests. > > WDYT? In RFC2828 and 6125, > In some cases, the URI is specified as an IP address rather than a > hostname. In this case, the iPAddress subjectAltName must be present > in the certificate and must exactly match the IP in the URI. It seems like saying that we must search for iPAddress and mustn't use CN nor dNSName if the client connected using IP address. Otherwise, if the host name is a domain name, we use only dNSName if present, and use CN otherwise. That behavior seems agreeing to what you wrote as NSS's behavior. That being said it seems to me we should preserve that behavior at least for OpenSSL as an established behavior. In short, I think the current behavior of the patch is the direction we would go but some documentation is may be needed. I'm not sure about ipv4 comptible addresses. However, I think we can identify ipv4 compatible address easily. +* pg_inet_net_pton() will accept CIDR masks, which we don't want to +* match, so skip the comparison if the host string contains a slash. +*/ + if (!strchr(host, '/') + && pg_inet_net_pton(PGSQL_AF_INET6, host, addr, -1) == 128) If a cidr is given, pg_inet_net_pton returns a number less than 128 so we don't need to check '/' explicity? (I'm not sure '/128' is sensible but doesn't harm..) regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On 12/15/21 20:13, Jacob Champion wrote: > Hello all, > > libpq currently supports server certificates with a single IP address > in the Common Name. It's fairly brittle; as far as I can tell, the > single name you choose has to match the client's address exactly. > > Attached is a patch for libpq to support IP addresses in the server's > Subject Alternative Names, which would allow admins to issue certs for > multiple IP addresses, both IPv4 and IPv6, and mix them with > alternative DNS hostnames. These addresses are compared bytewise > instead of stringwise, so the client can contact the server via > alternative spellings of the same IP address. Good job, this is certainly going to be useful. > > This patch arose because I was writing tests for the NSS implementation > that used a server cert with both DNS names and IP addresses, and then > they failed when I ran those tests against the OpenSSL implementation. > NSS supports this functionality natively. Anecdotally, I've heard from > at least one client group who is utilizing IP-based certificates in > their cloud deployments. It seems uncommon but still useful. > > There are two open questions I have; they're based on NSS > implementation details that I did not port here: > > - NSS allows an IPv4 SAN to match an IPv6 mapping of that same address, > and vice-versa. I chose not to implement that behavior, figuring it > is easy enough for people to issue a certificate with both addresses. > Is that okay? Sure. > > - If a certificate contains only iPAddress SANs, and none of them > match, I fall back to check the certificate Common Name. OpenSSL will > not do this (its X509_check_ip considers only SANs). NSS will only do > this if the client's address is itself a DNS name. The spec says that > we can't fall back to Common Name if the SANs contain any DNS > entries, but it's silent on the subject of IP addresses. What should > the behavior be? I don't think we should fall back on the CN. It would seem quite odd to do so for IP addresses but not for DNS names. cheers andrew -- Andrew Dunstan EDB: https://www.enterprisedb.com
Re: [PATCH] Accept IP addresses in server certificate SANs
On Thu, 2021-12-16 at 14:54 +0900, Kyotaro Horiguchi wrote: > In RFC2828 and 6125, > > > In some cases, the URI is specified as an IP address rather than a > > hostname. In this case, the iPAddress subjectAltName must be present > > in the certificate and must exactly match the IP in the URI. Ah, right, I misremembered. Disregard my statement that the spec is "silent on the subject", sorry. > It seems like saying that we must search for iPAddress and mustn't use > CN nor dNSName if the client connected using IP address. Otherwise, if > the host name is a domain name, we use only dNSName if present, and > use CN otherwise. That behavior seems agreeing to what you wrote as > NSS's behavior. NSS departs slightly from the spec and will additionally try to match an IP address against the CN, but only if there are no iPAddresses in the SAN. It roughly matches the logic for DNS names. Here's the description of the NSS behavior and some of the reasoning behind it, quoted from a developer on Bugzilla [1]: > Elsewhere in RFC 2818, it says > >If a subjectAltName extension of type dNSName is present, that MUST >be used as the identity. Otherwise, the (most specific) Common Name >field in the Subject field of the certificate MUST be used. > > Notice that this section is not conditioned upon the URI being a hostname > and not an IP address. So this statement conflicts with the one cited > above. > > I implemented this policy: > > if the URI contains a host name > if the subject alt name is present and has one or more DNS names > use the DNS names in that extension as the server identity > else > use the subject common name as the server identity > else if the URI contains an IP address > if the subject alt name is present and has one or more IP addresses > use the IP addresses in that extension as the server identity > else > compare the URI IP address string with the subject common name. It sounds like both you and Andrew might be comfortable with that same behavior? I think it looks like a sane solution, so I'll implement that and we can see what it looks like. (My work on this will be paused over the end-of-year holidays.) > That being said it seems to me we should preserve > that behavior at least for OpenSSL as an established behavior. That part is interesting. I'll talk more about that in my reply to Andrew. > In short, I think the current behavior of the patch is the direction > we would go but some documentation is may be needed. Great! > I'm not sure about ipv4 comptible addresses. However, I think we can > identify ipv4 compatible address easily. Yeah, it would probably not be a difficult feature to add later. > +* pg_inet_net_pton() will accept CIDR masks, which we don't > want to > +* match, so skip the comparison if the host string contains > a slash. > +*/ > + if (!strchr(host, '/') > + && pg_inet_net_pton(PGSQL_AF_INET6, host, addr, -1) > == 128) > > If a cidr is given, pg_inet_net_pton returns a number less than 128 so > we don't need to check '/' explicity? (I'm not sure '/128' is > sensible but doesn't harm..) Personally I think that, if someone wants your libpq to connect to a server with a hostname of "some:ipv6::address/128", then they are trying to pull something (evading a poorly coded blocklist, perhaps?) and we should not allow that to match an IP. Thoughts? Thanks for the review! --Jacob [1] https://bugzilla.mozilla.org/show_bug.cgi?id=103752
Re: [PATCH] Accept IP addresses in server certificate SANs
On Thu, 2021-12-16 at 10:50 -0500, Andrew Dunstan wrote: > Good job, this is certainly going to be useful. Thanks! > I don't think we should fall back on the CN. It would seem quite odd to > do so for IP addresses but not for DNS names. So there's at least one compatibility concern with disabling the fallback, in that there could be existing users that are happily using a certificate with an IP address CN, and libpq is just ignoring any iPAddress SANs that the certificate has. Once libpq becomes aware of those, it will stop accepting the CN and the certificate might stop working. Personally I think that's acceptable, but it would probably warrant a release note or some such. I will work on implementing behavior that's modeled off of the NSS matching logic (see my reply to Horiguchi-san), which will at least make it more logically consistent, and we can see what that looks like? Thanks for the review! --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
At Thu, 16 Dec 2021 18:44:54 +, Jacob Champion wrote in > On Thu, 2021-12-16 at 14:54 +0900, Kyotaro Horiguchi wrote: > > It seems like saying that we must search for iPAddress and mustn't use > > CN nor dNSName if the client connected using IP address. Otherwise, if > > the host name is a domain name, we use only dNSName if present, and > > use CN otherwise. That behavior seems agreeing to what you wrote as > > NSS's behavior. > > NSS departs slightly from the spec and will additionally try to match > an IP address against the CN, but only if there are no iPAddresses in > the SAN. It roughly matches the logic for DNS names. OpenSSL seems different. X509_check_host() tries SAN then CN iff SAN doesn't exist. X509_check_ip() tries SAN and completely ignores iPAdress and CN. > Here's the description of the NSS behavior and some of the reasoning > behind it, quoted from a developer on Bugzilla [1]: > > > Elsewhere in RFC 2818, it says > > > >If a subjectAltName extension of type dNSName is present, that MUST > >be used as the identity. Otherwise, the (most specific) Common Name > >field in the Subject field of the certificate MUST be used. > > > > Notice that this section is not conditioned upon the URI being a hostname > > and not an IP address. So this statement conflicts with the one cited > > above. > > > > I implemented this policy: > > > > if the URI contains a host name > > if the subject alt name is present and has one or more DNS names > > use the DNS names in that extension as the server identity > > else > > use the subject common name as the server identity > > else if the URI contains an IP address > > if the subject alt name is present and has one or more IP addresses > > use the IP addresses in that extension as the server identity > > else > > compare the URI IP address string with the subject common name. (Wow. The article is 20-years old.) *I* am fine with it. > It sounds like both you and Andrew might be comfortable with that same > behavior? I think it looks like a sane solution, so I'll implement that > and we can see what it looks like. (My work on this will be paused over > the end-of-year holidays.) > > I'm not sure about ipv4 comptible addresses. However, I think we can > > identify ipv4 compatible address easily. > > Yeah, it would probably not be a difficult feature to add later. I agree. > > +* pg_inet_net_pton() will accept CIDR masks, which we > > don't want to > > +* match, so skip the comparison if the host string > > contains a slash. > > +*/ > > + if (!strchr(host, '/') > > + && pg_inet_net_pton(PGSQL_AF_INET6, host, addr, -1) > > == 128) > > > > If a cidr is given, pg_inet_net_pton returns a number less than 128 so > > we don't need to check '/' explicity? (I'm not sure '/128' is > > sensible but doesn't harm..) > > Personally I think that, if someone wants your libpq to connect to a > server with a hostname of "some:ipv6::address/128", then they are > trying to pull something (evading a poorly coded blocklist, perhaps?) > and we should not allow that to match an IP. Thoughts? If the client could connect to the network-address, it could be said that we can assume that address is the name:p Just kidding. As the name suggests, the function reads a network address. And the only user is network_in(). I think we should provide pg_inet_pton() instead of abusing pg_inet_net_pton(). inet_net_pton_*() functions can be modified to reject /cidr part without regression so we are able to have pg_inet_pton() with a small amount of change. - inet_net_pton_ipv4(const char *src, u_char *dst) + inet_net_pton_ipv4_internal(const char *src, u_char *dst, bool netaddr) + inet_net_pton_ipv4(const char *src, u_char *dst) (calls inet_net_pton_ipv4_internal(src, dst, true)) + inet_pton_ipv4(const char *src, u_char *dst) (calls inet_net_pton_ipv4_internal(src, dst, false)) > Thanks for the review! > --Jacob > > [1] https://bugzilla.mozilla.org/show_bug.cgi?id=103752 -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
Sorry for the silly mistake. At Fri, 17 Dec 2021 15:40:10 +0900 (JST), Kyotaro Horiguchi wrote in > > NSS departs slightly from the spec and will additionally try to match > > an IP address against the CN, but only if there are no iPAddresses in > > the SAN. It roughly matches the logic for DNS names. > > OpenSSL seems different. X509_check_host() tries SAN then CN iff SAN > doesn't exist. X509_check_ip() tries SAN and completely ignores > iPAdress and CN. OpenSSL seems different. X509_check_host() tries SAN then CN iff SAN doesn't exist. X509_check_ip() tries iPAddress and completely ignores CN. regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
Hi, On 2021-12-16 01:13:57 +, Jacob Champion wrote: > Attached is a patch for libpq to support IP addresses in the server's > Subject Alternative Names, which would allow admins to issue certs for > multiple IP addresses, both IPv4 and IPv6, and mix them with > alternative DNS hostnames. These addresses are compared bytewise > instead of stringwise, so the client can contact the server via > alternative spellings of the same IP address. This fails to build on windows: https://cirrus-ci.com/task/6734650927218688?logs=build#L1029 [14:33:28.277] network.obj : error LNK2019: unresolved external symbol pg_inet_net_pton referenced in function network_in [c:\cirrus\postgres.vcxproj] Greetings, Andres Freund
Re: [PATCH] Accept IP addresses in server certificate SANs
At Thu, 17 Mar 2022 21:55:07 +, Jacob Champion wrote in > On Wed, 2022-03-16 at 23:49 +, Jacob Champion wrote: > > Thank you for the explanation -- the misunderstanding was all on my > > end. I thought you were asking me to move the check_cn assignment > > instead of copying it to the end. I agree that your suggestion is much > > clearer, and I'll make that change tomorrow. > > Done in v8. Thanks again for your suggestions (and for your > perseverance when I didn't get it)! Thanks! .. and some nitpicks..(Sorry) fe-secure-common.c doesn't need netinet/in.h. +++ b/src/include/utils/inet.h .. +#include "common/inet-common.h" I'm not sure about the project policy on #include practice, but I think it is the common practice not to include headers that are not required by the file itself. In this case, fe-secure-common.h itself doesn't need the include. Instead, fe-secure-openssl.c and fe-secure-common.c needs the include. regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
At Fri, 18 Mar 2022 16:38:57 +0900 (JST), Kyotaro Horiguchi wrote in > At Thu, 17 Mar 2022 21:55:07 +, Jacob Champion > wrot> Thanks! .. and some nitpicks..(Sorry) > > fe-secure-common.c doesn't need netinet/in.h. > > > +++ b/src/include/utils/inet.h > .. > +#include "common/inet-common.h" > > I'm not sure about the project policy on #include practice, but I > think it is the common practice not to include headers that are not > required by the file itself. In this case, fe-secure-common.h itself > doesn't need the include. Instead, fe-secure-openssl.c and > fe-secure-common.c needs the include. I noticed that this doesn't contain doc changes. https://www.postgresql.org/docs/current/libpq-ssl.html > In verify-full mode, the host name is matched against the > certificate's Subject Alternative Name attribute(s), or against the > Common Name attribute if no Subject Alternative Name of type dNSName > is present. If the certificate's name attribute starts with an > asterisk (*), the asterisk will be treated as a wildcard, which will > match all characters except a dot (.). This means the certificate will > not match subdomains. If the connection is made using an IP address > instead of a host name, the IP address will be matched (without doing > any DNS lookups). This refers to dNSName, so we should revise this so that it describes the new behavior. regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On Tue, 2022-03-22 at 13:32 +0900, Kyotaro Horiguchi wrote: > At Fri, 18 Mar 2022 16:38:57 +0900 (JST), Kyotaro Horiguchi > wrote in > > > > fe-secure-common.c doesn't need netinet/in.h. > > > > > > +++ b/src/include/utils/inet.h > > .. > > +#include "common/inet-common.h" > > > > I'm not sure about the project policy on #include practice, but I > > think it is the common practice not to include headers that are not > > required by the file itself. In this case, fe-secure-common.h itself > > doesn't need the include. Instead, fe-secure-openssl.c and > > fe-secure-common.c needs the include. Thanks, looks like I had some old header dependencies left over from several versions ago. Fixed in v9. > I noticed that this doesn't contain doc changes. > > https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.postgresql.org%2Fdocs%2Fcurrent%2Flibpq-ssl.html&data=04%7C01%7Cpchampion%40vmware.com%7Cb25566c0f0124a30221908da0bbcec13%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637835203290105956%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=sZuKc9UxmW1oZQij%2F%2F91rkEF57BZiQebkXtvEt%2FdROU%3D&reserved=0 > > > In verify-full mode, the host name is matched against the > > certificate's Subject Alternative Name attribute(s), or against the > > Common Name attribute if no Subject Alternative Name of type dNSName > > is present. If the certificate's name attribute starts with an > > asterisk (*), the asterisk will be treated as a wildcard, which will > > match all characters except a dot (.). This means the certificate will > > not match subdomains. If the connection is made using an IP address > > instead of a host name, the IP address will be matched (without doing > > any DNS lookups). > > This refers to dNSName, so we should revise this so that it describes > the new behavior. v9 contains the bare minimum but I don't think it's quite enough. How much of the behavior (and edge cases) do you think we should detail here? All of it? Thanks, --Jacob commit 6278176f7f417f0afecceb71807eb480e20769ef Author: Jacob Champion Date: Tue Mar 22 13:21:48 2022 -0700 squash! libpq: add OAUTHBEARER SASL mechanism feedback: - clean up header deps - add a mention in the docs diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 3998b1781b..bdbfed1aa5 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -8343,7 +8343,8 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*) In verify-full mode, the host name is matched against the certificate's Subject Alternative Name attribute(s), or against the - Common Name attribute if no Subject Alternative Name of type dNSName is + Common Name attribute if no Subject Alternative Name of type dNSName + (or iPAddress, if the connection uses an IP address) is present. If the certificate's name attribute starts with an asterisk (*), the asterisk will be treated as a wildcard, which will match all characters except a dot diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c index 4d78715756..9c408df369 100644 --- a/src/interfaces/libpq/fe-secure-common.c +++ b/src/interfaces/libpq/fe-secure-common.c @@ -19,9 +19,9 @@ #include "postgres_fe.h" -#include #include +#include "common/inet-common.h" #include "fe-secure-common.h" #include "libpq-int.h" diff --git a/src/interfaces/libpq/fe-secure-common.h b/src/interfaces/libpq/fe-secure-common.h index 20ff9ba5db..d18db7138c 100644 --- a/src/interfaces/libpq/fe-secure-common.h +++ b/src/interfaces/libpq/fe-secure-common.h @@ -16,7 +16,6 @@ #ifndef FE_SECURE_COMMON_H #define FE_SECURE_COMMON_H -#include "common/inet-common.h" #include "libpq-fe.h" extern int pq_verify_peer_name_matches_certificate_name(PGconn *conn, diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index 06b166b7fd..b70bd2eb19 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -26,6 +26,7 @@ #include #include +#include "common/inet-common.h" #include "libpq-fe.h" #include "fe-auth.h" #include "fe-secure-common.h" From 84a107da33b7d901cb318f8c2fd140644fbc5100 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 24 Nov 2021 14:46:11 -0800 Subject: [PATCH v9 1/3] Move inet_net_pton() to src/port This will be helpful for IP address verification in libpq. --- src/backend/utils/adt/Makefile | 1 - src/include/port.h | 3 +++ src/include/utils/builtins.h| 4 src/port/Makefile | 1 + src/{backend/utils/adt => port}/inet_net_pton.c | 16 +++- src/tools/msvc/Mkvcbuild.pm | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) rename src/{backend/utils/adt => port}/inet_net_pton.c (96%)
Re: [PATCH] Accept IP addresses in server certificate SANs
At Tue, 22 Mar 2022 20:42:37 +, Jacob Champion wrote in > Thanks, looks like I had some old header dependencies left over from > several versions ago. Fixed in v9. Thanks! Looks perfect. > v9 contains the bare minimum but I don't think it's quite enough. How > much of the behavior (and edge cases) do you think we should detail > here? All of it? I tried to write out the doc part. What do you think about it? regards. -- Kyotaro Horiguchi NTT Open Source Software Center diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 3998b1781b..13e3e63768 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -8342,16 +8342,31 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*) In verify-full mode, the host name is matched against the - certificate's Subject Alternative Name attribute(s), or against the - Common Name attribute if no Subject Alternative Name of type dNSName is + certificate's Subject Alternative Name attribute(s) (SAN), or against the + Common Name attribute if no SAN of type dNSName is present. If the certificate's name attribute starts with an asterisk (*), the asterisk will be treated as a wildcard, which will match all characters except a dot (.). This means the certificate will not match subdomains. If the connection is made using an IP address instead of a host name, the - IP address will be matched (without doing any DNS lookups). + IP address will be matched (without doing any DNS lookups) against SANs of + type iPAddress or dNSName. If no + ipAddress SAN is present and no + matching dNSName SAN is present, the host IP address is + matched against the Common Name attribute. + + + For backward compatibility with earlier versions of PostgreSQL, the host + IP address is verified in a manner different + from https://tools.ietf.org/html/rfc6125";>RFC 6125. + The host IP address is always matched against dNSName + SANs as well as iPAdress SANs, and can be matched + against the Common Name attribute for a certain condition. + + + To allow server certificate verification, one or more root certificates must be placed in the file ~/.postgresql/root.crt
Re: [PATCH] Accept IP addresses in server certificate SANs
On Wed, 2022-03-23 at 14:20 +0900, Kyotaro Horiguchi wrote: > I tried to write out the doc part. What do you think about it? I like it, thanks! I've applied that in v10, with a tweak to two iPAddress spellings and a short expansion of the condition in the Note, and I've added you as a co-author to 0002. --Jacob From 84a107da33b7d901cb318f8c2fd140644fbc5100 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 24 Nov 2021 14:46:11 -0800 Subject: [PATCH v10 1/3] Move inet_net_pton() to src/port This will be helpful for IP address verification in libpq. --- src/backend/utils/adt/Makefile | 1 - src/include/port.h | 3 +++ src/include/utils/builtins.h| 4 src/port/Makefile | 1 + src/{backend/utils/adt => port}/inet_net_pton.c | 16 +++- src/tools/msvc/Mkvcbuild.pm | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) rename src/{backend/utils/adt => port}/inet_net_pton.c (96%) diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 41b486bcef..d173d52157 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -43,7 +43,6 @@ OBJS = \ geo_selfuncs.o \ geo_spgist.o \ inet_cidr_ntop.o \ - inet_net_pton.o \ int.o \ int8.o \ json.o \ diff --git a/src/include/port.h b/src/include/port.h index 3d103a2b31..2852e5b58b 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -515,6 +515,9 @@ extern int pg_codepage_to_encoding(UINT cp); extern char *pg_inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size); +/* port/inet_net_pton.c */ +extern int pg_inet_net_pton(int af, const char *src, void *dst, size_t size); + /* port/pg_strong_random.c */ extern void pg_strong_random_init(void); extern bool pg_strong_random(void *buf, size_t len); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 666e545496..67b9326dc8 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -93,10 +93,6 @@ extern int xidLogicalComparator(const void *arg1, const void *arg2); extern char *pg_inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size); -/* inet_net_pton.c */ -extern int pg_inet_net_pton(int af, const char *src, - void *dst, size_t size); - /* network.c */ extern double convert_network_to_scalar(Datum value, Oid typid, bool *failure); extern Datum network_scan_first(Datum in); diff --git a/src/port/Makefile b/src/port/Makefile index bfe1feb0d4..c3ae7b3d5c 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -43,6 +43,7 @@ OBJS = \ bsearch_arg.o \ chklocale.o \ inet_net_ntop.o \ + inet_net_pton.o \ noblock.o \ path.o \ pg_bitutils.o \ diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/port/inet_net_pton.c similarity index 96% rename from src/backend/utils/adt/inet_net_pton.c rename to src/port/inet_net_pton.c index d3221a1313..bae50ba67e 100644 --- a/src/backend/utils/adt/inet_net_pton.c +++ b/src/port/inet_net_pton.c @@ -14,14 +14,18 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * src/backend/utils/adt/inet_net_pton.c + * src/port/inet_net_pton.c */ #if defined(LIBC_SCCS) && !defined(lint) static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $"; #endif +#ifndef FRONTEND #include "postgres.h" +#else +#include "postgres_fe.h" +#endif #include #include @@ -29,9 +33,19 @@ static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 m #include #include +#ifndef FRONTEND #include "utils/builtins.h" /* pgrminclude ignore */ /* needed on some * platforms */ #include "utils/inet.h" +#else +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that pg_inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) +#endif static int inet_net_pton_ipv4(const char *src, u_char *dst); diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 441d6ae6bf..667b173ec3 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -100,7 +100,7 @@ sub mkvcbuild our @pgportfiles = qw( chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c - getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c + getaddrinfo.c gettimeofday.c inet_net_ntop.c inet_net_pton.c kill.c open.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c dirent.c dlopen.c getopt.c getopt_long.c link.c pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c -- 2.25.1 From 6ad0b390f11a025045be278a789d72547775fe77 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Thu, 18 Nov 2021
Re: [PATCH] Accept IP addresses in server certificate SANs
At Wed, 23 Mar 2022 23:52:06 +, Jacob Champion wrote in > On Wed, 2022-03-23 at 14:20 +0900, Kyotaro Horiguchi wrote: > > I tried to write out the doc part. What do you think about it? > > I like it, thanks! I've applied that in v10, with a tweak to two > iPAddress spellings and a short expansion of the condition in the Note, > and I've added you as a co-author to 0002. I'm fine with it. Thanks. I marked it as Ready-for-Commiter. Note for the patch set: 0001 is preliminary patch to move inet_pton out of src/backend tree. 0002 is the main patch of this patchset 0003 is optional, which introduces pg_inet_pton() only works for IPv6 addresses. 0002 gets the same effect by the following use of pg_inet_net_pton(). > if (!strchr(host, '/') > && pg_inet_net_pton(PGSQL_AF_INET6, host, addr, -1) == 128) regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On Thu, 2022-03-24 at 17:10 +0900, Kyotaro Horiguchi wrote: > I'm fine with it. Thanks. I marked it as Ready-for-Commiter. Thank you for the reviews and feedback! --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
Jacob Champion writes: > [ v10-0001-Move-inet_net_pton-to-src-port.patch etc ] There is something broken about the ssl tests as modified by this patch. The cfbot doesn't provide a lot of evidence about why it's failing, but I applied the patchset locally and what I see is ... ok 47 - mismatch between host name and server certificate sslmode=verify-full: m atches Odd number of elements in hash assignment at /home/postgres/pgsql/src/test/ssl/t /SSL/Server.pm line 288. Use of uninitialized value in concatenation (.) or string at /home/postgres/pgsq l/src/test/ssl/t/SSL/Backend/OpenSSL.pm line 178. Use of uninitialized value in concatenation (.) or string at /home/postgres/pgsq l/src/test/ssl/t/SSL/Backend/OpenSSL.pm line 178. ### Restarting node "primary" # Running: pg_ctl -w -D /home/postgres/pgsql/src/test/ssl/tmp_check/t_001_ssltes ts_primary_data/pgdata -l /home/postgres/pgsql/src/test/ssl/tmp_check/log/001_ss ltests_primary.log restart waiting for server to shut down done server stopped waiting for server to start stopped waiting pg_ctl: could not start server The tail end of the server log is 2022-03-27 17:13:11.482 EDT [551720] FATAL: could not load server certificate file ".crt": No such file or directory so it seems pretty clear that something's fouling up computation of a certificate file name. This may be caused by 9ca234bae or 4a7e964fc. regards, tom lane
Re: [PATCH] Accept IP addresses in server certificate SANs
> On 27 Mar 2022, at 23:19, Tom Lane wrote: > This may be caused by 9ca234bae or 4a7e964fc. I'd say 4a7e964fc is the culprit here. From a quick skim the the switch_server_cert() calls need to be changed along the lines of: from: switch_server_cert($node, 'server-ip-in-dnsname'); to: switch_server_cert($node, certfile => 'server-ip-in-dnsname'); There migth be more changes required, that was the one that stood out. Unless someone beats me to it I'll take a look at fixing up the test in this patch tomorrow. -- Daniel Gustafsson https://vmware.com/
Re: [PATCH] Accept IP addresses in server certificate SANs
Hi, On 2022-03-27 17:19:27 -0400, Tom Lane wrote: > The cfbot doesn't provide a lot of evidence about > why it's failing, but I applied the patchset locally and what > I see is FWIW - and I agree that's not nice user interface wise - just below the cpu / memory graphs there's a "directory browser", allowing you to navigate to the saved log files. Navigating to log/src/test/ssl/tmp_check/log allows you to download https://api.cirrus-ci.com/v1/artifact/task/5261015175659520/log/src/test/ssl/tmp_check/log/regress_log_001_ssltests https://api.cirrus-ci.com/v1/artifact/task/5261015175659520/log/src/test/ssl/tmp_check/log/001_ssltests_primary.log Greetings, Andres Freund
Re: [PATCH] Accept IP addresses in server certificate SANs
> On 28 Mar 2022, at 00:44, Daniel Gustafsson wrote: > I'll take a look at fixing up the test in this patch tomorrow. Fixing up the switch_server_cert() calls and using default_ssl_connstr makes the test pass for me. The required fixes are in the supplied 0004 diff, I kept them separate to allow the original author to incorporate them without having to dig them out to see what changed (named to match the git format-patch output since I think the CFBot just applies the patches in alphabetical order). -- Daniel Gustafsson https://vmware.com/ v10-0001-Move-inet_net_pton-to-src-port.patch Description: Binary data v10-0003-squash-libpq-allow-IP-address-SANs-in-server-cer.patch Description: Binary data v10-0003-squash-libpq-allow-IP-address-SANs-in-server-cer-1.patch Description: Binary data v10-0004-fix-tests.diff Description: Binary data
Re: [PATCH] Accept IP addresses in server certificate SANs
On Mon, 28 Mar 2022 at 05:17, Daniel Gustafsson wrote: > > named to match the git format-patch output > since I think the CFBot just applies the patches in alphabetical order). The first patch doesn't seem to actually apply though so it doesn't get to the subsequent patches. http://cfbot.cputube.org/patch_37_3458.log === Applying patches on top of PostgreSQL commit ID e26114c817b610424010cfbe91a743f591246ff1 === === applying patch ./v10-0001-Move-inet_net_pton-to-src-port.patch patching file src/backend/utils/adt/Makefile Hunk #1 FAILED at 44. 1 out of 1 hunk FAILED -- saving rejects to file src/backend/utils/adt/Makefile.rej patching file src/include/port.h patching file src/include/utils/builtins.h patching file src/port/Makefile patching file src/port/inet_net_pton.c (renamed from src/backend/utils/adt/inet_net_pton.c) patching file src/tools/msvc/Mkvcbuild.pm -- greg
Re: [PATCH] Accept IP addresses in server certificate SANs
On Mon, 2022-03-28 at 11:17 +0200, Daniel Gustafsson wrote: > Fixing up the switch_server_cert() calls and using default_ssl_connstr makes > the test pass for me. The required fixes are in the supplied 0004 diff, I > kept > them separate to allow the original author to incorporate them without having > to dig them out to see what changed (named to match the git format-patch > output > since I think the CFBot just applies the patches in alphabetical order). Thanks! Those changes look good to me; I've folded them into v11. This is rebased on a newer HEAD so it should fix the apply failures that Greg pointed out. --Jacob From 0025dcecab2d05c30e118105ea7d461ce98a8466 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 24 Nov 2021 14:46:11 -0800 Subject: [PATCH v11 1/3] Move inet_net_pton() to src/port This will be helpful for IP address verification in libpq. --- src/backend/utils/adt/Makefile | 1 - src/include/port.h | 3 +++ src/include/utils/builtins.h| 4 src/port/Makefile | 1 + src/{backend/utils/adt => port}/inet_net_pton.c | 16 +++- src/tools/msvc/Mkvcbuild.pm | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) rename src/{backend/utils/adt => port}/inet_net_pton.c (96%) diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 7c722ea2ce..ccaa46babf 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -44,7 +44,6 @@ OBJS = \ geo_spgist.o \ hbafuncs.o \ inet_cidr_ntop.o \ - inet_net_pton.o \ int.o \ int8.o \ json.o \ diff --git a/src/include/port.h b/src/include/port.h index 3d103a2b31..2852e5b58b 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -515,6 +515,9 @@ extern int pg_codepage_to_encoding(UINT cp); extern char *pg_inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size); +/* port/inet_net_pton.c */ +extern int pg_inet_net_pton(int af, const char *src, void *dst, size_t size); + /* port/pg_strong_random.c */ extern void pg_strong_random_init(void); extern bool pg_strong_random(void *buf, size_t len); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 666e545496..67b9326dc8 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -93,10 +93,6 @@ extern int xidLogicalComparator(const void *arg1, const void *arg2); extern char *pg_inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size); -/* inet_net_pton.c */ -extern int pg_inet_net_pton(int af, const char *src, - void *dst, size_t size); - /* network.c */ extern double convert_network_to_scalar(Datum value, Oid typid, bool *failure); extern Datum network_scan_first(Datum in); diff --git a/src/port/Makefile b/src/port/Makefile index bfe1feb0d4..c3ae7b3d5c 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -43,6 +43,7 @@ OBJS = \ bsearch_arg.o \ chklocale.o \ inet_net_ntop.o \ + inet_net_pton.o \ noblock.o \ path.o \ pg_bitutils.o \ diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/port/inet_net_pton.c similarity index 96% rename from src/backend/utils/adt/inet_net_pton.c rename to src/port/inet_net_pton.c index d3221a1313..bae50ba67e 100644 --- a/src/backend/utils/adt/inet_net_pton.c +++ b/src/port/inet_net_pton.c @@ -14,14 +14,18 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * src/backend/utils/adt/inet_net_pton.c + * src/port/inet_net_pton.c */ #if defined(LIBC_SCCS) && !defined(lint) static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $"; #endif +#ifndef FRONTEND #include "postgres.h" +#else +#include "postgres_fe.h" +#endif #include #include @@ -29,9 +33,19 @@ static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 m #include #include +#ifndef FRONTEND #include "utils/builtins.h" /* pgrminclude ignore */ /* needed on some * platforms */ #include "utils/inet.h" +#else +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that pg_inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) +#endif static int inet_net_pton_ipv4(const char *src, u_char *dst); diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index de8676d339..8491e31adb 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -100,7 +100,7 @@ sub mkvcbuild our @pgportfiles = qw( chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c - getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c + getaddrinfo.c gettimeofday.c inet_net_ntop.c inet_net_pton.
Re: [PATCH] Accept IP addresses in server certificate SANs
On 28.03.22 22:21, Jacob Champion wrote: On Mon, 2022-03-28 at 11:17 +0200, Daniel Gustafsson wrote: Fixing up the switch_server_cert() calls and using default_ssl_connstr makes the test pass for me. The required fixes are in the supplied 0004 diff, I kept them separate to allow the original author to incorporate them without having to dig them out to see what changed (named to match the git format-patch output since I think the CFBot just applies the patches in alphabetical order). Thanks! Those changes look good to me; I've folded them into v11. This is rebased on a newer HEAD so it should fix the apply failures that Greg pointed out. I'm not happy about how inet_net_pton.o is repurposed here. That code is clearly meant to support backend data types with specifically. Code like + /* + * pg_inet_net_pton() will accept CIDR masks, which we don't want to + * match, so skip the comparison if the host string contains a slash. + */ indicates that we are fighting against the API. Also, if someone ever wants to change how those backend data types work, we then have to check a bunch of other code as well. I think we should be using inet_ntop()/inet_pton() directly here. We can throw substitute implementations into libpgport if necessary, that's not so difficult.
Re: [PATCH] Accept IP addresses in server certificate SANs
On Wed, 2022-03-30 at 13:37 +0200, Peter Eisentraut wrote: > On 28.03.22 22:21, Jacob Champion wrote: > > On Mon, 2022-03-28 at 11:17 +0200, Daniel Gustafsson wrote: > > > Fixing up the switch_server_cert() calls and using default_ssl_connstr > > > makes > > > the test pass for me. The required fixes are in the supplied 0004 diff, > > > I kept > > > them separate to allow the original author to incorporate them without > > > having > > > to dig them out to see what changed (named to match the git format-patch > > > output > > > since I think the CFBot just applies the patches in alphabetical order). > > > > Thanks! Those changes look good to me; I've folded them into v11. This > > is rebased on a newer HEAD so it should fix the apply failures that > > Greg pointed out. > > I'm not happy about how inet_net_pton.o is repurposed here. That code > is clearly meant to support backend data types with specifically. Code like > > + /* > + * pg_inet_net_pton() will accept CIDR masks, which we don't want to > + * match, so skip the comparison if the host string contains a slash. > + */ > > indicates that we are fighting against the API. Horiguchi-san had the same concern upthread, I think. I replaced that code in the next patch, but it was enough net-new stuff that I kept the patches separate instead of merging them. I can change that if it's not helpful for review. > Also, if someone ever > wants to change how those backend data types work, we then have to check > a bunch of other code as well. > > I think we should be using inet_ntop()/inet_pton() directly here. We > can throw substitute implementations into libpgport if necessary, that's > not so difficult. Is this request satisfied by the implementation of pg_inet_pton() in patch 0003? If not, what needs to change? Thanks, --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
On 30.03.22 18:17, Jacob Champion wrote: Also, if someone ever wants to change how those backend data types work, we then have to check a bunch of other code as well. I think we should be using inet_ntop()/inet_pton() directly here. We can throw substitute implementations into libpgport if necessary, that's not so difficult. Is this request satisfied by the implementation of pg_inet_pton() in patch 0003? If not, what needs to change? Why add a (failry complicated) pg_inet_pton() when a perfectly reasonable inet_pton() exists? I would get rid of all that refactoring and just have your code call inet_pton()/inet_ntop() directly. If you're worried about portability, and you don't want to go through the effort of proving libpgport substitutes, just have your code raise an error in the "#else" code paths. We can fill that in later if there is demand.
Re: [PATCH] Accept IP addresses in server certificate SANs
On Thu, 2022-03-31 at 16:32 +0200, Peter Eisentraut wrote: > Why add a (failry complicated) pg_inet_pton() when a perfectly > reasonable inet_pton() exists? I think it was mostly just that inet_aton() and pg_inet_net_ntop() both had ports, and I figured I might as well port the other one since we already had the implementation. (I don't have a good intuition yet for the community's preference for port vs dependency.) > I would get rid of all that refactoring and just have your code call > inet_pton()/inet_ntop() directly. > > If you're worried about portability, and you don't want to go through > the effort of proving libpgport substitutes, just have your code raise > an error in the "#else" code paths. We can fill that in later if there > is demand. Switched to inet_pton() in v12, with no #if/else for now. I think this should work with Winsock as-is; let's see if the bot agrees... Thanks, --Jacob From a973663f1b4b98a427692bb6291ddc2c3ce9bb4f Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Thu, 18 Nov 2021 15:36:18 -0800 Subject: [PATCH v12] libpq: allow IP address SANs in server certs The current implementation supports exactly one IP address in a server certificate's Common Name, which is brittle (the strings must match exactly). This patch adds support for IPv4 and IPv6 addresses in a server's Subject Alternative Names. Per discussion on-list: - If the client's expected host is an IP address, we allow fallback to the Subject Common Name if an iPAddress SAN is not present, even if a dNSName is present. This matches the behavior of NSS, in violation of the relevant RFCs. - We also, counter-intuitively, match IP addresses embedded in dNSName SANs. From inspection this appears to have been the behavior since the SAN matching feature was introduced in acd08d76. - Unlike NSS, we don't map IPv4 to IPv6 addresses, or vice-versa. - Move PGSQL_AF_INET* to inet-common.h to reduce copy-paste. Co-authored-by: Kyotaro Horiguchi Co-authored-by: Daniel Gustafsson --- doc/src/sgml/libpq.sgml | 21 ++- src/backend/utils/adt/inet_net_pton.c | 2 +- src/include/common/inet-common.h | 35 + src/include/utils/inet.h | 13 +- src/interfaces/libpq/fe-secure-common.c | 99 + src/interfaces/libpq/fe-secure-common.h | 4 + src/interfaces/libpq/fe-secure-openssl.c | 139 -- src/port/inet_net_ntop.c | 12 +- .../conf/server-cn-and-ip-alt-names.config| 24 +++ src/test/ssl/conf/server-ip-alt-names.config | 19 +++ .../conf/server-ip-cn-and-alt-names.config| 21 +++ .../server-ip-cn-and-dns-alt-names.config | 21 +++ src/test/ssl/conf/server-ip-cn-only.config| 12 ++ src/test/ssl/conf/server-ip-in-dnsname.config | 18 +++ .../ssl/ssl/server-cn-and-ip-alt-names.crt| 20 +++ .../ssl/ssl/server-cn-and-ip-alt-names.key| 27 src/test/ssl/ssl/server-ip-alt-names.crt | 19 +++ src/test/ssl/ssl/server-ip-alt-names.key | 27 .../ssl/ssl/server-ip-cn-and-alt-names.crt| 19 +++ .../ssl/ssl/server-ip-cn-and-alt-names.key| 27 .../ssl/server-ip-cn-and-dns-alt-names.crt| 20 +++ .../ssl/server-ip-cn-and-dns-alt-names.key| 27 src/test/ssl/ssl/server-ip-cn-only.crt| 18 +++ src/test/ssl/ssl/server-ip-cn-only.key| 27 src/test/ssl/ssl/server-ip-in-dnsname.crt | 18 +++ src/test/ssl/ssl/server-ip-in-dnsname.key | 27 src/test/ssl/sslfiles.mk | 6 + src/test/ssl/t/001_ssltests.pl| 110 +- 28 files changed, 791 insertions(+), 41 deletions(-) create mode 100644 src/include/common/inet-common.h create mode 100644 src/test/ssl/conf/server-cn-and-ip-alt-names.config create mode 100644 src/test/ssl/conf/server-ip-alt-names.config create mode 100644 src/test/ssl/conf/server-ip-cn-and-alt-names.config create mode 100644 src/test/ssl/conf/server-ip-cn-and-dns-alt-names.config create mode 100644 src/test/ssl/conf/server-ip-cn-only.config create mode 100644 src/test/ssl/conf/server-ip-in-dnsname.config create mode 100644 src/test/ssl/ssl/server-cn-and-ip-alt-names.crt create mode 100644 src/test/ssl/ssl/server-cn-and-ip-alt-names.key create mode 100644 src/test/ssl/ssl/server-ip-alt-names.crt create mode 100644 src/test/ssl/ssl/server-ip-alt-names.key create mode 100644 src/test/ssl/ssl/server-ip-cn-and-alt-names.crt create mode 100644 src/test/ssl/ssl/server-ip-cn-and-alt-names.key create mode 100644 src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.crt create mode 100644 src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.key create mode 100644 src/test/ssl/ssl/server-ip-cn-only.crt create mode 100644 src/test/ssl/ssl/server-ip-cn-only.key create mode 100644 src/test/ssl/ssl/server-ip-in-dnsname.crt create mode 100644 src/test/ssl/ssl/server-ip-in-dnsname.key diff --git a/doc/src/sgml/libpq.sgml b/doc/sr
Re: [PATCH] Accept IP addresses in server certificate SANs
On 31.03.22 20:15, Jacob Champion wrote: On Thu, 2022-03-31 at 16:32 +0200, Peter Eisentraut wrote: Why add a (failry complicated) pg_inet_pton() when a perfectly reasonable inet_pton() exists? I think it was mostly just that inet_aton() and pg_inet_net_ntop() both had ports, and I figured I might as well port the other one since we already had the implementation. (I don't have a good intuition yet for the community's preference for port vs dependency.) I would get rid of all that refactoring and just have your code call inet_pton()/inet_ntop() directly. If you're worried about portability, and you don't want to go through the effort of proving libpgport substitutes, just have your code raise an error in the "#else" code paths. We can fill that in later if there is demand. Switched to inet_pton() in v12, with no #if/else for now. I think this should work with Winsock as-is; let's see if the bot agrees... I have committed this. I have removed the inet header refactoring that you had. That wasn't necessary, since pg_inet_net_ntop() can use the normal AF_INET* constants. The PGSQL_AF_INET* constants are only for the internal storage of the inet/cidr types. I have added a configure test for inet_pton(). We can check in the build farm if it turns out to be necessary.
Re: [PATCH] Accept IP addresses in server certificate SANs
On Fri, 2022-04-01 at 16:07 +0200, Peter Eisentraut wrote: > I have committed this. > > I have removed the inet header refactoring that you had. That wasn't > necessary, since pg_inet_net_ntop() can use the normal AF_INET* > constants. The PGSQL_AF_INET* constants are only for the internal > storage of the inet/cidr types. > > I have added a configure test for inet_pton(). We can check in the > build farm if it turns out to be necessary. Thanks! --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
On Fri, 2021-12-17 at 16:54 +0900, Kyotaro Horiguchi wrote: > Sorry for the silly mistake. > > At Fri, 17 Dec 2021 15:40:10 +0900 (JST), Kyotaro Horiguchi > wrote in > > > NSS departs slightly from the spec and will additionally try to match > > > an IP address against the CN, but only if there are no iPAddresses in > > > the SAN. It roughly matches the logic for DNS names. > > > > OpenSSL seems different. X509_check_host() tries SAN then CN iff SAN > > doesn't exist. X509_check_ip() tries SAN and completely ignores > > iPAdress and CN. > > OpenSSL seems different. X509_check_host() tries SAN then CN iff SAN > doesn't exist. X509_check_ip() tries iPAddress and completely ignores > CN. Right. On Fri, 2021-12-17 at 15:40 +0900, Kyotaro Horiguchi wrote: > > > +* pg_inet_net_pton() will accept CIDR masks, which we > > don't want to > > > +* match, so skip the comparison if the host string > > > contains a slash. > > > +*/ > > > + if (!strchr(host, '/') > > > + && pg_inet_net_pton(PGSQL_AF_INET6, host, addr, > > > -1) == 128) > > > > > > If a cidr is given, pg_inet_net_pton returns a number less than 128 so > > > we don't need to check '/' explicity? (I'm not sure '/128' is > > > sensible but doesn't harm..) > > > > Personally I think that, if someone wants your libpq to connect to a > > server with a hostname of "some:ipv6::address/128", then they are > > trying to pull something (evading a poorly coded blocklist, perhaps?) > > and we should not allow that to match an IP. Thoughts? > > If the client could connect to the network-address, it could be said > that we can assume that address is the name:p Just kidding. > > As the name suggests, the function reads a network address. And the > only user is network_in(). I think we should provide pg_inet_pton() > instead of abusing pg_inet_net_pton(). inet_net_pton_*() functions > can be modified to reject /cidr part without regression so we are able > to have pg_inet_pton() with a small amount of change. > > - inet_net_pton_ipv4(const char *src, u_char *dst) > + inet_net_pton_ipv4_internal(const char *src, u_char *dst, bool netaddr) > > + inet_net_pton_ipv4(const char *src, u_char *dst) > (calls inet_net_pton_ipv4_internal(src, dst, true)) > + inet_pton_ipv4(const char *src, u_char *dst) > (calls inet_net_pton_ipv4_internal(src, dst, false)) Sounds good, I will make that change. Thanks for the feedback! --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
On Sun, 2022-01-02 at 13:29 -0800, Andres Freund wrote: > Hi, > > On 2021-12-16 01:13:57 +, Jacob Champion wrote: > > Attached is a patch for libpq to support IP addresses in the server's > > Subject Alternative Names, which would allow admins to issue certs for > > multiple IP addresses, both IPv4 and IPv6, and mix them with > > alternative DNS hostnames. These addresses are compared bytewise > > instead of stringwise, so the client can contact the server via > > alternative spellings of the same IP address. > > This fails to build on windows: > https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fcirrus-ci.com%2Ftask%2F6734650927218688%3Flogs%3Dbuild%23L1029&data=04%7C01%7Cpchampion%40vmware.com%7C2b2171168f3c4935e89f08d9ce36f790%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637767557770534489%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=JtfsPtershSljU1oDGrkL8bQiHYB3iMfUgTqlh%2B4wbs%3D&reserved=0 > > [14:33:28.277] network.obj : error LNK2019: unresolved external symbol > pg_inet_net_pton referenced in function network_in > [c:\cirrus\postgres.vcxproj] Thanks for the heads up; I'll fix that while I'm implementing the internal API. --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
On Thu, 2021-12-16 at 18:44 +, Jacob Champion wrote: > It sounds like both you and Andrew might be comfortable with that same > behavior? I think it looks like a sane solution, so I'll implement that > and we can see what it looks like. (My work on this will be paused over > the end-of-year holidays.) v2 implements the discussed CN/SAN fallback behavior and should fix the build on Windows. Still TODO is the internal pg_inet_pton() refactoring that you asked for; I'm still deciding how best to approach it. Changes only in since-v1.diff.txt. Thanks, --Jacob diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c index 38ee65abf2..796b78e348 100644 --- a/src/interfaces/libpq/fe-secure-common.c +++ b/src/interfaces/libpq/fe-secure-common.c @@ -28,14 +28,6 @@ #include "port.h" #include "pqexpbuffer.h" -/* - * In a frontend build, we can't include inet.h, but we still need to have - * sensible definitions of these two constants. Note that pg_inet_net_ntop() - * assumes that PGSQL_AF_INET is equal to AF_INET. - */ -#define PGSQL_AF_INET (AF_INET + 0) -#define PGSQL_AF_INET6 (AF_INET + 1) - /* * Check if a wildcard certificate matches the server hostname. * diff --git a/src/interfaces/libpq/fe-secure-common.h b/src/interfaces/libpq/fe-secure-common.h index a090a92f60..29d4f54230 100644 --- a/src/interfaces/libpq/fe-secure-common.h +++ b/src/interfaces/libpq/fe-secure-common.h @@ -18,6 +18,14 @@ #include "libpq-fe.h" +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that pg_inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) + extern int pq_verify_peer_name_matches_certificate_name(PGconn *conn, const char *namedata, size_t namelen, char **store_name); diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index 16c5ff9223..479f63197a 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -548,6 +548,16 @@ openssl_verify_peer_name_matches_certificate_ip(PGconn *conn, return pq_verify_peer_name_matches_certificate_ip(conn, (const char *) addrdata, len, store_name); } +static bool +is_ip_address(const char *host) +{ + struct in_addr dummy4; + unsigned char dummy6[16]; + + return inet_aton(host, &dummy4) + || (pg_inet_net_pton(PGSQL_AF_INET6, host, dummy6, -1) == 128); +} + /* * Verify that the server certificate matches the hostname we connected to. * @@ -561,7 +571,36 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, STACK_OF(GENERAL_NAME) * peer_san; int i; int rc = 0; - boolhas_dnsname = false; + char *host = conn->connhost[conn->whichhost].host; + int host_type; + boolcheck_cn = true; + + Assert(host && host[0]); /* should be guaranteed by caller */ + + /* +* We try to match the NSS behavior here, which is a slight departure from +* the spec but seems to make more intuitive sense: +* +* If connhost contains a DNS name, and the certificate's SANs contain any +* dNSName entries, then we'll ignore the Subject Common Name entirely; +* otherwise, we fall back to checking the CN. (This behavior matches the +* RFC.) +* +* If connhost contains an IP address, and the SANs contain iPAddress +* entries, we again ignore the CN. Otherwise, we allow the CN to match, +* EVEN IF there is a dNSName in the SANs. (RFC 6125 prohibits this: "A +* client MUST NOT seek a match for a reference identifier of CN-ID if the +* presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any +* application-specific identifier types supported by the client.") +* +* NOTE: Prior versions of libpq did not consider iPAddress entries at all, +* so this new behavior might break a certificate that has different IP +* addresses in the Subject CN and the SANs. +*/ + if (is_ip_address(host)) + host_type = GEN_IPADD; + else + host_type = GEN_DNS; /* * First, get the Subject Alternative Names (SANs) from the certificate, @@ -579,9 +618,11 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, const GENERAL_NAME *name = sk_GENERAL_NAME_value(peer_san, i); char *alt_name = NULL; +
Re: [PATCH] Accept IP addresses in server certificate SANs
On Mon, 2022-01-03 at 16:19 +, Jacob Champion wrote: > On Fri, 2021-12-17 at 15:40 +0900, Kyotaro Horiguchi wrote: > > > > + inet_net_pton_ipv4(const char *src, u_char *dst) > > (calls inet_net_pton_ipv4_internal(src, dst, true)) > > + inet_pton_ipv4(const char *src, u_char *dst) > > (calls inet_net_pton_ipv4_internal(src, dst, false)) > > Sounds good, I will make that change. Thanks for the feedback! v3 implements a pg_inet_pton(), but for IPv6 instead of IPv4 as presented above (since we only need inet_pton() for IPv6 in this case). It's split into a separate patch (0003) for ease of review. Thanks! --Jacob From 3081b1e0f7af60241978c77bba71e806ba5c25f6 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 24 Nov 2021 14:46:11 -0800 Subject: [PATCH v3 1/3] Move inet_net_pton() to src/port This will be helpful for IP address verification in libpq. --- src/backend/utils/adt/Makefile | 1 - src/include/port.h | 3 +++ src/include/utils/builtins.h| 4 src/port/Makefile | 1 + src/{backend/utils/adt => port}/inet_net_pton.c | 16 +++- src/tools/msvc/Mkvcbuild.pm | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) rename src/{backend/utils/adt => port}/inet_net_pton.c (96%) diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 41b486bcef..d173d52157 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -43,7 +43,6 @@ OBJS = \ geo_selfuncs.o \ geo_spgist.o \ inet_cidr_ntop.o \ - inet_net_pton.o \ int.o \ int8.o \ json.o \ diff --git a/src/include/port.h b/src/include/port.h index fd9c9d6f94..cca29839bc 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -518,6 +518,9 @@ extern int pg_codepage_to_encoding(UINT cp); extern char *pg_inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size); +/* port/inet_net_pton.c */ +extern int pg_inet_net_pton(int af, const char *src, void *dst, size_t size); + /* port/pg_strong_random.c */ extern void pg_strong_random_init(void); extern bool pg_strong_random(void *buf, size_t len); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index b07eefaf1e..a5a18e62ba 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -92,10 +92,6 @@ extern int xidComparator(const void *arg1, const void *arg2); extern char *pg_inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size); -/* inet_net_pton.c */ -extern int pg_inet_net_pton(int af, const char *src, - void *dst, size_t size); - /* network.c */ extern double convert_network_to_scalar(Datum value, Oid typid, bool *failure); extern Datum network_scan_first(Datum in); diff --git a/src/port/Makefile b/src/port/Makefile index b3754d8940..7191cd6633 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -43,6 +43,7 @@ OBJS = \ bsearch_arg.o \ chklocale.o \ inet_net_ntop.o \ + inet_net_pton.o \ noblock.o \ path.o \ pg_bitutils.o \ diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/port/inet_net_pton.c similarity index 96% rename from src/backend/utils/adt/inet_net_pton.c rename to src/port/inet_net_pton.c index d3221a1313..bae50ba67e 100644 --- a/src/backend/utils/adt/inet_net_pton.c +++ b/src/port/inet_net_pton.c @@ -14,14 +14,18 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * src/backend/utils/adt/inet_net_pton.c + * src/port/inet_net_pton.c */ #if defined(LIBC_SCCS) && !defined(lint) static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $"; #endif +#ifndef FRONTEND #include "postgres.h" +#else +#include "postgres_fe.h" +#endif #include #include @@ -29,9 +33,19 @@ static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 m #include #include +#ifndef FRONTEND #include "utils/builtins.h" /* pgrminclude ignore */ /* needed on some * platforms */ #include "utils/inet.h" +#else +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that pg_inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) +#endif static int inet_net_pton_ipv4(const char *src, u_char *dst); diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 404c45a6f3..fa87511e04 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -100,7 +100,7 @@ sub mkvcbuild our @pgportfiles = qw( chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c - getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c + getaddrinfo.c gettimeofday.c inet_net_ntop.c inet_net_pton.c kill.c
Re: [PATCH] Accept IP addresses in server certificate SANs
At Thu, 6 Jan 2022 00:02:27 +, Jacob Champion wrote in > On Mon, 2022-01-03 at 16:19 +, Jacob Champion wrote: > > On Fri, 2021-12-17 at 15:40 +0900, Kyotaro Horiguchi wrote: > > > > > > + inet_net_pton_ipv4(const char *src, u_char *dst) > > > (calls inet_net_pton_ipv4_internal(src, dst, true)) > > > + inet_pton_ipv4(const char *src, u_char *dst) > > > (calls inet_net_pton_ipv4_internal(src, dst, false)) > > > > Sounds good, I will make that change. Thanks for the feedback! > > v3 implements a pg_inet_pton(), but for IPv6 instead of IPv4 as > presented above (since we only need inet_pton() for IPv6 in this case). > It's split into a separate patch (0003) for ease of review. 0001 looks fine as it is in the almost same shape withinet_net_pton about PGSQL_AF_INET and PGSQL_AF_INET6. I'm not sure about the difference on how to handle AF_INET6 between pg_inet_net_pton and ntop but that's not a matter of this patch. However, 0002, +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that pg_inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) + Now we have the same definition thrice in frontend code. Coulnd't we define them in, say, libpq-fe.h or inet-fe.h (nonexistent) then include it from the three files? +$node->connect_fails( + "$common_connstr host=192.0.2.2", + "host not matching an IPv4 address (Subject Alternative Name 1)", It is not the real IP address of the server. https://datatracker.ietf.org/doc/html/rfc6125 > In some cases, the URI is specified as an IP address rather than a > hostname. In this case, the iPAddress subjectAltName must be > present in the certificate and must exactly match the IP in the URI. When IP address is embedded in URI, it won't be translated to another IP address. Concretely https://192.0.1.5/hoge cannot reach to the host 192.0.1.8. On the other hand, as done in the test, libpq allows that when "host=192.0.1.5 hostaddr=192.0.1.8". I can't understand what we are doing in that case. Don't we need to match the SAN IP address with hostaddr instead of host? regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On Mon, 2022-01-31 at 17:29 +0900, Kyotaro Horiguchi wrote: > However, 0002, > > +/* > + * In a frontend build, we can't include inet.h, but we still need to have > + * sensible definitions of these two constants. Note that pg_inet_net_ntop() > + * assumes that PGSQL_AF_INET is equal to AF_INET. > + */ > +#define PGSQL_AF_INET (AF_INET + 0) > +#define PGSQL_AF_INET6 (AF_INET + 1) > + > > Now we have the same definition thrice in frontend code. Coulnd't we > define them in, say, libpq-fe.h or inet-fe.h (nonexistent) then > include it from the three files? I started down the inet-fe.h route, and then realized I didn't know where that should go. Does it need to be included in (or part of) port.h? And should it be installed as part of the logic in src/include/Makefile? > +$node->connect_fails( > + "$common_connstr host=192.0.2.2", > + "host not matching an IPv4 address (Subject Alternative Name 1)", > > It is not the real IP address of the server. > > https://datatracker.ietf.org/doc/html/rfc6125 > > In some cases, the URI is specified as an IP address rather than a > > hostname. In this case, the iPAddress subjectAltName must be > > present in the certificate and must exactly match the IP in the URI. > > When IP address is embedded in URI, it won't be translated to another > IP address. Concretely https://192.0.1.5/hoge cannot reach to the host > 192.0.1.8. On the other hand, as done in the test, libpq allows that > when "host=192.0.1.5 hostaddr=192.0.1.8". I can't understand what we > are doing in that case. Don't we need to match the SAN IP address > with hostaddr instead of host? I thought that host, not hostaddr, was the part that corresponded to the URI. So in a hypothetical future where postgresqls:// exists, the two URIs postgresqls://192.0.2.2:5432/db postgresqls://192.0.2.2:5432/db?hostaddr=127.0.0.1 should both be expecting the same certificate. That seems to match the libpq documentation as well. (Specifying a host parameter is also allowed... that seems like it could cause problems for a hypothetical postgresqls:// scheme, but it's probably not relevant for this thread.) --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
At Wed, 2 Feb 2022 19:46:13 +, Jacob Champion wrote in > On Mon, 2022-01-31 at 17:29 +0900, Kyotaro Horiguchi wrote: > > +#define PGSQL_AF_INET (AF_INET + 0) > > +#define PGSQL_AF_INET6 (AF_INET + 1) > > + > > > > Now we have the same definition thrice in frontend code. Coulnd't we > > define them in, say, libpq-fe.h or inet-fe.h (nonexistent) then > > include it from the three files? > > I started down the inet-fe.h route, and then realized I didn't know > where that should go. Does it need to be included in (or part of) > port.h? And should it be installed as part of the logic in > src/include/Makefile? I don't think it should be a part of port.h. Though I suggested frontend-only header file by the name, isn't it enough to separate out the definitions from utils/inet.h to common/inet-common.h then include the inet-common.h from inet.h? > > When IP address is embedded in URI, it won't be translated to another > > IP address. Concretely https://192.0.1.5/hoge cannot reach to the host > > 192.0.1.8. On the other hand, as done in the test, libpq allows that > > when "host=192.0.1.5 hostaddr=192.0.1.8". I can't understand what we > > are doing in that case. Don't we need to match the SAN IP address > > with hostaddr instead of host? > > I thought that host, not hostaddr, was the part that corresponded to > the URI. So in a hypothetical future where postgresqls:// exists, the > two URIs > > postgresqls://192.0.2.2:5432/db > postgresqls://192.0.2.2:5432/db?hostaddr=127.0.0.1 > > should both be expecting the same certificate. That seems to match the > libpq documentation as well. Hmm. Well, considering that the objective for the validation is to check if the server is actually the client is intending to connect, it is fine. Sorry for the noise. > (Specifying a host parameter is also allowed... that seems like it > could cause problems for a hypothetical postgresqls:// scheme, but it's > probably not relevant for this thread.) Yeah. regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On Thu, 2022-02-03 at 16:23 +0900, Kyotaro Horiguchi wrote: > At Wed, 2 Feb 2022 19:46:13 +, Jacob Champion > wrote in > > On Mon, 2022-01-31 at 17:29 +0900, Kyotaro Horiguchi wrote: > > > +#define PGSQL_AF_INET (AF_INET + 0) > > > +#define PGSQL_AF_INET6 (AF_INET + 1) > > > + > > > > > > Now we have the same definition thrice in frontend code. Coulnd't we > > > define them in, say, libpq-fe.h or inet-fe.h (nonexistent) then > > > include it from the three files? > > > > I started down the inet-fe.h route, and then realized I didn't know > > where that should go. Does it need to be included in (or part of) > > port.h? And should it be installed as part of the logic in > > src/include/Makefile? > > I don't think it should be a part of port.h. Though I suggested > frontend-only header file by the name, isn't it enough to separate out > the definitions from utils/inet.h to common/inet-common.h then include > the inet-common.h from inet.h? That works a lot better than what I had in my head. Done that way in v4. Thanks! --Jacob From e54b41c722d04ea6eabe4434bdf10c92fa79a192 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 24 Nov 2021 14:46:11 -0800 Subject: [PATCH v4 1/3] Move inet_net_pton() to src/port This will be helpful for IP address verification in libpq. --- src/backend/utils/adt/Makefile | 1 - src/include/port.h | 3 +++ src/include/utils/builtins.h| 4 src/port/Makefile | 1 + src/{backend/utils/adt => port}/inet_net_pton.c | 16 +++- src/tools/msvc/Mkvcbuild.pm | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) rename src/{backend/utils/adt => port}/inet_net_pton.c (96%) diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 41b486bcef..d173d52157 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -43,7 +43,6 @@ OBJS = \ geo_selfuncs.o \ geo_spgist.o \ inet_cidr_ntop.o \ - inet_net_pton.o \ int.o \ int8.o \ json.o \ diff --git a/src/include/port.h b/src/include/port.h index 3d103a2b31..2852e5b58b 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -515,6 +515,9 @@ extern int pg_codepage_to_encoding(UINT cp); extern char *pg_inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size); +/* port/inet_net_pton.c */ +extern int pg_inet_net_pton(int af, const char *src, void *dst, size_t size); + /* port/pg_strong_random.c */ extern void pg_strong_random_init(void); extern bool pg_strong_random(void *buf, size_t len); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index d8f05a7df3..22ebbfda17 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -93,10 +93,6 @@ extern int xidLogicalComparator(const void *arg1, const void *arg2); extern char *pg_inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size); -/* inet_net_pton.c */ -extern int pg_inet_net_pton(int af, const char *src, - void *dst, size_t size); - /* network.c */ extern double convert_network_to_scalar(Datum value, Oid typid, bool *failure); extern Datum network_scan_first(Datum in); diff --git a/src/port/Makefile b/src/port/Makefile index bfe1feb0d4..c3ae7b3d5c 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -43,6 +43,7 @@ OBJS = \ bsearch_arg.o \ chklocale.o \ inet_net_ntop.o \ + inet_net_pton.o \ noblock.o \ path.o \ pg_bitutils.o \ diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/port/inet_net_pton.c similarity index 96% rename from src/backend/utils/adt/inet_net_pton.c rename to src/port/inet_net_pton.c index d3221a1313..bae50ba67e 100644 --- a/src/backend/utils/adt/inet_net_pton.c +++ b/src/port/inet_net_pton.c @@ -14,14 +14,18 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * src/backend/utils/adt/inet_net_pton.c + * src/port/inet_net_pton.c */ #if defined(LIBC_SCCS) && !defined(lint) static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $"; #endif +#ifndef FRONTEND #include "postgres.h" +#else +#include "postgres_fe.h" +#endif #include #include @@ -29,9 +33,19 @@ static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 m #include #include +#ifndef FRONTEND #include "utils/builtins.h" /* pgrminclude ignore */ /* needed on some * platforms */ #include "utils/inet.h" +#else +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that pg_inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) +#endif static int inet_net_pton_ipv4(const char *src, u_char *dst); diff --git a/
Re: [PATCH] Accept IP addresses in server certificate SANs
At Fri, 4 Feb 2022 17:06:53 +, Jacob Champion wrote in > That works a lot better than what I had in my head. Done that way in > v4. Thanks! Thanks! 0002: +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) .. -#define PGSQL_AF_INET (AF_INET + 0) -#define PGSQL_AF_INET6 (AF_INET + 1) I feel this should be a part of 0001. (But the patches will be finally merged so maybe no need to bother moving it). > * The use of inet_aton() instead of inet_pton() is deliberate; the > * latter cannot handle alternate IPv4 notations ("numbers-and-dots"). I think we should be consistent in handling IP addresses. We have both inet_pton and inet_aton to parse IPv4 addresses. We use inet_pton in the inet type (network_in). We use inet_aton in server addresses. # Hmm. I'm surprised to see listen_addresses accepts "0x7f.1". # I think we should accept the same by network_in but it is another # issue. So, inet_aton there seems to be the right choice but the comment doesn't describe the reason for that behavior. I think we should add an explanation about the reason for the behavior, maybe something like this: > We accept alternative IPv4 address notations that are accepted by > inet_aton but not by inet_pton as server address. +* GEN_IPADD is an OCTET STRING containing an IP address in network byte +* order. + /* OK to cast from unsigned to plain char, since it's all ASCII. */ + return pq_verify_peer_name_matches_certificate_ip(conn, (const char *) addrdata, len, store_name); Aren't the two comments contradicting each other? The retruned general name looks like an octet array, which is not a subset of ASCII string. So pq_verify_peer_name_matches_certificate_ip should receive addrdata as "const unsigned char *", without casting. + if (name->type == host_type) + check_cn = false; Don't we want a concise coment for this? - if (*names_examined == 0) + if ((rc == 0) && check_cn) To me, it seems a bit hard to understand. We can set false to check_cn in the rc != 0 path in the loop on i, like this: > if (rc != 0) > + { > + /* > + * don't fall back to CN when we have a match > or have an error > + */ > + check_cn = false; > break; > + } ... > - if ((rc == 0) && check_cn) > + if (check_cn) The following existing code (CN fallback) > rc = openssl_verify_peer_name_matches_certificate_name(conn, > X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, cn_index)), > first_name); is expecting that first_name has not been set when it is visited. However, with this patch, first_name can be set when the cert has any SAN of unmatching type (DNS/IPADD) and the already-set name leaks. We need to avoid that memory leak since the path can be visited multiple times from the user-program of libpq. I came up with two directions. 1. Completely ignore type-unmatching entries. first_name is not set by such entries. Such unmatching entreis doesn't increase *names_examined. 2. Avoid overwriting first_name there. I like 1, but since we don't make distinction between DNS and IPADDR in the error message emited by the caller, we would take 2? regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On Mon, 2022-02-07 at 17:29 +0900, Kyotaro Horiguchi wrote: > At Fri, 4 Feb 2022 17:06:53 +, Jacob Champion > wrote in > > That works a lot better than what I had in my head. Done that way in > > v4. Thanks! > > Thanks! > > 0002: > > +#define PGSQL_AF_INET (AF_INET + 0) > +#define PGSQL_AF_INET6 (AF_INET + 1) > .. > -#define PGSQL_AF_INET (AF_INET + 0) > -#define PGSQL_AF_INET6 (AF_INET + 1) > > I feel this should be a part of 0001. (But the patches will be > finally merged so maybe no need to bother moving it). Okay. I can move it easily if you feel like it would help review, but for now I've kept it in 0002. > > * The use of inet_aton() instead of inet_pton() is deliberate; the > > * latter cannot handle alternate IPv4 notations ("numbers-and-dots"). > > I think we should be consistent in handling IP addresses. We have > both inet_pton and inet_aton to parse IPv4 addresses. > > We use inet_pton in the inet type (network_in). > We use inet_aton in server addresses. > > # Hmm. I'm surprised to see listen_addresses accepts "0x7f.1". > # I think we should accept the same by network_in but it is another > # issue. Yeah, that's an interesting inconsistency. > So, inet_aton there seems to be the right choice but the comment > doesn't describe the reason for that behavior. I think we should add > an explanation about the reason for the behavior, maybe something like > this: > > > We accept alternative IPv4 address notations that are accepted by > > inet_aton but not by inet_pton as server address. I've pulled this wording into the comment in v5, attached. > +* GEN_IPADD is an OCTET STRING containing an IP address in network > byte > +* order. > > + /* OK to cast from unsigned to plain char, since it's all ASCII. */ > + return pq_verify_peer_name_matches_certificate_ip(conn, (const char > *) addrdata, len, store_name); > > Aren't the two comments contradicting each other? The retruned general > name looks like an octet array, which is not a subset of ASCII > string. So pq_verify_peer_name_matches_certificate_ip should receive > addrdata as "const unsigned char *", without casting. Bad copy-paste on my part; thanks for the catch. Fixed. > + if (name->type == host_type) > + check_cn = false; > > Don't we want a concise coment for this? Added one; see what you think. > - if (*names_examined == 0) > + if ((rc == 0) && check_cn) > > To me, it seems a bit hard to understand. We can set false to > check_cn in the rc != 0 path in the loop on i, like this: > > > if (rc != 0) > > + { > > + /* > > + * don't fall back to CN when we have a match > > or have an error > > + */ > > + check_cn = false; > > break; > > + } > ... > > - if ((rc == 0) && check_cn) > > + if (check_cn) If I understand right, that's not quite equivalent (and the new tests fail if I implement it that way). We have to disable fallback if the SAN exists, whether it matches or not. > The following existing code (CN fallback) > > > rc = openssl_verify_peer_name_matches_certificate_name(conn, > > X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, cn_index)), > > first_name); > > is expecting that first_name has not been set when it is visited. > However, with this patch, first_name can be set when the cert has any > SAN of unmatching type (DNS/IPADD) and the already-set name leaks. We > need to avoid that memory leak since the path can be visited multiple > times from the user-program of libpq. I came up with two directions. > > 1. Completely ignore type-unmatching entries. first_name is not set by > such entries. Such unmatching entreis doesn't increase > *names_examined. > > 2. Avoid overwriting first_name there. > > I like 1, but since we don't make distinction between DNS and IPADDR > in the error message emited by the caller, we would take 2? Great catch, thanks! I implemented option 2 to start. Option 1 might make things difficult to debug if you're connecting to a server by IP address but its certificate only has DNS names. Thanks! --Jacob commit 8c427e3289a28cb683eff5d05b2e8770c3c07662 Author: Jacob Champion Date: Tue Feb 8 16:26:27 2022 -0800 squash! libpq: allow IP address SANs in server certs diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c index cfdde58e67..4d78715756 100644 --- a/src/interfaces/libpq/fe-secure-common.c +++ b/src/interfaces/libpq/fe-secure-common.c @@ -160,7 +160,8 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn, */ int pq_verify_peer_name_matches_certificate_ip(PGconn *conn, - const char *ipdata, size_
Re: [PATCH] Accept IP addresses in server certificate SANs
(This needs rebasing) At Wed, 9 Feb 2022 00:52:48 +, Jacob Champion wrote in > On Mon, 2022-02-07 at 17:29 +0900, Kyotaro Horiguchi wrote: > > I feel this should be a part of 0001. (But the patches will be > > finally merged so maybe no need to bother moving it). > > Okay. I can move it easily if you feel like it would help review, but > for now I've kept it in 0002. Thanks. > > So, inet_aton there seems to be the right choice but the comment > > doesn't describe the reason for that behavior. I think we should add > > an explanation about the reason for the behavior, maybe something like > > this: > > > > > We accept alternative IPv4 address notations that are accepted by > > > inet_aton but not by inet_pton as server address. > > I've pulled this wording into the comment in v5, attached. > > + if (name->type == host_type) > > + check_cn = false; > > > > Don't we want a concise coment for this? > > Added one; see what you think. That's fine with me. > > > if (rc != 0) > > > + { > > > + /* > > > + * don't fall back to CN when we have a > > > match or have an error > > > + */ > > > + check_cn = false; > > > break; > > > + } > > ... > > > - if ((rc == 0) && check_cn) > > > + if (check_cn) > > If I understand right, that's not quite equivalent (and the new tests > fail if I implement it that way). We have to disable fallback if the > SAN exists, whether it matches or not. # I forgot to mention that, the test fails for me even without the # change. I didn't checked what is wrong there, though. Mmm. after the end of the loop, rc is non-zero only when the loop has exited by the break and otherwise rc is zero. Why isn't it equivalent to setting check_cn to false at the break? Anyway, apart from that detail, I reconfirmed the spec the patch is going to implement. * If connhost contains a DNS name, and the certificate's SANs contain any * dNSName entries, then we'll ignore the Subject Common Name entirely; * otherwise, we fall back to checking the CN. (This behavior matches the * RFC.) Sure. * If connhost contains an IP address, and the SANs contain iPAddress * entries, we again ignore the CN. Otherwise, we allow the CN to match, * EVEN IF there is a dNSName in the SANs. (RFC 6125 prohibits this: "A * client MUST NOT seek a match for a reference identifier of CN-ID if the * presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any * application-specific identifier types supported by the client.") Actually the patch searches for a match of IP address connhost from dNSName SANs even if iPAddress SANs exist. I think we've not explicitly defined thebehavior in that case. I supposed that we only be deviant in the case "IP address connhost and no SANs of any type exists". What do you think about it? - For the certificate that have only dNSNames or no SANs presented, we serach for a match from all dNSNames if any or otherwise try CN regardless of the type of connhost. - Otherwise (the cert has at least one iPAddress SANs) we follow the RFCs. - For IP-addr connhost, we search only the iPAddress SANs. - For DNSName connhost, we search only dNSName SANs if any or otherwise try CN. Honestly I didn't consider to that detail. On second thought, with this specification we cannot decide the behavior unless we scanned all SANs. Maybe we can find an elegant implement but I don't think people here would welcome even that level of complexity needed only for that dubious existing use case. What do you think about this? And I'd like to hear from others. > Great catch, thanks! I implemented option 2 to start. Option 1 might > make things difficult to debug if you're connecting to a server by IP > address but its certificate only has DNS names. Looks fine. Thanks! regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On Tue, 2022-02-15 at 15:16 +0900, Kyotaro Horiguchi wrote: > (This needs rebasing) Done in v6, attached. > # I forgot to mention that, the test fails for me even without the > # change. I didn't checked what is wrong there, though. Ah. We should probably figure that out, then -- what failures do you see? > Mmm. after the end of the loop, rc is non-zero only when the loop has > exited by the break and otherwise rc is zero. Why isn't it equivalent > to setting check_cn to false at the break? check_cn can be false if rc is zero, too; it means that we found a SAN of the correct type but it didn't match. > Anyway, apart from that detail, I reconfirmed the spec the patch is > going to implement. > > * If connhost contains a DNS name, and the certificate's SANs contain any > * dNSName entries, then we'll ignore the Subject Common Name entirely; > * otherwise, we fall back to checking the CN. (This behavior matches the > * RFC.) > > Sure. > > * If connhost contains an IP address, and the SANs contain iPAddress > * entries, we again ignore the CN. Otherwise, we allow the CN to match, > * EVEN IF there is a dNSName in the SANs. (RFC 6125 prohibits this: "A > * client MUST NOT seek a match for a reference identifier of CN-ID if the > * presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any > * application-specific identifier types supported by the client.") > > Actually the patch searches for a match of IP address connhost from > dNSName SANs even if iPAddress SANs exist. I think we've not > explicitly defined thebehavior in that case. That's a good point; I didn't change the prior behavior. I feel more comfortable leaving that check, since it is technically possible to push something that looks like an IP address into a dNSName SAN. We should probably make an explicit decision on that, as you say. But I don't think that contradicts the code comment, does it? The comment is just talking about CN fallback scenarios. If you find a match in a dNSName, there's no reason to fall back to the CN. > I supposed that we only > be deviant in the case "IP address connhost and no SANs of any type > exists". What do you think about it? We fall back in the case of "IP address connhost and dNSName SANs exist", which is prohibited by that part of RFC 6125. I don't think we deviate in the case you described; can you explain further? > - For the certificate that have only dNSNames or no SANs presented, we > serach for a match from all dNSNames if any or otherwise try CN > regardless of the type of connhost. Correct. (I don't find that way of dividing up the cases very intuitive, though.) > - Otherwise (the cert has at least one iPAddress SANs) we follow the RFCs. > > - For IP-addr connhost, we search only the iPAddress SANs. We search the dNSNames as well, as you pointed out above. But we don't fall back to the CN. > - For DNSName connhost, we search only dNSName SANs if any or > otherwise try CN. Effectively, yes. (We call the IP address verification functions too, to get alt_name, but they can't match. If that's too confusing, we'd need to pull the alt_name handling up out of the verification layer.) > Honestly I didn't consider to that detail. On second thought, with > this specification we cannot decide the behavior unless we scanned all > SANs. Right. > Maybe we can find an elegant implement but I don't think people > here would welcome even that level of complexity needed only for that > dubious existing use case. Which use case do you mean? > What do you think about this? And I'd like to hear from others. I think we need to decide whether or not to keep the current "IP address connhost can match a dNSName SAN" behavior, and if so I need to add it to the test cases. (And we need to figure out why the tests are failing in your build, of course.) Thanks! --Jacob From fa93bc48654164d36fc163ee84d8f9b78fe29e96 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 24 Nov 2021 14:46:11 -0800 Subject: [PATCH v6 1/3] Move inet_net_pton() to src/port This will be helpful for IP address verification in libpq. --- src/backend/utils/adt/Makefile | 1 - src/include/port.h | 3 +++ src/include/utils/builtins.h| 4 src/port/Makefile | 1 + src/{backend/utils/adt => port}/inet_net_pton.c | 16 +++- src/tools/msvc/Mkvcbuild.pm | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) rename src/{backend/utils/adt => port}/inet_net_pton.c (96%) diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 41b486bcef..d173d52157 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -43,7 +43,6 @@ OBJS = \ geo_selfuncs.o \ geo_spgist.o \ inet_cidr_ntop.o \ - inet_net_pton.o \ int.o \ int8.o \ json.o \ diff --git a/src/include/port.h b/src/include/port.h index 3d103a2b31..285
Re: [PATCH] Accept IP addresses in server certificate SANs
At Thu, 17 Feb 2022 17:29:15 +, Jacob Champion wrote in > On Tue, 2022-02-15 at 15:16 +0900, Kyotaro Horiguchi wrote: > > (This needs rebasing) > > Done in v6, attached. Thanks! > > # I forgot to mention that, the test fails for me even without the > > # change. I didn't checked what is wrong there, though. > > Ah. We should probably figure that out, then -- what failures do you > see? I forgot the detail but v6 still fails for me. I think it is that. t/003_sslinfo.pl ... 1/? # Tests were run but no plan was declared and done_testing() was not seen. # Looks like your test exited with 29 just after 6. t/003_sslinfo.pl ... Dubious, test returned 29 (wstat 7424, 0x1d00) All 6 subtests passed ... Result: FAIL The script complains like this: ok 6 - ssl_client_cert_present() for connection with cert connection error: 'psql: error: connection to server at "127.0.0.1", port 62656 failed: SSL error: tlsv1 alert unknown ca' while running 'psql -XAtq -d sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=trustdb hostaddr=127.0.0.1 user=ssltestuser host=localhost -f - -v ON_ERROR_STOP=1' at /home/horiguti/work/worktrees/ipsan/src/test/ssl/../../../src/test/perl/PostgreSQL/Test/Cluster.pm line 1873. So, psql looks like disliking the ca certificate. I also will dig into that. > > Mmm. after the end of the loop, rc is non-zero only when the loop has > > exited by the break and otherwise rc is zero. Why isn't it equivalent > > to setting check_cn to false at the break? > > check_cn can be false if rc is zero, too; it means that we found a SAN > of the correct type but it didn't match. Don't we count unmatched certs as "existed"? In that case I think we don't go to CN. > > Anyway, apart from that detail, I reconfirmed the spec the patch is > > going to implement. > > > > * If connhost contains a DNS name, and the certificate's SANs contain any > > * dNSName entries, then we'll ignore the Subject Common Name entirely; > > * otherwise, we fall back to checking the CN. (This behavior matches the > > * RFC.) > > > > Sure. > > > > * If connhost contains an IP address, and the SANs contain iPAddress > > * entries, we again ignore the CN. Otherwise, we allow the CN to match, > > * EVEN IF there is a dNSName in the SANs. (RFC 6125 prohibits this: "A > > * client MUST NOT seek a match for a reference identifier of CN-ID if the > > * presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any > > * application-specific identifier types supported by the client.") > > > > Actually the patch searches for a match of IP address connhost from > > dNSName SANs even if iPAddress SANs exist. I think we've not > > explicitly defined thebehavior in that case. > > That's a good point; I didn't change the prior behavior. I feel more > comfortable leaving that check, since it is technically possible to > push something that looks like an IP address into a dNSName SAN. We > should probably make an explicit decision on that, as you say. > > But I don't think that contradicts the code comment, does it? The > comment is just talking about CN fallback scenarios. If you find a > match in a dNSName, there's no reason to fall back to the CN. The comment explains the spec correctly. From a practical view, the behavior above doesn't seem to make things insecure. So I don't have a strong opinion on the choice of the behaviors. The only thing I'm concerned here is the possibility that the decision corners us to some uncomfortable state between the RFC and our spec in future. On the other hand, changing the behavior can immediately make someone uncomfortable. So, I'd like to leave it to committers:p > > I supposed that we only > > be deviant in the case "IP address connhost and no SANs of any type > > exists". What do you think about it? > > We fall back in the case of "IP address connhost and dNSName SANs > exist", which is prohibited by that part of RFC 6125. I don't think we > deviate in the case you described; can you explain further? In that case, i.e., connhost is IP address and no SANs exist at all, we go to CN. On the other hand in RFC6125: rfc6125> In some cases, the URI is specified as an IP address rather rfc6125> than a hostname. In this case, the iPAddress subjectAltName rfc6125> must be present in the certificate and must exactly match the rfc6125> IP in the URI. It (seems to me) denies that behavior. Regardless of the existence of other types of SANs, iPAddress is required if connname is an IP address. (That is, it doesn't seem to me that there's any context like "if any SANs exists", but I'm not so sure I read it perfectly.) > > - For the certificate that have only dNSNames or no SANs presented, we > > serach for a match from all dNSNames if any or otherwise try CN > > regardless of the type of connhost. > > Correct. (I don't find that way of dividing up the cases very > intuitive, though.) Yeah, it's the same decision to the above. It doesn't matter in the
Re: [PATCH] Accept IP addresses in server certificate SANs
On Mon, 2022-03-14 at 15:30 +0900, Kyotaro Horiguchi wrote: > t/003_sslinfo.pl ... 1/? # Tests were run but no plan was declared and > done_testing() was not seen. > # Looks like your test exited with 29 just after 6. > t/003_sslinfo.pl ... Dubious, test returned 29 (wstat 7424, 0x1d00) > All 6 subtests passed > ... > Result: FAIL > > The script complains like this: > > ok 6 - ssl_client_cert_present() for connection with cert > connection error: 'psql: error: connection to server at "127.0.0.1", port > 62656 failed: SSL error: tlsv1 alert unknown ca' > while running 'psql -XAtq -d sslrootcert=ssl/root+server_ca.crt > sslmode=require dbname=trustdb hostaddr=127.0.0.1 user=ssltestuser > host=localhost -f - -v ON_ERROR_STOP=1' at > /home/horiguti/work/worktrees/ipsan/src/test/ssl/../../../src/test/perl/PostgreSQL/Test/Cluster.pm > line 1873. > > So, psql looks like disliking the ca certificate. I also will dig > into that. Hmm, the sslinfo tests are failing? I wouldn't have expected that based on the patch changes. Just to confirm -- they pass for you without the patch? > > > Mmm. after the end of the loop, rc is non-zero only when the loop has > > > exited by the break and otherwise rc is zero. Why isn't it equivalent > > > to setting check_cn to false at the break? > > > > check_cn can be false if rc is zero, too; it means that we found a SAN > > of the correct type but it didn't match. > > Don't we count unmatched certs as "existed"? In that case I think we > don't go to CN. Unmatched names, you mean? I'm not sure I understand. If it helps, the two tests that will fail if check_cn is unset only at the break are - certificate with both a CN and SANs ignores CN - certificate with both an IP CN and IP SANs ignores CN because none of the SANs would match in that case. > > > Actually the patch searches for a match of IP address connhost from > > > dNSName SANs even if iPAddress SANs exist. I think we've not > > > explicitly defined thebehavior in that case. > > > > That's a good point; I didn't change the prior behavior. I feel more > > comfortable leaving that check, since it is technically possible to > > push something that looks like an IP address into a dNSName SAN. We > > should probably make an explicit decision on that, as you say. > > > > But I don't think that contradicts the code comment, does it? The > > comment is just talking about CN fallback scenarios. If you find a > > match in a dNSName, there's no reason to fall back to the CN. > > The comment explains the spec correctly. From a practical view, the > behavior above doesn't seem to make things insecure. So I don't have > a strong opinion on the choice of the behaviors. > > The only thing I'm concerned here is the possibility that the decision > corners us to some uncomfortable state between the RFC and our spec in > future. On the other hand, changing the behavior can immediately make > someone uncomfortable. > > So, I'd like to leave it to committers:p Sounds good. I'll work on adding tests for the current behavior, and if the committers don't like it, we can change it. > > > I supposed that we only > > > be deviant in the case "IP address connhost and no SANs of any type > > > exists". What do you think about it? > > > > We fall back in the case of "IP address connhost and dNSName SANs > > exist", which is prohibited by that part of RFC 6125. I don't think we > > deviate in the case you described; can you explain further? > > In that case, i.e., connhost is IP address and no SANs exist at all, > we go to CN. On the other hand in RFC6125: > > rfc6125> In some cases, the URI is specified as an IP address rather > rfc6125> than a hostname. In this case, the iPAddress subjectAltName > rfc6125> must be present in the certificate and must exactly match the > rfc6125> IP in the URI. > > It (seems to me) denies that behavior. Regardless of the existence of > other types of SANs, iPAddress is required if connname is an IP > address. (That is, it doesn't seem to me that there's any context > like "if any SANs exists", but I'm not so sure I read it perfectly.) I see what you mean now. Yes, we deviate there as well (and have done so for a while now). I think breaking compatibility there would probably not go over well. > Thanks. All behaviors and theier reasons is now clear. So > > Let's leave them for committers for now. Thank you for the review! --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
On Tue, 2022-03-15 at 21:41 +, Jacob Champion wrote: > Sounds good. I'll work on adding tests for the current behavior, and if > the committers don't like it, we can change it. Done in v7, attached. --Jacob commit 51052e322f4d4e4f75d3cf3d000a363244696013 Author: Jacob Champion Date: Tue Mar 15 16:02:15 2022 -0700 squash! libpq: allow IP address SANs in server certs Per discussion on list, add tests for the (counter-intuitive) standing behavior that matches IP addresses embedded in dNSName SANs. diff --git a/src/test/ssl/conf/server-ip-in-dnsname.config b/src/test/ssl/conf/server-ip-in-dnsname.config new file mode 100644 index 00..b15649aef7 --- /dev/null +++ b/src/test/ssl/conf/server-ip-in-dnsname.config @@ -0,0 +1,18 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# + +[ req ] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +OU = PostgreSQL test suite + +# For Subject Alternative Names +[ v3_req ] +subjectAltName = @alt_names + +# Normally IP addresses should not go into a dNSName. +[ alt_names ] +DNS.1 = 192.0.2.1 diff --git a/src/test/ssl/ssl/server-ip-in-dnsname.crt b/src/test/ssl/ssl/server-ip-in-dnsname.crt new file mode 100644 index 00..78ad8d99c8 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-in-dnsname.crt @@ -0,0 +1,70 @@ +Certificate: +Data: +Version: 3 (0x2) +Serial Number: 2315416547509162496 (0x2022031515585200) +Signature Algorithm: sha256WithRSAEncryption +Issuer: CN = Test CA for PostgreSQL SSL regression test server certs +Validity +Not Before: Mar 15 22:58:52 2022 GMT +Not After : Jul 31 22:58:52 2049 GMT +Subject: OU = PostgreSQL test suite +Subject Public Key Info: +Public Key Algorithm: rsaEncryption +RSA Public-Key: (2048 bit) +Modulus: +00:ca:67:e5:b3:f5:fc:e7:c1:41:1f:f2:bc:e9:0e: +07:3c:40:ac:4d:63:d5:84:a1:55:ad:a9:72:3f:3d: +eb:e0:95:0c:48:15:37:91:e5:bb:3e:ca:1d:4f:c9: +63:33:59:08:01:db:e5:60:07:ba:f5:f1:29:ea:23: +e1:38:b9:6d:ed:4a:b2:a3:b4:98:dd:69:f9:13:0d: +ed:59:42:a4:2a:5b:d0:05:93:cf:8c:fe:23:c4:d4: +85:26:67:9a:08:21:1e:f7:d6:e1:17:dc:64:c0:9c: +1e:ad:6f:7a:f5:53:0f:14:7f:70:06:c3:3d:8a:60: +04:20:f9:17:f4:99:31:1c:8c:0f:0e:41:ec:82:cb: +99:fb:74:7a:a0:35:9c:2a:9a:bf:2a:81:08:66:6f: +c7:5a:25:f0:de:41:7d:57:6b:0d:7a:7f:ac:de:7d: +f7:b9:21:05:64:11:67:c8:3c:e0:56:72:15:95:df: +a7:0a:79:ed:eb:b4:38:64:03:cd:91:57:a0:42:f6: +b7:83:95:95:de:bb:2b:98:dc:72:55:95:c4:76:3a: +14:67:07:8c:2b:f2:aa:ce:3c:3c:23:ce:47:ce:1a: +9d:9c:23:1a:ca:95:39:2e:b6:e7:4f:c3:58:a0:50: +3b:82:b2:86:44:d5:7f:40:16:fc:80:86:48:c3:8a: +90:09 +Exponent: 65537 (0x10001) +X509v3 extensions: +X509v3 Subject Alternative Name: +DNS:192.0.2.1 +Signature Algorithm: sha256WithRSAEncryption + c8:00:01:f4:58:92:84:a5:b3:cd:d1:08:a1:37:a4:85:50:0e: + 56:2f:88:b1:b0:10:02:a3:d3:88:00:69:12:9c:a7:8e:79:51: + 06:e6:fb:e0:3a:76:b4:86:34:1c:39:c8:2d:23:5a:02:0c:b8: + 54:2d:c8:c2:bb:0c:62:21:6e:b2:42:7b:4e:6b:3c:a8:d2:ca: + 28:26:bd:21:12:6d:96:7e:2a:6f:22:94:2c:e9:6f:44:d4:e4: + dc:b3:ad:b8:04:05:5f:90:0c:2b:e9:64:f6:57:0a:08:00:f7: + 10:0f:69:d1:b4:84:21:49:42:11:f4:9e:d0:e5:66:f3:b9:0c: + 3b:a6:d8:50:84:17:d6:fd:43:d6:c3:bb:da:8d:e9:74:af:57: + 5f:95:b7:b4:0a:0a:79:58:63:5f:a7:59:18:cb:e4:1f:5d:6f: + d8:8e:4b:2d:64:43:f5:28:59:be:4c:5e:7c:bb:1a:c8:0e:66: + 1b:a1:2f:04:7a:b5:11:10:64:22:c6:28:db:23:40:1f:5e:34: + a1:c6:d8:b2:4b:ad:eb:6d:da:68:dc:2d:01:3e:3f:58:c7:65: + 6d:5a:2e:14:d2:aa:1e:7f:f8:8b:27:12:41:08:bb:c4:ff:de: + fe:76:e3:dd:2b:1d:00:92:43:96:03:d5:6e:7a:79:16:2e:9e: + fd:ed:43:3c +-BEGIN CERTIFICATE- +MIIC/DCCAeSgAwIBAgIIICIDFRVYUgAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl +cnZlciBjZXJ0czAeFw0yMjAzMTUyMjU4NTJaFw00OTA3MzEyMjU4NTJaMCAxHjAc +BgNVBAsMFVBvc3RncmVTUUwgdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMpn5bP1/OfBQR/yvOkOBzxArE1j1YShVa2pcj896+CVDEgV +N5Hluz7KHU/JYzNZCAHb5WAHuvXxKeoj4Ti5be1KsqO0mN1p+RMN7VlCpCpb0AWT +z4z+I8TUhSZnmgghHvfW4RfcZMCcHq1vevVTDxR/cAbDPYpgBCD5F/SZMRyMDw5B +7ILLmft0eqA1nCqavyqBCGZvx1ol8N5BfVdrDXp/rN5997khBWQRZ8g84FZyFZXf +pwp57eu0OGQDzZFXoEL2t4OVld67K5jcclWVxHY6FGcHjCvyqs48PCPOR84anZwj +GsqVOS6250/DWKBQO4KyhkTVf0AW/ICGSMOKkAkCAwEAAaMYMBYwFAYDVR0RBA0w +C4IJMTkyLjAuMi4xMA0GCSqGSIb3
Re: [PATCH] Accept IP addresses in server certificate SANs
At Tue, 15 Mar 2022 21:41:49 +, Jacob Champion wrote in > On Mon, 2022-03-14 at 15:30 +0900, Kyotaro Horiguchi wrote: > > t/003_sslinfo.pl ... 1/? # Tests were run but no plan was declared and > > done_testing() was not seen. > > # Looks like your test exited with 29 just after 6. > > t/003_sslinfo.pl ... Dubious, test returned 29 (wstat 7424, 0x1d00) > > All 6 subtests passed > > ... > > Result: FAIL > > > > The script complains like this: > > > > ok 6 - ssl_client_cert_present() for connection with cert > > connection error: 'psql: error: connection to server at "127.0.0.1", port > > 62656 failed: SSL error: tlsv1 alert unknown ca' > > while running 'psql -XAtq -d sslrootcert=ssl/root+server_ca.crt > > sslmode=require dbname=trustdb hostaddr=127.0.0.1 user=ssltestuser > > host=localhost -f - -v ON_ERROR_STOP=1' at > > /home/horiguti/work/worktrees/ipsan/src/test/ssl/../../../src/test/perl/PostgreSQL/Test/Cluster.pm > > line 1873. > > > > So, psql looks like disliking the ca certificate. I also will dig > > into that. > > Hmm, the sslinfo tests are failing? I wouldn't have expected that based > on the patch changes. Just to confirm -- they pass for you without the > patch? Mmm I'm not sure how come I didn't noticed that, master also fails for me fo the same reason. In the past that may fail when valid clinent-certs exists in the users ~/.postgresql but I believe that has been fixed. > > > > Mmm. after the end of the loop, rc is non-zero only when the loop has > > > > exited by the break and otherwise rc is zero. Why isn't it equivalent > > > > to setting check_cn to false at the break? > > > > > > check_cn can be false if rc is zero, too; it means that we found a SAN > > > of the correct type but it didn't match. I'm not discussing on the meaning. Purely on the logical equivalancy of the two ways to decide whether to visit CN. Concretely, the equivalancy between this: = check_cn = true; rc = 0; for (i < san_len) { if (type matches) check_cn = false; if (some error happens) rc = nonzero; if (rc != 0) break; } !if ((rc == 0) && check_cn) {} = and this. = check_cn = true; rc = 0; for (i < san_len) { if (type matches) check_cn = false; if (some error happens) rc = nonzero; if (rc != 0) { !check_cn = false; break; } } !if (check_cn) {} = The two are equivalant to me. And if it is, the latter form smees simpler and clearner to me. > > > check_cn can be false if rc is zero, too; it means that we found a SAN > > > of the correct type but it didn't match. > > > Don't we count unmatched certs as "existed"? In that case I think we > > don't go to CN. > > Unmatched names, you mean? I'm not sure I understand. Sorry, I was confused here. Please ignore that. I shoudl have said something like the following instead. > check_cn can be false if rc is zero, too; it means that we found a SAN > of the correct type but it didn't match. Yes, in that case we don't visit CN because check_cn is false even if we don't exit by (rc != 0) and that behavior is not changed by my proposal. > > > > I supposed that we only > > > > be deviant in the case "IP address connhost and no SANs of any type > > > > exists". What do you think about it? > > > > > > We fall back in the case of "IP address connhost and dNSName SANs > > > exist", which is prohibited by that part of RFC 6125. I don't think we > > > deviate in the case you described; can you explain further? > > > > In that case, i.e., connhost is IP address and no SANs exist at all, > > we go to CN. On the other hand in RFC6125: > > > > rfc6125> In some cases, the URI is specified as an IP address rather > > rfc6125> than a hostname. In this case, the iPAddress subjectAltName > > rfc6125> must be present in the certificate and must exactly match the > > rfc6125> IP in the URI. > > > > It (seems to me) denies that behavior. Regardless of the existence of > > other types of SANs, iPAddress is required if connname is an IP > > address. (That is, it doesn't seem to me that there's any context > > like "if any SANs exists", but I'm not so sure I read it perfectly.) > > I see what you mean now. Yes, we deviate there as well (and have done > so for a while now). I think breaking compatibility there would > probably not go over well. Agreed. regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
At Wed, 16 Mar 2022 15:56:02 +0900 (JST), Kyotaro Horiguchi wrote in > Mmm I'm not sure how come I didn't noticed that, master also fails > for me fo the same reason. In the past that may fail when valid > clinent-certs exists in the users ~/.postgresql but I believe that has > been fixed. And finally my fear found to be true.. The test doesn't fail after removing files under ~/.postgresql, which should not happen. I'll fix it apart from this. Sorry for the confusion. regards. -- Kyotaro Horiguchi NTT Open Source Software Center
Re: [PATCH] Accept IP addresses in server certificate SANs
On Wed, 2022-03-16 at 15:56 +0900, Kyotaro Horiguchi wrote: > At Tue, 15 Mar 2022 21:41:49 +, Jacob Champion > wrote in > > Hmm, the sslinfo tests are failing? I wouldn't have expected that based > > on the patch changes. Just to confirm -- they pass for you without the > > patch? > > Mmm I'm not sure how come I didn't noticed that, master also fails > for me fo the same reason. In the past that may fail when valid > clinent-certs exists in the users ~/.postgresql but I believe that has > been fixed. Good to know; I was worried that I'd messed up something well outside the code I'd touched. > > > > > Mmm. after the end of the loop, rc is non-zero only when the loop has > > > > > exited by the break and otherwise rc is zero. Why isn't it equivalent > > > > > to setting check_cn to false at the break? > > > > > > > > check_cn can be false if rc is zero, too; it means that we found a SAN > > > > of the correct type but it didn't match. > > I'm not discussing on the meaning. Purely on the logical equivalancy > of the two ways to decide whether to visit CN. > > Concretely, the equivalancy between this: [snip] Thank you for the explanation -- the misunderstanding was all on my end. I thought you were asking me to move the check_cn assignment instead of copying it to the end. I agree that your suggestion is much clearer, and I'll make that change tomorrow. Thanks! --Jacob
Re: [PATCH] Accept IP addresses in server certificate SANs
On Wed, 2022-03-16 at 23:49 +, Jacob Champion wrote: > Thank you for the explanation -- the misunderstanding was all on my > end. I thought you were asking me to move the check_cn assignment > instead of copying it to the end. I agree that your suggestion is much > clearer, and I'll make that change tomorrow. Done in v8. Thanks again for your suggestions (and for your perseverance when I didn't get it)! --Jacob diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index c6a80d30c2..06b166b7fd 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -650,7 +650,14 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, } if (rc != 0) + { + /* +* Either we hit an error or a match, and either way we should +* not fall back to the CN. +*/ + check_cn = false; break; + } } sk_GENERAL_NAME_pop_free(peer_san, GENERAL_NAME_free); } @@ -663,7 +670,7 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, * dNSName is present, the CN must be ignored. We break this rule if host is * an IP address; see the comment above.) */ - if ((rc == 0) && check_cn) + if (check_cn) { X509_NAME *subject_name; From 84a107da33b7d901cb318f8c2fd140644fbc5100 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 24 Nov 2021 14:46:11 -0800 Subject: [PATCH v8 1/3] Move inet_net_pton() to src/port This will be helpful for IP address verification in libpq. --- src/backend/utils/adt/Makefile | 1 - src/include/port.h | 3 +++ src/include/utils/builtins.h| 4 src/port/Makefile | 1 + src/{backend/utils/adt => port}/inet_net_pton.c | 16 +++- src/tools/msvc/Mkvcbuild.pm | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) rename src/{backend/utils/adt => port}/inet_net_pton.c (96%) diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 41b486bcef..d173d52157 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -43,7 +43,6 @@ OBJS = \ geo_selfuncs.o \ geo_spgist.o \ inet_cidr_ntop.o \ - inet_net_pton.o \ int.o \ int8.o \ json.o \ diff --git a/src/include/port.h b/src/include/port.h index 3d103a2b31..2852e5b58b 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -515,6 +515,9 @@ extern int pg_codepage_to_encoding(UINT cp); extern char *pg_inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size); +/* port/inet_net_pton.c */ +extern int pg_inet_net_pton(int af, const char *src, void *dst, size_t size); + /* port/pg_strong_random.c */ extern void pg_strong_random_init(void); extern bool pg_strong_random(void *buf, size_t len); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 666e545496..67b9326dc8 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -93,10 +93,6 @@ extern int xidLogicalComparator(const void *arg1, const void *arg2); extern char *pg_inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size); -/* inet_net_pton.c */ -extern int pg_inet_net_pton(int af, const char *src, - void *dst, size_t size); - /* network.c */ extern double convert_network_to_scalar(Datum value, Oid typid, bool *failure); extern Datum network_scan_first(Datum in); diff --git a/src/port/Makefile b/src/port/Makefile index bfe1feb0d4..c3ae7b3d5c 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -43,6 +43,7 @@ OBJS = \ bsearch_arg.o \ chklocale.o \ inet_net_ntop.o \ + inet_net_pton.o \ noblock.o \ path.o \ pg_bitutils.o \ diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/port/inet_net_pton.c similarity index 96% rename from src/backend/utils/adt/inet_net_pton.c rename to src/port/inet_net_pton.c index d3221a1313..bae50ba67e 100644 --- a/src/backend/utils/adt/inet_net_pton.c +++ b/src/port/inet_net_pton.c @@ -14,14 +14,18 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * src/backend/utils/adt/inet_net_pton.c + * src/port/inet_net_pton.c */ #if defined(LIBC_SCCS) && !defined(lint) static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $"; #endif +#ifndef FRONTEND #include "postgres.h" +#else +#include "postgres_fe.h" +#endif #include #include @@ -29,9 +33,19 @@ static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 m