diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c new file mode 100644 index fc930bd..a082268 *** a/src/interfaces/libpq/fe-misc.c --- b/src/interfaces/libpq/fe-misc.c *************** static int pqSendSome(PGconn *conn, int *** 64,69 **** --- 64,71 ---- static int pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time); static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time); + static void libpq_bindomain(); + /* * PQlibVersion: return the libpq version number *************** PQenv2encoding(void) *** 1210,1223 **** #ifdef ENABLE_NLS ! char * ! libpq_gettext(const char *msgid) { static bool already_bound = false; if (!already_bound) { ! /* dgettext() preserves errno, but bindtextdomain() doesn't */ #ifdef WIN32 int save_errno = GetLastError(); #else --- 1212,1225 ---- #ifdef ENABLE_NLS ! static void ! libpq_bindomain() { static bool already_bound = false; if (!already_bound) { ! /* bindtextdomain() does not preserve errno */ #ifdef WIN32 int save_errno = GetLastError(); #else *************** libpq_gettext(const char *msgid) *** 1237,1244 **** --- 1239,1258 ---- errno = save_errno; #endif } + } + char * + libpq_gettext(const char *msgid) + { + libpq_bindomain(); return dgettext(PG_TEXTDOMAIN("libpq"), msgid); } + char * + libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) + { + libpq_bindomain(); + return dngettext(PG_TEXTDOMAIN("libpq"), msgid, msgid_plural, n); + } + #endif /* ENABLE_NLS */ diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c new file mode 100644 index f950fc3..c51e8ff *** a/src/interfaces/libpq/fe-secure-openssl.c --- b/src/interfaces/libpq/fe-secure-openssl.c *************** *** 60,68 **** --- 60,72 ---- #ifdef USE_SSL_ENGINE #include #endif + #include static bool verify_peer_name_matches_certificate(PGconn *); static int verify_cb(int ok, X509_STORE_CTX *ctx); + static int verify_peer_name_matches_certificate_name(PGconn *conn, + ASN1_STRING *name, + char **store_name); static void destroy_ssl_system(void); static int initialize_SSL(PGconn *conn); static PostgresPollingStatusType open_client_SSL(PGconn *); *************** wildcard_certificate_match(const char *p *** 473,570 **** /* ! * Verify that common name resolves to peer. */ ! static bool ! verify_peer_name_matches_certificate(PGconn *conn) { - char *peer_cn; - int r; int len; ! bool result; ! /* ! * If told not to verify the peer name, don't do it. Return true ! * indicating that the verification was successful. ! */ ! if (strcmp(conn->sslmode, "verify-full") != 0) ! return true; /* ! * Extract the common name from the certificate. * ! * XXX: Should support alternate names here */ ! /* First find out the name's length and allocate a buffer for it. */ ! len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer), ! NID_commonName, NULL, 0); ! if (len == -1) { printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("could not get server common name from server certificate\n")); ! return false; } ! peer_cn = malloc(len + 1); ! if (peer_cn == NULL) { printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("out of memory\n")); ! return false; } ! r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer), ! NID_commonName, peer_cn, len + 1); ! if (r != len) { ! /* Got different length than on the first call. Shouldn't happen. */ ! printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("could not get server common name from server certificate\n")); ! free(peer_cn); ! return false; } ! peer_cn[len] = '\0'; /* ! * Reject embedded NULLs in certificate common name to prevent attacks ! * like CVE-2009-4034. */ ! if (len != strlen(peer_cn)) { printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("SSL certificate's common name contains embedded null\n")); ! free(peer_cn); return false; } /* ! * We got the peer's common name. Now compare it against the originally ! * given hostname. */ ! if (!(conn->pghost && conn->pghost[0] != '\0')) { ! printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("host name must be specified for a verified SSL connection\n")); ! result = false; } ! else { ! if (pg_strcasecmp(peer_cn, conn->pghost) == 0) ! /* Exact name match */ ! result = true; ! else if (wildcard_certificate_match(peer_cn, conn->pghost)) ! /* Matched wildcard certificate */ ! result = true; else { printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"), ! peer_cn, conn->pghost); ! result = false; } } ! free(peer_cn); ! return result; } #ifdef ENABLE_THREAD_SAFETY --- 477,704 ---- /* ! * Check if a name from a server's certificate matches the peer's hostname. ! * ! * Returns 1 if the name matches, and 0 if it does not. On error, returns ! * -1, and sets the libpq error message. ! * ! * The name extracted from the certificate is returned in *store_name. The ! * caller is responsible for freeing it. */ ! static int ! verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *name_entry, ! char **store_name) { int len; ! char *name; ! unsigned char *namedata; ! int result; ! *store_name = NULL; ! ! /* Should not happen... */ ! if (name_entry == NULL) ! { ! printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("SSL certificate's name entry is missing\n")); ! return -1; ! } /* ! * GEN_DNS can be only IA5String, equivalent to US ASCII. * ! * There is no guarantee the string returned from the certificate is ! * NULL-terminated, so make a copy that is. */ ! namedata = ASN1_STRING_data(name_entry); ! len = ASN1_STRING_length(name_entry); ! name = malloc(len + 1); ! if (name == NULL) { printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("out of memory\n")); ! return -1; } ! memcpy(name, namedata, len); ! name[len] = '\0'; ! ! /* ! * Reject embedded NULLs in certificate common or alternative name to ! * prevent attacks like CVE-2009-4034. ! */ ! if (len != strlen(name)) { + free(name); printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("SSL certificate's name contains embedded null\n")); ! return -1; } ! if (pg_strcasecmp(name, conn->pghost) == 0) { ! /* Exact name match */ ! result = 1; ! } ! else if (wildcard_certificate_match(name, conn->pghost)) ! { ! /* Matched wildcard certificate */ ! result = 1; ! } ! else ! { ! result = 0; } ! ! *store_name = name; ! return result; ! } ! ! /* ! * Verify that the server certificate matches the hostname we connected to. ! * ! * The certificate's Common Name and Subject Alternative Names are considered. ! */ ! static bool ! verify_peer_name_matches_certificate(PGconn *conn) ! { ! int names_examined = 0; ! bool found_match = false; ! bool got_error = false; ! char *first_name = NULL; ! STACK_OF(GENERAL_NAME) *peer_san; ! int i; ! int rc; /* ! * If told not to verify the peer name, don't do it. Return true ! * indicating that the verification was successful. */ ! if (strcmp(conn->sslmode, "verify-full") != 0) ! return true; ! ! /* Check that we have a hostname to compare with. */ ! if (!(conn->pghost && conn->pghost[0] != '\0')) { printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("host name must be specified for a verified SSL connection\n")); return false; } /* ! * First, get the Subject Alternative Names (SANs) from the certificate, ! * and compare them against the originally given hostname. */ ! peer_san = (STACK_OF(GENERAL_NAME) *) ! X509_get_ext_d2i(conn->peer, NID_subject_alt_name, NULL, NULL); ! ! if (peer_san) { ! int san_len = sk_GENERAL_NAME_num(peer_san); ! ! for (i = 0; i < san_len; i++) ! { ! const GENERAL_NAME *name = sk_GENERAL_NAME_value(peer_san, i); ! ! if (name->type == GEN_DNS) ! { ! char *alt_name; ! ! names_examined++; ! rc = verify_peer_name_matches_certificate_name(conn, ! name->d.dNSName, ! &alt_name); ! if (rc == -1) ! got_error = true; ! if (rc == 1) ! found_match = true; ! ! if (alt_name) ! { ! if (!first_name) ! first_name = alt_name; ! else ! free(alt_name); ! } ! } ! if (found_match || got_error) ! break; ! } ! sk_GENERAL_NAME_free(peer_san); } ! ! /* ! * If no match to any of the SANs, check the Common Name. ! */ ! if (!found_match && !got_error) { ! X509_NAME *subject_name; ! ! subject_name = X509_get_subject_name(conn->peer); ! ! if (subject_name != NULL) ! { ! int cn_index; ! ! cn_index = X509_NAME_get_index_by_NID(subject_name, ! NID_commonName, -1); ! if (cn_index >= 0) ! { ! /* ! * We prefer the Common Name over SANs in the error message, ! * if both are present. ! */ ! if (first_name) ! free(first_name); ! ! names_examined++; ! rc = verify_peer_name_matches_certificate_name( ! conn, ! X509_NAME_ENTRY_get_data( ! X509_NAME_get_entry(subject_name, cn_index)), ! &first_name); ! ! if (rc == -1) ! got_error = true; ! else if (rc == 1) ! found_match = true; ! } ! } ! } ! ! if (!found_match && !got_error) ! { ! /* ! * No match. Include the host name from the server certificate in the ! * error message, to aid debugging broken configurations. If there ! * are multiple names, only print the first one to avoid an overly ! * long error message. ! */ ! if (names_examined > 1) ! { ! printfPQExpBuffer(&conn->errorMessage, ! libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name \"%s\"\n", ! "server certificate for \"%s\" (and %d other names) does not match host name \"%s\"\n", ! names_examined - 1), ! first_name, names_examined - 1, conn->pghost); ! } ! else if (names_examined == 1) ! { ! printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"), ! first_name, conn->pghost); ! } else { printfPQExpBuffer(&conn->errorMessage, ! libpq_gettext("could not get server's hostname from server certificate\n")); } } ! /* clean up */ ! if (first_name) ! free(first_name); ! ! return found_match && !got_error; } #ifdef ENABLE_THREAD_SAFETY diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h new file mode 100644 index 6032904..9808ab4 *** a/src/interfaces/libpq/libpq-int.h --- b/src/interfaces/libpq/libpq-int.h *************** extern ssize_t pgtls_write(PGconn *conn, *** 653,660 **** --- 653,664 ---- extern char * libpq_gettext(const char *msgid) __attribute__((format_arg(1))); + extern char * + libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) + __attribute__((format_arg(1))) __attribute__((format_arg(2))); #else #define libpq_gettext(x) (x) + #define libpq_ngettext(s,p,n) ((n) == 1 ? (s) : (p)) #endif /*