@@ -72,6 +72,9 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx);
7272static int openssl_verify_peer_name_matches_certificate_name (PGconn * conn ,
7373 ASN1_STRING * name ,
7474 char * * store_name );
75+ static int openssl_verify_peer_name_matches_certificate_ip (PGconn * conn ,
76+ ASN1_OCTET_STRING * addr_entry ,
77+ char * * store_name );
7578static void destroy_ssl_system (void );
7679static int initialize_SSL (PGconn * conn );
7780static PostgresPollingStatusType open_client_SSL (PGconn * );
@@ -509,6 +512,56 @@ openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *nam
509512 return pq_verify_peer_name_matches_certificate_name (conn , (const char * ) namedata , len , store_name );
510513}
511514
515+ /*
516+ * OpenSSL-specific wrapper around
517+ * pq_verify_peer_name_matches_certificate_ip(), converting the
518+ * ASN1_OCTET_STRING into a plain C string.
519+ */
520+ static int
521+ openssl_verify_peer_name_matches_certificate_ip (PGconn * conn ,
522+ ASN1_OCTET_STRING * addr_entry ,
523+ char * * store_name )
524+ {
525+ int len ;
526+ const unsigned char * addrdata ;
527+
528+ /* Should not happen... */
529+ if (addr_entry == NULL )
530+ {
531+ appendPQExpBufferStr (& conn -> errorMessage ,
532+ libpq_gettext ("SSL certificate's address entry is missing\n" ));
533+ return -1 ;
534+ }
535+
536+ /*
537+ * GEN_IPADD is an OCTET STRING containing an IP address in network byte
538+ * order.
539+ */
540+ #ifdef HAVE_ASN1_STRING_GET0_DATA
541+ addrdata = ASN1_STRING_get0_data (addr_entry );
542+ #else
543+ addrdata = ASN1_STRING_data (addr_entry );
544+ #endif
545+ len = ASN1_STRING_length (addr_entry );
546+
547+ return pq_verify_peer_name_matches_certificate_ip (conn , addrdata , len , store_name );
548+ }
549+
550+ static bool
551+ is_ip_address (const char * host )
552+ {
553+ struct in_addr dummy4 ;
554+ #ifdef HAVE_INET_PTON
555+ struct in6_addr dummy6 ;
556+ #endif
557+
558+ return inet_aton (host , & dummy4 )
559+ #ifdef HAVE_INET_PTON
560+ || (inet_pton (AF_INET6 , host , & dummy6 ) == 1 )
561+ #endif
562+ ;
563+ }
564+
512565/*
513566 * Verify that the server certificate matches the hostname we connected to.
514567 *
@@ -522,6 +575,36 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
522575 STACK_OF (GENERAL_NAME ) * peer_san ;
523576 int i ;
524577 int rc = 0 ;
578+ char * host = conn -> connhost [conn -> whichhost ].host ;
579+ int host_type ;
580+ bool check_cn = true;
581+
582+ Assert (host && host [0 ]); /* should be guaranteed by caller */
583+
584+ /*
585+ * We try to match the NSS behavior here, which is a slight departure from
586+ * the spec but seems to make more intuitive sense:
587+ *
588+ * If connhost contains a DNS name, and the certificate's SANs contain any
589+ * dNSName entries, then we'll ignore the Subject Common Name entirely;
590+ * otherwise, we fall back to checking the CN. (This behavior matches the
591+ * RFC.)
592+ *
593+ * If connhost contains an IP address, and the SANs contain iPAddress
594+ * entries, we again ignore the CN. Otherwise, we allow the CN to match,
595+ * EVEN IF there is a dNSName in the SANs. (RFC 6125 prohibits this: "A
596+ * client MUST NOT seek a match for a reference identifier of CN-ID if the
597+ * presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any
598+ * application-specific identifier types supported by the client.")
599+ *
600+ * NOTE: Prior versions of libpq did not consider iPAddress entries at
601+ * all, so this new behavior might break a certificate that has different
602+ * IP addresses in the Subject CN and the SANs.
603+ */
604+ if (is_ip_address (host ))
605+ host_type = GEN_IPADD ;
606+ else
607+ host_type = GEN_DNS ;
525608
526609 /*
527610 * First, get the Subject Alternative Names (SANs) from the certificate,
@@ -537,38 +620,62 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
537620 for (i = 0 ; i < san_len ; i ++ )
538621 {
539622 const GENERAL_NAME * name = sk_GENERAL_NAME_value (peer_san , i );
623+ char * alt_name = NULL ;
540624
541- if (name -> type == GEN_DNS )
625+ if (name -> type == host_type )
542626 {
543- char * alt_name ;
627+ /*
628+ * This SAN is of the same type (IP or DNS) as our host name,
629+ * so don't allow a fallback check of the CN.
630+ */
631+ check_cn = false;
632+ }
544633
634+ if (name -> type == GEN_DNS )
635+ {
545636 (* names_examined )++ ;
546637 rc = openssl_verify_peer_name_matches_certificate_name (conn ,
547638 name -> d .dNSName ,
548639 & alt_name );
640+ }
641+ else if (name -> type == GEN_IPADD )
642+ {
643+ (* names_examined )++ ;
644+ rc = openssl_verify_peer_name_matches_certificate_ip (conn ,
645+ name -> d .iPAddress ,
646+ & alt_name );
647+ }
549648
550- if (alt_name )
551- {
552- if (!* first_name )
553- * first_name = alt_name ;
554- else
555- free (alt_name );
556- }
649+ if (alt_name )
650+ {
651+ if (!* first_name )
652+ * first_name = alt_name ;
653+ else
654+ free (alt_name );
557655 }
656+
558657 if (rc != 0 )
658+ {
659+ /*
660+ * Either we hit an error or a match, and either way we should
661+ * not fall back to the CN.
662+ */
663+ check_cn = false;
559664 break ;
665+ }
560666 }
561667 sk_GENERAL_NAME_pop_free (peer_san , GENERAL_NAME_free );
562668 }
563669
564670 /*
565- * If there is no subjectAltName extension of type dNSName , check the
671+ * If there is no subjectAltName extension of the matching type , check the
566672 * Common Name.
567673 *
568674 * (Per RFC 2818 and RFC 6125, if the subjectAltName extension of type
569- * dNSName is present, the CN must be ignored.)
675+ * dNSName is present, the CN must be ignored. We break this rule if host
676+ * is an IP address; see the comment above.)
570677 */
571- if (* names_examined == 0 )
678+ if (check_cn )
572679 {
573680 X509_NAME * subject_name ;
574681
@@ -581,10 +688,20 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
581688 NID_commonName , -1 );
582689 if (cn_index >= 0 )
583690 {
691+ char * common_name = NULL ;
692+
584693 (* names_examined )++ ;
585694 rc = openssl_verify_peer_name_matches_certificate_name (conn ,
586695 X509_NAME_ENTRY_get_data (X509_NAME_get_entry (subject_name , cn_index )),
587- first_name );
696+ & common_name );
697+
698+ if (common_name )
699+ {
700+ if (!* first_name )
701+ * first_name = common_name ;
702+ else
703+ free (common_name );
704+ }
588705 }
589706 }
590707 }
0 commit comments