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 <pchamp...@vmware.com> 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..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 <sys/socket.h> #include <netinet/in.h> @@ -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 <assert.h> #include <ctype.h> +#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 105f5c72a2..db2f7afd24 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 dc5682d7cff0fe5d8eff6ff936ed4738f33e251f Mon Sep 17 00:00:00 2001 From: Jacob Champion <pchamp...@vmware.com> Date: Thu, 18 Nov 2021 15:36:18 -0800 Subject: [PATCH v6 2/3] 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. - 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. --- src/include/common/inet-common.h | 35 +++++ src/include/utils/inet.h | 13 +- src/interfaces/libpq/fe-secure-common.c | 104 ++++++++++++++ src/interfaces/libpq/fe-secure-common.h | 5 + src/interfaces/libpq/fe-secure-openssl.c | 131 ++++++++++++++++-- src/port/inet_net_ntop.c | 12 +- src/port/inet_net_pton.c | 10 +- .../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 ++ .../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/sslfiles.mk | 5 + src/test/ssl/t/001_ssltests.pl | 99 ++++++++++++- 24 files changed, 696 insertions(+), 46 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/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 diff --git a/src/include/common/inet-common.h b/src/include/common/inet-common.h new file mode 100644 index 0000000000..3ad72261b6 --- /dev/null +++ b/src/include/common/inet-common.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * inet-common.h + * + * Common code for clients of the inet_* APIs. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/common/inet-common.h + * + *------------------------------------------------------------------------- + */ + +#ifndef INET_COMMON_H +#define INET_COMMON_H + +/* + * We use these values for the "family" field of inet_struct. + * + * Referencing all of the non-AF_INET types to AF_INET lets us work on + * machines which may not have the appropriate address family (like + * inet6 addresses when AF_INET6 isn't present) but doesn't cause a + * dump/reload requirement. Pre-7.4 databases used AF_INET for the family + * type on disk. + * + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. (Frontend clients should + * include this header directly.) 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 /* INET_COMMON_H */ diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h index 3073c0307e..c56c016987 100644 --- a/src/include/utils/inet.h +++ b/src/include/utils/inet.h @@ -14,6 +14,7 @@ #ifndef INET_H #define INET_H +#include "common/inet-common.h" #include "fmgr.h" /* @@ -27,18 +28,6 @@ typedef struct unsigned char ipaddr[16]; /* up to 128 bits of address */ } inet_struct; -/* - * We use these values for the "family" field. - * - * Referencing all of the non-AF_INET types to AF_INET lets us work on - * machines which may not have the appropriate address family (like - * inet6 addresses when AF_INET6 isn't present) but doesn't cause a - * dump/reload requirement. Pre-7.4 databases used AF_INET for the family - * type on disk. - */ -#define PGSQL_AF_INET (AF_INET + 0) -#define PGSQL_AF_INET6 (AF_INET + 1) - /* * Both INET and CIDR addresses are represented within Postgres as varlena * objects, ie, there is a varlena header in front of the struct type diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c index bd46f08fae..2b8f2d6b24 100644 --- a/src/interfaces/libpq/fe-secure-common.c +++ b/src/interfaces/libpq/fe-secure-common.c @@ -19,9 +19,13 @@ #include "postgres_fe.h" +#include <netinet/in.h> +#include <arpa/inet.h> + #include "fe-secure-common.h" #include "libpq-int.h" +#include "port.h" #include "pqexpbuffer.h" /* @@ -144,6 +148,106 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn, return result; } +/* + * Check if an IP address from a server's certificate matches the peer's + * hostname (which must itself be an IPv4/6 address). + * + * Returns 1 if the address matches, and 0 if it does not. On error, returns + * -1, and sets the libpq error message. + * + * A string representation of the certificate's IP address is returned in + * *store_name. The caller is responsible for freeing it. + */ +int +pq_verify_peer_name_matches_certificate_ip(PGconn *conn, + const unsigned char *ipdata, + size_t iplen, + char **store_name) +{ + char *addrstr; + int match = 0; + char *host = conn->connhost[conn->whichhost].host; + int family; + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + char sebuf[PG_STRERROR_R_BUFLEN]; + + *store_name = NULL; + + if (!(host && host[0] != '\0')) + { + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("host name must be specified\n")); + return -1; + } + + /* + * The data from the certificate is in network byte order. Convert our host + * string to network-ordered bytes as well, for comparison. (The host string + * isn't guaranteed to actually be an IP address, so if this conversion + * fails we need to consider it a mismatch rather than an error.) + */ + if (iplen == 4) + { + /* IPv4 */ + struct in_addr addr; + + family = PGSQL_AF_INET; + + /* + * The use of inet_aton() is deliberate; we accept alternative IPv4 + * address notations that are accepted by inet_aton() but not + * inet_pton() as server addresses. + */ + if (inet_aton(host, &addr)) + { + if (memcmp(ipdata, &addr.s_addr, iplen) == 0) + match = 1; + } + } + else if (iplen == 16) + { + /* IPv6 */ + unsigned char addr[16]; + + family = PGSQL_AF_INET6; + + /* + * 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 (memcmp(ipdata, addr, iplen) == 0) + match = 1; + } + } + else + { + /* + * Not IPv4 or IPv6. We could ignore the field, but leniency seems wrong + * given the subject matter. + */ + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("certificate contains IP address with invalid length %lu\n"), + (unsigned long) iplen); + return -1; + } + + /* Generate a human-readable representation of the certificate's IP. */ + addrstr = pg_inet_net_ntop(family, ipdata, 8 * iplen, tmp, sizeof(tmp)); + if (!addrstr) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not convert certificate's IP address to string: %s\n"), + strerror_r(errno, sebuf, sizeof(sebuf))); + return -1; + } + + *store_name = strdup(addrstr); + return match; +} + /* * Verify that the server certificate matches the hostname we connected to. * diff --git a/src/interfaces/libpq/fe-secure-common.h b/src/interfaces/libpq/fe-secure-common.h index 1cca6d785a..20ff9ba5db 100644 --- a/src/interfaces/libpq/fe-secure-common.h +++ b/src/interfaces/libpq/fe-secure-common.h @@ -16,11 +16,16 @@ #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, const char *namedata, size_t namelen, char **store_name); +extern int pq_verify_peer_name_matches_certificate_ip(PGconn *conn, + const unsigned char *addrdata, + size_t addrlen, + char **store_name); extern bool pq_verify_peer_name_matches_certificate(PGconn *conn); #endif /* FE_SECURE_COMMON_H */ diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index f6e563a2e5..108def3c1b 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -72,6 +72,9 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx); static int openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *name, char **store_name); +static int openssl_verify_peer_name_matches_certificate_ip(PGconn *conn, + ASN1_OCTET_STRING *addr_entry, + char **store_name); static void destroy_ssl_system(void); static int initialize_SSL(PGconn *conn); static PostgresPollingStatusType open_client_SSL(PGconn *); @@ -509,6 +512,51 @@ openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *nam return pq_verify_peer_name_matches_certificate_name(conn, (const char *) namedata, len, store_name); } +/* + * OpenSSL-specific wrapper around + * pq_verify_peer_name_matches_certificate_ip(), converting the + * ASN1_OCTET_STRING into a plain C string. + */ +static int +openssl_verify_peer_name_matches_certificate_ip(PGconn *conn, + ASN1_OCTET_STRING *addr_entry, + char **store_name) +{ + int len; + const unsigned char *addrdata; + + /* Should not happen... */ + if (addr_entry == NULL) + { + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("SSL certificate's address entry is missing\n")); + return -1; + } + + /* + * GEN_IPADD is an OCTET STRING containing an IP address in network byte + * order. + */ +#ifdef HAVE_ASN1_STRING_GET0_DATA + addrdata = ASN1_STRING_get0_data(addr_entry); +#else + addrdata = ASN1_STRING_data(addr_entry); +#endif + len = ASN1_STRING_length(addr_entry); + + return pq_verify_peer_name_matches_certificate_ip(conn, 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. * @@ -522,6 +570,36 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, STACK_OF(GENERAL_NAME) * peer_san; int i; int rc = 0; + char *host = conn->connhost[conn->whichhost].host; + int host_type; + bool check_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, @@ -537,24 +615,40 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, for (i = 0; i < san_len; i++) { const GENERAL_NAME *name = sk_GENERAL_NAME_value(peer_san, i); + char *alt_name = NULL; - if (name->type == GEN_DNS) + if (name->type == host_type) { - char *alt_name; + /* + * This SAN is of the same type (IP or DNS) as our host name, so + * don't allow a fallback check of the CN. + */ + check_cn = false; + } + if (name->type == GEN_DNS) + { (*names_examined)++; rc = openssl_verify_peer_name_matches_certificate_name(conn, name->d.dNSName, &alt_name); + } + else if (name->type == GEN_IPADD) + { + (*names_examined)++; + rc = openssl_verify_peer_name_matches_certificate_ip(conn, + name->d.iPAddress, + &alt_name); + } - if (alt_name) - { - if (!*first_name) - *first_name = alt_name; - else - free(alt_name); - } + if (alt_name) + { + if (!*first_name) + *first_name = alt_name; + else + free(alt_name); } + if (rc != 0) break; } @@ -562,13 +656,14 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, } /* - * If there is no subjectAltName extension of type dNSName, check the + * If there is no subjectAltName extension of the matching type, check the * Common Name. * * (Per RFC 2818 and RFC 6125, if the subjectAltName extension of type - * dNSName is present, the CN must be ignored.) + * dNSName is present, the CN must be ignored. We break this rule if host is + * an IP address; see the comment above.) */ - if (*names_examined == 0) + if ((rc == 0) && check_cn) { X509_NAME *subject_name; @@ -581,10 +676,20 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, NID_commonName, -1); if (cn_index >= 0) { + char *common_name = NULL; + (*names_examined)++; rc = openssl_verify_peer_name_matches_certificate_name(conn, X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, cn_index)), - first_name); + &common_name); + + if (common_name) + { + if (!*first_name) + *first_name = common_name; + else + free(common_name); + } } } } diff --git a/src/port/inet_net_ntop.c b/src/port/inet_net_ntop.c index b8ad69c390..af28056132 100644 --- a/src/port/inet_net_ntop.c +++ b/src/port/inet_net_ntop.c @@ -31,17 +31,7 @@ static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 m #include <netinet/in.h> #include <arpa/inet.h> -#ifndef FRONTEND -#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 +#include "common/inet-common.h" #define NS_IN6ADDRSZ 16 diff --git a/src/port/inet_net_pton.c b/src/port/inet_net_pton.c index bae50ba67e..5bae811c6f 100644 --- a/src/port/inet_net_pton.c +++ b/src/port/inet_net_pton.c @@ -33,18 +33,10 @@ static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 m #include <assert.h> #include <ctype.h> +#include "common/inet-common.h" #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 diff --git a/src/test/ssl/conf/server-cn-and-ip-alt-names.config b/src/test/ssl/conf/server-cn-and-ip-alt-names.config new file mode 100644 index 0000000000..a6fa09bad3 --- /dev/null +++ b/src/test/ssl/conf/server-cn-and-ip-alt-names.config @@ -0,0 +1,24 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# +# This certificate contains a CN and SANs for both IPv4 and IPv6. + + +[ req ] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +# Note: According to RFC 2818 and 6125, the CN is ignored, when DNS names are +# present in the SANs. But they are silent on whether the CN is checked when IP +# addresses are present. +CN = common-name.pg-ssltest.test +OU = PostgreSQL test suite + +# For Subject Alternative Names +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 192.0.2.1 +IP.2 = 2001:DB8::1 diff --git a/src/test/ssl/conf/server-ip-alt-names.config b/src/test/ssl/conf/server-ip-alt-names.config new file mode 100644 index 0000000000..c22f22951a --- /dev/null +++ b/src/test/ssl/conf/server-ip-alt-names.config @@ -0,0 +1,19 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# +# This certificate has a two IP-address SANs, and no CN. + +[ 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 + +[ alt_names ] +IP.1 = 192.0.2.1 +IP.2 = 2001:DB8::1 diff --git a/src/test/ssl/conf/server-ip-cn-and-alt-names.config b/src/test/ssl/conf/server-ip-cn-and-alt-names.config new file mode 100644 index 0000000000..a4087f0a18 --- /dev/null +++ b/src/test/ssl/conf/server-ip-cn-and-alt-names.config @@ -0,0 +1,21 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# +# This certificate contains both a CN and SANs in IP address format. + + +[ req ] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +CN = 192.0.2.1 +OU = PostgreSQL test suite + +# For Subject Alternative Names +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 192.0.2.2 +IP.2 = 2001:DB8::1 diff --git a/src/test/ssl/conf/server-ip-cn-and-dns-alt-names.config b/src/test/ssl/conf/server-ip-cn-and-dns-alt-names.config new file mode 100644 index 0000000000..7121803b49 --- /dev/null +++ b/src/test/ssl/conf/server-ip-cn-and-dns-alt-names.config @@ -0,0 +1,21 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# +# This certificate contains both a CN and SANs in IP address format. + + +[ req ] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +CN = 192.0.2.1 +OU = PostgreSQL test suite + +# For Subject Alternative Names +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = dns1.alt-name.pg-ssltest.test +DNS.2 = dns2.alt-name.pg-ssltest.test diff --git a/src/test/ssl/conf/server-ip-cn-only.config b/src/test/ssl/conf/server-ip-cn-only.config new file mode 100644 index 0000000000..585d8bdae8 --- /dev/null +++ b/src/test/ssl/conf/server-ip-cn-only.config @@ -0,0 +1,12 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# + +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] +CN = 192.0.2.1 +OU = PostgreSQL test suite + +# No Subject Alternative Names diff --git a/src/test/ssl/ssl/server-cn-and-ip-alt-names.crt b/src/test/ssl/ssl/server-cn-and-ip-alt-names.crt new file mode 100644 index 0000000000..4e58c85ccb --- /dev/null +++ b/src/test/ssl/ssl/server-cn-and-ip-alt-names.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgIIICERKRE1UQAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl +cnZlciBjZXJ0czAeFw0yMTExMjkxOTM1NTFaFw00OTA0MTYxOTM1NTFaMEYxHjAc +BgNVBAsMFVBvc3RncmVTUUwgdGVzdCBzdWl0ZTEkMCIGA1UEAwwbY29tbW9uLW5h +bWUucGctc3NsdGVzdC50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA6+8IYKAFnZ7V+fDo1cyMpbGBLzCfJOQ/1o2jOGP4+GjpsZgv6S6UT2MheC8M +iiEFrYwdsSIZyYc3jEZrluy/UuR0bCGtqU92BCqa0iBLhvHOgjR588u253eLxQtQ +8iJn11QPrKMk35nMkmY8GfHt4sGFbvBL6+GpipHq7a6cde3Z+v4kCB5dKMYDUDtm +3mJmviuGNAu5wOqItk2Yi5dwJs1054007KNH0Il43urxiOfnkLS0cG5kehboPf86 +vxBt3iHByrU/9/DY5IvQCfSXVNa6rb5w5/pGja9aCei6Mv1jQY/V8SMQTga+MOsA +0WB9akxMi2NxwS2+BQ4k/McPlwIDAQABoyUwIzAhBgNVHREEGjAYhwTAAAIBhxAg +AQ24AAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQAQLo2RzC07dG9p+J3A +W6C0p3Y+Os/YE2D9wfp4TIDTZxcRUQZ0S6ahF1N6sp8l9KHBJHPU1cUpRAU1oD+Y +SqmnP/VJRRDTTj9Ytdc/Vuo2jeLpSYhVKrCqtjqIrCwYJFoYRmMoxTtJGlwA0hSd +kwo3XYrALPUQWUErTYPvNfDNIuUwqUXNfS0CXuIOVN3LJ+shegg6Pwbh9B5T9NHx +kH+HswajhdpdnZIgh0FYTlTCPILDrB49aOWwqLa54AUA6WXa35hPsP8SoqL9Eucq +ifPhBYyadsjOb+70N8GbbAsDPN1jCX9L8RuNcEkxSCKCYx91cWXh7K5KMPuGlzB7 +j8xB +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-cn-and-ip-alt-names.key b/src/test/ssl/ssl/server-cn-and-ip-alt-names.key new file mode 100644 index 0000000000..837eef996d --- /dev/null +++ b/src/test/ssl/ssl/server-cn-and-ip-alt-names.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA6+8IYKAFnZ7V+fDo1cyMpbGBLzCfJOQ/1o2jOGP4+GjpsZgv +6S6UT2MheC8MiiEFrYwdsSIZyYc3jEZrluy/UuR0bCGtqU92BCqa0iBLhvHOgjR5 +88u253eLxQtQ8iJn11QPrKMk35nMkmY8GfHt4sGFbvBL6+GpipHq7a6cde3Z+v4k +CB5dKMYDUDtm3mJmviuGNAu5wOqItk2Yi5dwJs1054007KNH0Il43urxiOfnkLS0 +cG5kehboPf86vxBt3iHByrU/9/DY5IvQCfSXVNa6rb5w5/pGja9aCei6Mv1jQY/V +8SMQTga+MOsA0WB9akxMi2NxwS2+BQ4k/McPlwIDAQABAoIBAQCuNFKVNdKvrUYF +RLJGmsAG3+eo9lern7TbML2ht39vu9dBwEMwA6qSa3mdCfBSVUuh9uE9lxY/TU3g +j2aFi81A4VptNPjLGNblAKhMGnhp7UUzspeRQYuNoSFcnpxoDKtrvK/OIq/pQeBh +AIfECHRDh+yEG32Tb44FuPQkB1eTYl8xbMEImrhNUaSjJk7tTsmydHy0DjmqHVKX +HUj0TREfDBDOBiHtY0XV6Pu3bnqDH/TKLTfUf3UdfTuay3Yai9aEcRPWp9GrMO7G +axsKCifTz6177gyr6Fv8HLeMZMh9rMZRn3e0zfaF6vrH1QnZZOts5jpUa0KugSCd +//uC0iNxAoGBAPXVc3b+o3hY5gcwwpaW6JtsarDrmNRxrizqIDG7NgpqwdFXgTi6 +6q0t2pjv81ATqij69IcPkNSissyR4OEKnu/OFJWzreg8yLi75WHKi0E/6msHpwRk +d1yP0Zgd05ots/yOjDSp593RagaPVvHBxMECZ/Tm3B+Tq55Azudd/zvLAoGBAPWw +xf0oUEJl6NdUZD6K7eFc6jf8yrpD85dldeko6LeN8x0XlKKWvUDJ2+3oizXoQvCm +8by6KOYEIo4MrtXuy9MmtPWfNvRBr+hsUHchIj7IgFa9bKXyK2FnJqu/8CbEymli +eZu7hoOhelurhnFy1zSqwNO4GC+kw60Y/BO3Z1nlAoGAVOyYJtNwxXJwhKtjjYI0 +ePzLHrNE6J8c/Ick+AkkchTPP/JqwZ5Q0+KzUYITG+avMdkAAGhwMATEn8cFWLjC +jzUyB0U7Hq9g5/CBHXdLBA+Ae9j46ZuLYH6OeW5UWz7OnsDfzpGjeA2QAxQhhQLb +ZZHfN8tI39+zucfJskPWmGECgYEAg9guF1Fn6InJrqwR82IYj6SN6CeXHufSM392 +C/4xDDd3rDf4QlwECV2J0RzGf9I5Ae2EshNwWScE6Be0RweTh6cw2tJq6h7J6D8f +2x4Dw49TF7klMdRIJUf2f5pLpHJccLswqTqzz7V69PCSABVxmUi8m6EiEYconp5W +v7nfE2UCgYALrEqzncuSIX3q6TVAjnzT7gO4h8h2TUekIWdHQFldFx8R7Kncggnd +48gQqhewchNR83UCcd7pPsCcTqu6UR1QRdq/DV5P6J3xdZ2iS/2gCM6hvWIvKZEv +/ClnkyFCOW7zX6RKIXtRYZTV1kz3TajApi34RTIeIMTieaCarnBJbA== +-----END RSA PRIVATE KEY----- diff --git a/src/test/ssl/ssl/server-ip-alt-names.crt b/src/test/ssl/ssl/server-ip-alt-names.crt new file mode 100644 index 0000000000..8a1bc620bb --- /dev/null +++ b/src/test/ssl/ssl/server-ip-alt-names.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIIICERKREEUAAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl +cnZlciBjZXJ0czAeFw0yMTExMjkxOTA0NTBaFw00OTA0MTYxOTA0NTBaMCAxHjAc +BgNVBAsMFVBvc3RncmVTUUwgdGVzdCBzdWl0ZTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAOM8yB6aVWb17ujr3ayU62mxHQoqn4CvG9yXlJvGOGv/ursW +Vs0UYJdc96LsNZN1szdm9ayNzCIw3eja+ULsjxCi6+3LM4pO76IORL/XFamlTPYb +BZ4pHdZVB0nnZAAnWCZPyXdnjOKQ5+8unVXkfibkjj8UELBJ2snehsOa+CTkOBez +zxYMqxAgbywLIYsW448brun7UXpWmqbGK+SsdGaIZ5Sb7Zezc5lt6CrLemTZTHHK +7l4WZFCCEi4t3sgO8o1vDELD/IE5G8lyXvIdgJg6t8ssper7iCw6S8x+okhjiSjT +vDLU2g4AanqZRZB49aPwTo0QUcJA2BCJxL9xLy8CAwEAAaMlMCMwIQYDVR0RBBow +GIcEwAACAYcQIAENuAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAwZJ+ +8KpABTlMEgKnHIYb35ItGhtFiTLQta9RkXx7vaeDwpOdPP/IvuvpjpQZkobRgBsk +bNM0KuJpd2mSTphQAt6eKQIdcPrkzvc/Yh9OK3YNLUAbu/ZhBUnBvFnUL4wn2f1U +mfO+m8P/LxybwqKx7r1mbaB+tP3RTxxLcIMvm9ECPQEoBntfEL325Wdoj+WuQH5Y +IvcM6FaCTkQsNIPbaBD5l5MhMLHRULZujbDjXqGSvRMQfns6np/biMjNdQA8NZ5z +STeUFvkQbCxoA0YYLgoSHL5KhZjXrg2g+T+2TUyCTR/91xf9OoOjBZdixR0S0DzJ +B1+5vnUjZaCfnSEA7A== +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-ip-alt-names.key b/src/test/ssl/ssl/server-ip-alt-names.key new file mode 100644 index 0000000000..b210b3a991 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-alt-names.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA4zzIHppVZvXu6OvdrJTrabEdCiqfgK8b3JeUm8Y4a/+6uxZW +zRRgl1z3ouw1k3WzN2b1rI3MIjDd6Nr5QuyPEKLr7cszik7vog5Ev9cVqaVM9hsF +nikd1lUHSedkACdYJk/Jd2eM4pDn7y6dVeR+JuSOPxQQsEnayd6Gw5r4JOQ4F7PP +FgyrECBvLAshixbjjxuu6ftRelaapsYr5Kx0ZohnlJvtl7NzmW3oKst6ZNlMccru +XhZkUIISLi3eyA7yjW8MQsP8gTkbyXJe8h2AmDq3yyyl6vuILDpLzH6iSGOJKNO8 +MtTaDgBqeplFkHj1o/BOjRBRwkDYEInEv3EvLwIDAQABAoIBACp3uY6+mSdc3wF4 +0zzlt/lQuHSl8plCIJrhWUyjhvfoGyXLzv0Uydh/72frbTfZz1yTSWauOXBKYa6a +/eqb+0DIsf8G8uLuTaqjsAWKVOoXkoKMGkistn7P9UTCkdXVhIvkbWp7V8EgA7iX +pZ/fzBPIsyzmuxe3NcR0ags0cxuxkNuu+YXDv1oTedmT2wS3CZq1d/T1Y/EOVIf8 +Iznd2aOverlsnt6iiQ3ZWdG/W5F8FhnrR/rrBdYsdCv6TH/KUYexnDOUYpayjDbu +oAKnifPp6UqiOM4SuBL83OAz19jptp5vpF370BEVRs3eK0q+zo/mETjv9HsXdolZ +lfoXA0ECgYEA/7nb2azbq/2muvXCh1ZxCEbn3mt8KXoJP/xkx/v9eEc/cc5Q9e0V +2oGfjC2hSE+bjOWMwiUMD6uU+iRjhz5A3IvUxnoSdoL7H9p0hTqLMyP7dTDkoVF5 +aEuLMaiI5YEnfAFu9L5h8ZKieoQTBoscT06wnGjh9pBV9bthfTKA7ksCgYEA43sb +55m9WL4kWCPwOAp3vdEAFyxzmZHlO26sEQOU/m5aN01pumYybBruziEXMI96yfTj +VmXKReeYb6XUiCcs3fLSipD/+8/8CsjO4uMORtxWumXe8AbKZfysGFzL7wJlByGT +38AGQwIG/XD8cKnaiEMX4E/3Owbcoxwixo3WZC0CgYEAovaqJ9mEU+Jc8h/TS7PG +bGPjN1Z/1V6zrlcFUnw/Vvrwb3HvHglsN8cLCaW6df5lPjC6tq4tNX8+fPnbg0Ak +zWc+vQzl3ygxKGdqgcyBEKIJiPETgcoN+GzL02V3d+oKY3f2YXlBqVSsvi6UgUL9 +U3zuB36/IQVyAhrbUZFxoGkCgYEAnaFAO+Nvrp/LhXwZyGuQf+rkmipGTIMpil5t +QzjtNMV5JFszSWPpyrl7A0Ew1YiG+I0GP2c3m+sY2TzbIiGrWH0b4cMKbw63Qy3V +FqlpyjaCrpVKv56k/7jv883RzuQk56Uf1+szK5mrCFITy2oXsVZ0pA4lbjSaDTjA +7D968V0CgYEA+qKqXKL98+c5CMPnpf+0B1x2zgyUym1ouPfon2x5fhK84T53zDMA +zfdUJ/SOZw6/c9vRF7RL8h+ZfFdIyoAXv4Tt6mIiZe7P+AUVg6XgJ0ce2MUSeWjI +W8D4WdSi0jyqr99TuVBWhbTZJviMB3pHqKaHQ07hnd/lPtvzsiH12qk= +-----END RSA PRIVATE KEY----- diff --git a/src/test/ssl/ssl/server-ip-cn-and-alt-names.crt b/src/test/ssl/ssl/server-ip-cn-and-alt-names.crt new file mode 100644 index 0000000000..2be02feb03 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-cn-and-alt-names.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHTCCAgWgAwIBAgIIICIBBBQ2MQAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl +cnZlciBjZXJ0czAeFw0yMjAxMDQyMjM2MzFaFw00OTA1MjIyMjM2MzFaMDQxHjAc +BgNVBAsMFVBvc3RncmVTUUwgdGVzdCBzdWl0ZTESMBAGA1UEAwwJMTkyLjAuMi4x +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwmqTdQJfs2Ti9tPitYp2 +27I0HvL/kNSgA6egFr0foRo0BorwJNIzdbV0+EnsfiBNTWL5It26gqO7UP3ms8t2 +vHD5gkXfT+f6ts0lVJEcIOkUD/8ws4Ic9Y4uPqb4gN+pUKqcxtmLW1TYk84MBK59 +Xz4yPPS6N+G/DMMeFHTNkM9EQwn/+DC3fDsWdGYM2GRWDTJGg1A5tSUcF+seu7i1 +Vg7XajBfsvgAUAsrAxV+X/sLZh94HY+paD6wfaI99mY2OXVc/XW/z1r9WQznor65 +ZkonNCaPfavqPG5vqnab9AyQcqPqmX8hf/xrniASBAkqNCctbASrFCIYvCJfGfmX +EQIDAQABoyUwIzAhBgNVHREEGjAYhwTAAAIChxAgAQ24AAAAAAAAAAAAAAABMA0G +CSqGSIb3DQEBCwUAA4IBAQBf7kmYfRYfnWk1OUfY3N1kaNg9piBBlFr9g+OQn9KU +zirkN7s0ZQbCGxV1uJQBKS58NyE414Vorau77379emgYDcCBpDIYpkLiNujVrIOr +ggRFKsFRgxu4/mw0BSgCcV8RPe9SWHZ90Mos7TMCnW/PdxOCD1wD0YMkcs0rwB3l +0Kzc7jDnfOEvmgw/Ysm7v67ps+05Uq5VskQ6WrpSAw6kPD/QMuuBAX8ATPczIaox +zAMyncq1IiSIwG93f3EoQQThdQ70C6G9vLcu9TtL6JAsEMFEzR99gt1Wsqvmgl9W +kStzj1yjIWeo5gIsa4Jgcke1lZviWyrTxHDfyunYE5i5 +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-ip-cn-and-alt-names.key b/src/test/ssl/ssl/server-ip-cn-and-alt-names.key new file mode 100644 index 0000000000..54fe80fc68 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-cn-and-alt-names.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAwmqTdQJfs2Ti9tPitYp227I0HvL/kNSgA6egFr0foRo0Borw +JNIzdbV0+EnsfiBNTWL5It26gqO7UP3ms8t2vHD5gkXfT+f6ts0lVJEcIOkUD/8w +s4Ic9Y4uPqb4gN+pUKqcxtmLW1TYk84MBK59Xz4yPPS6N+G/DMMeFHTNkM9EQwn/ ++DC3fDsWdGYM2GRWDTJGg1A5tSUcF+seu7i1Vg7XajBfsvgAUAsrAxV+X/sLZh94 +HY+paD6wfaI99mY2OXVc/XW/z1r9WQznor65ZkonNCaPfavqPG5vqnab9AyQcqPq +mX8hf/xrniASBAkqNCctbASrFCIYvCJfGfmXEQIDAQABAoIBAB6GgVST5NbT9lbu ++d+rN/JSzqA1Yy8oU19/iEFJvJec96I3WnFNl8rZjN4XLUy4YarO6XMyAUDV2Gll +FD4Sqjf4PRTZR7DSKaleGIhoqFP6hK3mUY091rIves9XhBkoBPunbipCqgDTF5ZN +edGaXBECQP0VJ8/yX/7u++AWXthnjDis9X0taZfFg/PYbV7SCJ1Hg1O/wEsgXlnC +7mbL6wkCW0f6700B0x1kKbZqJY95xRqp6Ipq2lIQbJDdGywoj0WzKqNltf9cer+r +cXl8WjeiMvvvpl4uGhckAbzUifUzxN6A3f1fu/XKtOmabMi9t7J4MRfgOgedgtQB +0jaZGSkCgYEA+lBLnNY6M48HX2mdtr86+n41gh69v8Z7oNikJFDZkodrvI8uqE0i +0XwnYPFddt8NbmuUhhuzI2M8RKhGLgdlbKpkSSVafnMfcxRmX2EAtWQgdvX1Iult +752LWdBgSuw2vlzvy3T/GYnjMrXSCGput4amqojMEbvUGvIdSUMdHGMCgYEAxtU1 +WixKPL6aEnYy1f4bybzcNgGtl8PBRz9xw+P46g+ijOPoaG9O73Tr7An11AO003Ot +DHhMW+b8yHLyxoKwS2sU2cN/lKB8xNQYZc1D61RNJlzgnHMXnA0lcH0I3M35fqKr +/71pD1ZP40SSJS+od/KEjW80XzuOdyiXg8q81vsCgYEAnUPLbbsuj+whzrFVlFZr +IKwgxCK6Rn3WeIUEA4kEWUpZxvsSbk0gPgtJ1l9uwFt9Xc2bX/KRRv93Aw/SH+Mn +tvEK1uXwCBgePzgm5W/VeSFyQCthm1CbcHtD7Oa9SPVFo65SPjrAd3QpWVfgoMb1 +zrp7hhMyW0XuCgvpmHjhFk8CgYEAxq/thXM2p+bLLWGhwQcRG5G299zLbBl4PUsf +0uEvLi17gJCKADoiRdSvoAn/9eHSQ26XYRuhKkDzHxcGlOmpY2PYzRa3mXyZ0VIk +Iy5wDWwLQCeVZ6D22cClRfgb8BF/nFTPzVmn72SPpgoyhChQj7PvUynpyrRH07jj +VxYziBsCgYAFr37Xbl0VnXVK+XU+vMwUZjcF4jpoCr7SFZqgRbW2GbYSUoMuPXns +RnJh+Fvi1NUei+E5s1H4P1pVq4p0jFxP4GvH/qvNjnIn/Er3bbqvpox6dWUJXprq +qTQSDIeoDC/V8cyRoIfqPvTVqY8Rgew6GEkv0bAImdxhoSng7vIseg== +-----END RSA PRIVATE KEY----- diff --git a/src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.crt b/src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.crt new file mode 100644 index 0000000000..23c06da01c --- /dev/null +++ b/src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQzCCAiugAwIBAgIIICIBBBQ2MQEwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl +cnZlciBjZXJ0czAeFw0yMjAxMDQyMjM2MzFaFw00OTA1MjIyMjM2MzFaMDQxHjAc +BgNVBAsMFVBvc3RncmVTUUwgdGVzdCBzdWl0ZTESMBAGA1UEAwwJMTkyLjAuMi4x +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8xddbo/x2TOSIa/br8BN +o/URdTr9+l2R5YojiZKDuLxiQVkgC30PJ2/CNFKIh2nHhRrzknI6sETVtrxZ+9V2 +qRc1yShVu462u0DHPRMIZnZIOZg3hlNB0cRWbOglUKlttIARNEQUcTUyPOtyo4/v ++u0Ej5NTNcHFbFT01vdD9MjQiCO3jKdAwPIb14jTg4C71EpZ+LuelDo4DzF2/XgG +WqUTrgD/XnBU/60PU9Iy3G0nVpx21q6ppn9G7a9R+i8FjBcwW1T+cfsBDWhAv+bi +RmSAkENf8L8TwOlDQUwROkfz3Hz36vuJjdkreQJsiqL0HnrnH5T5G9UzJO86FvZQ +5wIDAQABo0swSTBHBgNVHREEQDA+gh1kbnMxLmFsdC1uYW1lLnBnLXNzbHRlc3Qu +dGVzdIIdZG5zMi5hbHQtbmFtZS5wZy1zc2x0ZXN0LnRlc3QwDQYJKoZIhvcNAQEL +BQADggEBAF+mfaw6iBPzpCgqq830pHRa3Yzm1aezt8SkeRohUYHNv/yCnDSRaqtj +xbENih3lJMSTBL3g0wtTOHfH8ViC/h+lvYELHzXKic7gkjV7H5XETKGr0ZsjBBT2 +4cZQKbD9e0x0HrENXMYgGpBf747qL6uTOVJdG0s15hwpLq47bY5WUjXathejbpxW +prmF8F+xaC52N9P/1VnqguQB909F4x1pyOK7D7tjFu+Y8Je7PHKbb6WY5K6xAv6t +R17CY0749/FotlphquElUR2bs5Zzv5YrjUHPTcbwKvcH5cdNi93/u6NJt2xNAoYf +aZERhX5TA9DYk4gC8OY0yGaYCIj3Dd4= +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.key b/src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.key new file mode 100644 index 0000000000..0ace41e0a1 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-cn-and-dns-alt-names.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA8xddbo/x2TOSIa/br8BNo/URdTr9+l2R5YojiZKDuLxiQVkg +C30PJ2/CNFKIh2nHhRrzknI6sETVtrxZ+9V2qRc1yShVu462u0DHPRMIZnZIOZg3 +hlNB0cRWbOglUKlttIARNEQUcTUyPOtyo4/v+u0Ej5NTNcHFbFT01vdD9MjQiCO3 +jKdAwPIb14jTg4C71EpZ+LuelDo4DzF2/XgGWqUTrgD/XnBU/60PU9Iy3G0nVpx2 +1q6ppn9G7a9R+i8FjBcwW1T+cfsBDWhAv+biRmSAkENf8L8TwOlDQUwROkfz3Hz3 +6vuJjdkreQJsiqL0HnrnH5T5G9UzJO86FvZQ5wIDAQABAoIBAGv0BFoFMrHyZQLw +xe7Wx6P4QTh+aiu1QgVdw0pk9nojrr62hbSUZRZuWyBBRsBcCW7i+Sgf8lA1QXNV +UeC0e228EPa0It6YEi42JkTJHwHhpVFud7n/X0t4lajnryqTE1UFSp6bXTipFxZW +uSJJ2ZjliRD5rApDcxkY4WJVjKg3aEt7P/DiM8iKGfyE6stq72VjEbJjdViMEcOP +BNf0TiREZz5Mp7jAVWhpen0ebbLOBVWV4/ONNcL+yqR4mCEDUSFGewrTVX4zHL0A +hYk198C5F8sFvEDnFkPco9sXMVanmLoI8sbhP4IIz9g4+GU6kFuj7fUKp11Azqv+ +3WQDKYECgYEA/XG4mmG/g8FG44y42mfZpUXWi1pwU4CQIrhkoU5j7EPQrvRboOOE +Rv95jSwyZu4vCqjyI5FN1jCGTdhmt++R1e//zH6Hqa9Smo+jw7DtAFrCYd1JnCf1 +ToOwsYPHv4P7A8q8kc5vCNIv+AQSlP/wqdVNo3grdf7cGXkMtEY4F9UCgYEA9Yrq +zWdnNGPATuSBqL6TSjQ37oR+dBD6WnGsiDenQkOzyDPFZ3CT1DjJghjEtxc8EfNf +Oo8dMMR2q+5FZQo7WuqONEgyzKePiNR8RK2gOYpgdjN9bih1sAhHR10D26cpwlDJ +bx7D5ZzENLbdZmfEiWwKswnaIhN4yMalgE0mP8sCgYAhzJy12ftUct4lUosEdX0N +EXc/NlxshmSyfKzO5kllJNYbvvLJTg5B+agYL6C5IWKcpVNFcwdSXT5L+2QXe5eT +VGJkvysQchUuD6HjYyD4PyJVMtGyRZHtWpqh0dU9sTg0lUD4oPMl1gIXrVNdE5Tg +0VV9S3VgUxC/ROlw0TyB0QKBgGsVE0NS9hJF8mc1hko2GnwA++d8Rr2NbfElo+2f +/8SJTA1ibpOm6AFkZpTjAl8qtdrKPVyHb16GP47Jkd/3r1z979hjKCxSYul0aWF2 +KusNKvZBjFEPOgv0AEniCb2wUCjbHI3mZ95qGLM4kKOJW4/m21+rS0MTJNjCsQic +HLMzAoGAeCsY09d3m8xGeU+DuTPC6GH7Sgy/NBYqS5VaVNjb2jnuZlW2SSW2oiID +4tXTi4ruKmHC898BfyFxhSMqub+tg3pVqIYADC71rnJLrVyc1SzoWzL7yMT3qFj7 +C7ZYZYmfG9agcZb5NkqKPTfCxkBhWbdgTTgBKVO/xQst8EUgko8= +-----END RSA PRIVATE KEY----- diff --git a/src/test/ssl/ssl/server-ip-cn-only.crt b/src/test/ssl/ssl/server-ip-cn-only.crt new file mode 100644 index 0000000000..9bf015cf18 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-cn-only.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCAdkCCCAhESkRN1IAMA0GCSqGSIb3DQEBCwUAMEIxQDA+BgNVBAMMN1Rl +c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzZXJ2ZXIg +Y2VydHMwHhcNMjExMTI5MTkzNzUyWhcNNDkwNDE2MTkzNzUyWjA0MR4wHAYDVQQL +DBVQb3N0Z3JlU1FMIHRlc3Qgc3VpdGUxEjAQBgNVBAMMCTE5Mi4wLjIuMTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANWs1uUL71nHYF9Zj6p+M3MpYDvx +32iCjVdtH5a2qpSWHXTg0rR8dLX0y92cvOYvMXHRajZT1avpHr8dooPYSVaXpGMK +NvF/Qi+WFYovRbP2vmd1yv1cgW/FggbwJFWVobizIz4seyA4d0B2j9fqoi2OFBNP +huW664SjF0u3p21tDy+43i2LNUMAKf6dnRR5Vqenath87LEU41tSLudu6NXgbFMk +jvfNkl4d0w7YCzeXmklmSI+uaX3PlJJ4NzQO2j8w5BvnKVhNVD0KjgrXZ6nB/8F7 +Pg3XY+d7rJlwRgXemU6resWQDJ7+UaC9u7I4EIP+9lzCR/nNBqUktpHRmHUCAwEA +ATANBgkqhkiG9w0BAQsFAAOCAQEAos1JncV8Yf4UaKl6h1GdYtcVtzFyJvBEnhRD +07ldL+TYnfZiX8wK2ssBtM3cg/C78y5bzdUa5XGS83ZKQJFFdhE7PSnrvyNqyIqY +ZgNBxto3gyvir+EjO1u9BAB0NP3r3gYoHRDZS1xOPPzt4WgjuUgTLM9k82GsqAbO +UrOTOdRnkIqC5xLpa05EnRyJPRsR1w1PRJC2XXKnHIuFjMb4v7UuPwyCcX1P5ioc +rQszQcORy/L+k0ezCkyweORg68htjYbBHuwOuiGfok6yKKDMzrTvD3lIslls6eX7 +4sI3XWqzkPmG9Vsxm9Vu9/Ma+PRO76VyCoIwBd+Ufg5vNXhMmw== +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-ip-cn-only.key b/src/test/ssl/ssl/server-ip-cn-only.key new file mode 100644 index 0000000000..1966530e72 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-cn-only.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA1azW5QvvWcdgX1mPqn4zcylgO/HfaIKNV20flraqlJYddODS +tHx0tfTL3Zy85i8xcdFqNlPVq+kevx2ig9hJVpekYwo28X9CL5YVii9Fs/a+Z3XK +/VyBb8WCBvAkVZWhuLMjPix7IDh3QHaP1+qiLY4UE0+G5brrhKMXS7enbW0PL7je +LYs1QwAp/p2dFHlWp6dq2HzssRTjW1Iu527o1eBsUySO982SXh3TDtgLN5eaSWZI +j65pfc+Ukng3NA7aPzDkG+cpWE1UPQqOCtdnqcH/wXs+Dddj53usmXBGBd6ZTqt6 +xZAMnv5RoL27sjgQg/72XMJH+c0GpSS2kdGYdQIDAQABAoIBAQDNXviU4WnF8rmQ +K7bH+dBdqbETLKC8BG7xTrMD2sINWlMpmUUrsEtE7+paMGHnJAj0CoF5gg5m0wN4 +UXV4H5QtpEad4p14dAYbUreVP2ZRWKEdM7xM1HKcCUu2e22QzObJbXQ8N+iHyX3k ++Y+7yYrjGiH1hYR0nbnsnAyx++zyYBSQeqzpdQwf/BLY5xZmyYWNfqbckiMpEqMs +EmZmGXnCjIipzEC0LQHoSW9PNa92Z9bvuxOKYl8iHYDDXjvMRFoZBSiMXpzHQocb +QlQ5F4ayfW2OrOhpNbY7niYM9GN3Bk9TgMP+0BkJE6uuktLYW35LY1M78CCPWcWb +npJNK3QBAoGBAOxkGrhAHAysSmtirIyMdvySb76wb/Ukfi+AULKz20FI5j4/GXm9 +qCb2GeT+FFSUHeSC8f0EFnosRYkdBGruqeZioI+5rUkboYFJPspAHAuvg9kgtfF+ +kvphD4O4P/foYsEZRx66FHozDbhrrR5UXc7KzqRIASc/D3FOx2UFJLb1AoGBAOdm +WcaMvYygl9ZW+ThWAR1xG1X70AGKwrlrpF2hBkWYxSurxSMXnD0DUzC9Nb4EyCaM +c2uSqEZOKdW+XfXtK2DnqXKfb3YCVEoGN4gVfyuW/vxii/+ZxLo3md/b3vrkZEVp +pfkXy/HoZ71YN7bNpcDpOnhml6vvuCRCYFnI1WuBAoGAC0shB6pwbJ6Sk5zMN47C +ZICufAK75o9OxAAyWsdC81SDQ3gKRImuDeZ2CD2nRP8qim9DFl5qoH2a+Nj9DArI +7SvLFfK9958tURrpuAnmDRzehLIOXzI33WRjtFxKGhLtHOKTRkGHlur3fdcPF0La +lHWV971E6NYXa8diuU3Mmj0CgYBYd+ka3/QYL83dRKNDxp3mg7fPx9ZewI5yFZVh +to6PTTkU2Tclk4FIUl0b5TsGyw06r7fxCMENIBUegwmpXGOZSPifuhUDKSDQrE/O +12knYTNbitG7hy6Pg3JxA77cbTVo1FuAQHjYo+IFohSq7zTP7FtObOrP8XaVZksw +CHiQAQKBgBW4EiA9AAnZ1LOpifAvM7bs0NHg95qTwtAL52WKom2ga2H+lMhxeu6Y +hUSytC/f9kALVcYloZhkLYpO07x1gXmy7f4parMjA4Ex+4vfu3kPd8GiNGZ+AUJD +nnJ1OINY9ziXJZfju7FpVWpkiuPzWCh6y/o3gZ/veq5mIUxuDMVa +-----END RSA PRIVATE KEY----- diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk index 7ed3a30f5c..f9be5ffdba 100644 --- a/src/test/ssl/sslfiles.mk +++ b/src/test/ssl/sslfiles.mk @@ -22,9 +22,14 @@ # key/certificate pair will be generated for you, signed by the appropriate CA. # SERVERS := server-cn-and-alt-names \ + server-cn-and-ip-alt-names \ server-cn-only \ + server-ip-cn-only \ + server-ip-cn-and-alt-names \ + server-ip-cn-and-dns-alt-names \ server-single-alt-name \ server-multiple-alt-names \ + server-ip-alt-names \ server-no-names \ server-revoked CLIENTS := client client-dn client-revoked client_ext diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index b8f8b65a8f..11811d5794 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -249,6 +249,23 @@ $node->connect_fails( qr/\Qserver certificate for "common-name.pg-ssltest.test" does not match host name "wronghost.test"\E/ ); +# Test with an IP address in the Common Name. This is a strange corner case that +# nevertheless is supported, as long as the address string matches exactly. +switch_server_cert($node, 'server-ip-cn-only'); + +$common_connstr = + "user=ssltestuser dbname=trustdb sslcert=invalid sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR sslmode=verify-full"; + +$node->connect_ok("$common_connstr host=192.0.2.1", + "IP address in the Common Name"); + +$node->connect_fails( + "$common_connstr host=192.000.002.001", + "mismatch between host name and server certificate IP address", + expected_stderr => + qr/\Qserver certificate for "192.0.2.1" does not match host name "192.000.002.001"\E/ +); + # Test Subject Alternative Names. switch_server_cert($node, 'server-multiple-alt-names'); @@ -301,7 +318,54 @@ $node->connect_fails( qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/ ); -# Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN +# Test certificate with IP addresses in the SANs. +switch_server_cert($node, 'server-ip-alt-names'); + +$node->connect_ok( + "$common_connstr host=192.0.2.1", + "host matching an IPv4 address (Subject Alternative Name 1)"); + +$node->connect_ok( + "$common_connstr host=192.000.002.001", + "host matching an IPv4 address in alternate form (Subject Alternative Name 1)"); + +$node->connect_fails( + "$common_connstr host=192.0.2.2", + "host not matching an IPv4 address (Subject Alternative Name 1)", + expected_stderr => + qr/\Qserver certificate for "192.0.2.1" (and 1 other name) does not match host name "192.0.2.2"\E/ +); + +$node->connect_fails( + "$common_connstr host=192.0.2.1/32", + "IPv4 host with CIDR mask does not match", + expected_stderr => + qr/\Qserver certificate for "192.0.2.1" (and 1 other name) does not match host name "192.0.2.1\/32"\E/ +); + +$node->connect_ok( + "$common_connstr host=2001:DB8::1", + "host matching an IPv6 address (Subject Alternative Name 2)"); + +$node->connect_ok( + "$common_connstr host=2001:db8:0:0:0:0:0:1", + "host matching an IPv6 address in alternate form (Subject Alternative Name 2)"); + +$node->connect_fails( + "$common_connstr host=::1", + "host not matching an IPv6 address (Subject Alternative Name 2)", + expected_stderr => + qr/\Qserver certificate for "192.0.2.1" (and 1 other name) does not match host name "::1"\E/ +); + +$node->connect_fails( + "$common_connstr host=2001:DB8::1/128", + "IPv6 host with CIDR mask does not match", + expected_stderr => + qr/\Qserver certificate for "192.0.2.1" (and 1 other name) does not match host name "2001:DB8::1\/128"\E/ +); + +# Test server certificate with a CN and DNS SANs. Per RFCs 2818 and 6125, the CN # should be ignored when the certificate has both. switch_server_cert($node, 'server-cn-and-alt-names'); @@ -319,6 +383,39 @@ $node->connect_fails( qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 1 other name) does not match host name "common-name.pg-ssltest.test"\E/ ); +# But we will fall back to check the CN if the SANs contain only IP addresses. +switch_server_cert($node, 'server-cn-and-ip-alt-names'); + +$node->connect_ok("$common_connstr host=common-name.pg-ssltest.test", + "certificate with both a CN and IP SANs matches CN"); +$node->connect_ok("$common_connstr host=192.0.2.1", + "certificate with both a CN and IP SANs matches SAN 1"); +$node->connect_ok("$common_connstr host=2001:db8::1", + "certificate with both a CN and IP SANs matches SAN 2"); + +# And now the same tests, but with IP addresses and DNS names swapped. +switch_server_cert($node, 'server-ip-cn-and-alt-names'); + +$node->connect_ok("$common_connstr host=192.0.2.2", + "certificate with both an IP CN and IP SANs 1"); +$node->connect_ok("$common_connstr host=2001:db8::1", + "certificate with both an IP CN and IP SANs 2"); +$node->connect_fails( + "$common_connstr host=192.0.2.1", + "certificate with both an IP CN and IP SANs ignores CN", + expected_stderr => + qr/\Qserver certificate for "192.0.2.2" (and 1 other name) does not match host name "192.0.2.1"\E/ +); + +switch_server_cert($node, 'server-ip-cn-and-dns-alt-names'); + +$node->connect_ok("$common_connstr host=192.0.2.1", + "certificate with both an IP CN and DNS SANs matches CN"); +$node->connect_ok("$common_connstr host=dns1.alt-name.pg-ssltest.test", + "certificate with both an IP CN and DNS SANs matches SAN 1"); +$node->connect_ok("$common_connstr host=dns2.alt-name.pg-ssltest.test", + "certificate with both an IP CN and DNS SANs matches SAN 2"); + # Finally, test a server certificate that has no CN or SANs. Of course, that's # not a very sensible certificate, but libpq should handle it gracefully. switch_server_cert($node, 'server-no-names'); -- 2.25.1
From 334f43ac1a9af0c4d89bc40022eb50606ae1b37d Mon Sep 17 00:00:00 2001 From: Jacob Champion <pchamp...@vmware.com> Date: Wed, 5 Jan 2022 15:47:03 -0800 Subject: [PATCH v6 3/3] squash! libpq: allow IP address SANs in server certs Per review, provide a pg_inet_pton() interface for IPv6 and refactor the internals accordingly. IPv4 is not yet implemented. The call sites that just want standard addresses without a CIDR mask have been updated to use the new function. Internally, the IPv6 parser now takes an allow_cidr boolean. I've plumbed that all the way down to getbits() in order to minimize the amount of code touched, at the expense of an unnecessary function call in the failing case. --- src/include/port.h | 1 + src/interfaces/libpq/fe-secure-common.c | 7 +-- src/interfaces/libpq/fe-secure-openssl.c | 2 +- src/port/inet_net_pton.c | 66 ++++++++++++++++++------ 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/include/port.h b/src/include/port.h index 2852e5b58b..fd046f4c24 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -517,6 +517,7 @@ extern char *pg_inet_net_ntop(int af, const void *src, int bits, /* port/inet_net_pton.c */ extern int pg_inet_net_pton(int af, const char *src, void *dst, size_t size); +extern int pg_inet_pton(int af, const char *src, void *dst); /* port/pg_strong_random.c */ extern void pg_strong_random_init(void); diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c index 2b8f2d6b24..4d78715756 100644 --- a/src/interfaces/libpq/fe-secure-common.c +++ b/src/interfaces/libpq/fe-secure-common.c @@ -211,12 +211,7 @@ pq_verify_peer_name_matches_certificate_ip(PGconn *conn, family = PGSQL_AF_INET6; - /* - * 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 (pg_inet_pton(PGSQL_AF_INET6, host, addr) == 1) { if (memcmp(ipdata, addr, iplen) == 0) match = 1; diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index 108def3c1b..6339f4540e 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -554,7 +554,7 @@ is_ip_address(const char *host) unsigned char dummy6[16]; return inet_aton(host, &dummy4) - || (pg_inet_net_pton(PGSQL_AF_INET6, host, dummy6, -1) == 128); + || (pg_inet_pton(PGSQL_AF_INET6, host, dummy6) == 1); } /* diff --git a/src/port/inet_net_pton.c b/src/port/inet_net_pton.c index 5bae811c6f..ae0d1fd2e3 100644 --- a/src/port/inet_net_pton.c +++ b/src/port/inet_net_pton.c @@ -42,8 +42,8 @@ static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 m static int inet_net_pton_ipv4(const char *src, u_char *dst); static int inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size); -static int inet_net_pton_ipv6(const char *src, u_char *dst); -static int inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size); +static int inet_pton_ipv6_internal(const char *src, u_char *dst, size_t size, + bool allow_cidr); /* @@ -74,9 +74,9 @@ pg_inet_net_pton(int af, const char *src, void *dst, size_t size) inet_net_pton_ipv4(src, dst) : inet_cidr_pton_ipv4(src, dst, size); case PGSQL_AF_INET6: - return size == -1 ? - inet_net_pton_ipv6(src, dst) : - inet_cidr_pton_ipv6(src, dst, size); + return inet_pton_ipv6_internal(src, dst, + (size == -1) ? 16 : size, + true /* allow CIDR */); default: errno = EAFNOSUPPORT; return -1; @@ -352,13 +352,22 @@ emsgsize: } static int -getbits(const char *src, int *bitsp) +getbits(const char *src, int *bitsp, bool allow_cidr) { static const char digits[] = "0123456789"; int n; int val; char ch; + if (!allow_cidr) + { + /* + * If we got here, the top-level call is to pg_inet_pton() and the + * caller supplied a CIDR mask, which isn't allowed. + */ + return 0; + } + val = 0; n = 0; while ((ch = *src++) != '\0') @@ -385,7 +394,7 @@ getbits(const char *src, int *bitsp) } static int -getv4(const char *src, u_char *dst, int *bitsp) +getv4(const char *src, u_char *dst, int *bitsp, bool allow_cidr) { static const char digits[] = "0123456789"; u_char *odst = dst; @@ -416,7 +425,7 @@ getv4(const char *src, u_char *dst, int *bitsp) return 0; *dst++ = val; if (ch == '/') - return getbits(src, bitsp); + return getbits(src, bitsp, allow_cidr); val = 0; n = 0; continue; @@ -431,18 +440,12 @@ getv4(const char *src, u_char *dst, int *bitsp) return 1; } -static int -inet_net_pton_ipv6(const char *src, u_char *dst) -{ - return inet_cidr_pton_ipv6(src, dst, 16); -} - #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 #define NS_INADDRSZ 4 static int -inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) +inet_pton_ipv6_internal(const char *src, u_char *dst, size_t size, bool allow_cidr) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; @@ -510,13 +513,13 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) continue; } if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && - getv4(curtok, tp, &bits) > 0) + getv4(curtok, tp, &bits, allow_cidr) > 0) { tp += NS_INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } - if (ch == '/' && getbits(src, &bits) > 0) + if (ch == '/' && getbits(src, &bits, allow_cidr) > 0) break; goto enoent; } @@ -530,6 +533,9 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) if (bits == -1) bits = 128; + /* Without a CIDR mask, the above should guarantee a complete address. */ + Assert(allow_cidr || bits == 128); + endp = tmp + 16; if (colonp != NULL) @@ -568,3 +574,29 @@ emsgsize: errno = EMSGSIZE; return -1; } + +/* + * Converts a network address in presentation format to network format. The main + * difference between this and pg_inet_net_pton() above is that CIDR notation is + * not allowed. + * + * The memory pointed to by dst depends on the address family: + * + * PGSQL_AF_INET: not implemented yet (returns -1 with EAFNOSUPPORT) + * PGSQL_AF_INET6: *dst must be a u_char[16] + * + * Over and above the standard inet_pton() functionality, we also set errno on + * parse failure, with the same meanings as with pg_inet_net_pton(). + */ +int +pg_inet_pton(int af, const char *src, void *dst) +{ + if (af != PGSQL_AF_INET6) + { + /* Currently only IPv6 is implemented. */ + errno = EAFNOSUPPORT; + return -1; + } + + return (inet_pton_ipv6_internal(src, dst, 16, false /* no CIDR */) == 128); +} -- 2.25.1