1: 8d93ca3792 ! 1: ad71dc8b72 Add sslcertmode option for client certificates @@ configure: else fi - # Function introduced in OpenSSL 1.0.2. - for ac_func in X509_get_signature_nid -+ # Functions introduced in OpenSSL 1.0.2. LibreSSL doesn't have all of these. ++ # Functions introduced in OpenSSL 1.0.2. Note that LibreSSL doesn't have ++ # SSL_CTX_set_cert_cb(). + for ac_func in X509_get_signature_nid SSL_CTX_set_cert_cb do : - ac_fn_c_check_func "$LINENO" "X509_get_signature_nid" "ac_cv_func_X509_get_signature_nid" @@ configure.ac: if test "$with_ssl" = openssl ; then fi - # Function introduced in OpenSSL 1.0.2. - AC_CHECK_FUNCS([X509_get_signature_nid]) -+ # Functions introduced in OpenSSL 1.0.2. LibreSSL doesn't have all of these. ++ # Functions introduced in OpenSSL 1.0.2. Note that LibreSSL doesn't have ++ # SSL_CTX_set_cert_cb(). + AC_CHECK_FUNCS([X509_get_signature_nid SSL_CTX_set_cert_cb]) # Functions introduced in OpenSSL 1.1.0. We used to check for # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL @@ doc/src/sgml/libpq.sgml: postgresql://%2Fvar%2Flib%2Fpostgresql/dbname + allow (default) + + -+ a certificate may be sent, if the server requests one and it has -+ been provided via sslcert ++ a certificate may be sent, if the server requests one and the client ++ has one to send + + + @@ meson.build: if sslopt in ['auto', 'openssl'] ['SSL_new', {'required': true}], - # Function introduced in OpenSSL 1.0.2. -+ # Functions introduced in OpenSSL 1.0.2. LibreSSL doesn't have all of these. ++ # Functions introduced in OpenSSL 1.0.2. ['X509_get_signature_nid'], -+ ['SSL_CTX_set_cert_cb'], ++ ['SSL_CTX_set_cert_cb'], # not in LibreSSL # Functions introduced in OpenSSL 1.1.0. We used to check for # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL @@ src/interfaces/libpq/fe-auth.c: check_expected_areq(AuthRequest areq, PGconn *co + */ + if (!conn->ssl_cert_requested) + { -+ libpq_append_conn_error(conn, "server did not request a certificate"); ++ libpq_append_conn_error(conn, "server did not request an SSL certificate"); + return false; + } + else if (!conn->ssl_cert_sent) + { -+ libpq_append_conn_error(conn, "server accepted connection without a valid certificate"); ++ libpq_append_conn_error(conn, "server accepted connection without a valid SSL certificate"); + return false; + } + } @@ src/interfaces/libpq/fe-connect.c: static const internalPQconninfoOption PQconni {"sslpassword", NULL, NULL, NULL, "SSL-Client-Key-Password", "*", 20, offsetof(struct pg_conn, sslpassword)}, +@@ src/interfaces/libpq/fe-connect.c: connectOptions2(PGconn *conn) + case 'r': /* "require" */ + case 'v': /* "verify-ca" or "verify-full" */ + conn->status = CONNECTION_BAD; +- libpq_append_conn_error(conn, "sslmode value \"%s\" invalid when SSL support is not compiled in", +- conn->sslmode); ++ libpq_append_conn_error(conn, "%s value \"%s\" invalid when SSL support is not compiled in", ++ "sslmode", conn->sslmode); + return false; + } + #endif @@ src/interfaces/libpq/fe-connect.c: connectOptions2(PGconn *conn) return false; } @@ src/interfaces/libpq/fe-connect.c: connectOptions2(PGconn *conn) + if (strcmp(conn->sslcertmode, "require") == 0) + { + conn->status = CONNECTION_BAD; -+ libpq_append_conn_error(conn, "sslcertmode value \"%s\" invalid when SSL support is not compiled in", -+ conn->sslcertmode); ++ libpq_append_conn_error(conn, "%s value \"%s\" invalid when SSL support is not compiled in", ++ "sslcertmode", conn->sslcertmode); + return false; + } +#endif +#ifndef HAVE_SSL_CTX_SET_CERT_CB + /* + * Without a certificate callback, the current implementation can't -+ * figure out if a certficate was actually requested, so "require" is ++ * figure out if a certificate was actually requested, so "require" is + * useless. + */ + if (strcmp(conn->sslcertmode, "require") == 0) @@ src/interfaces/libpq/fe-connect.c: connectOptions2(PGconn *conn) /* * validate gssencmode option */ +@@ src/interfaces/libpq/fe-connect.c: freePGconn(PGconn *conn) + explicit_bzero(conn->sslpassword, strlen(conn->sslpassword)); + free(conn->sslpassword); + } ++ free(conn->sslcertmode); + free(conn->sslrootcert); + free(conn->sslcrl); + free(conn->sslcrldir); ## src/interfaces/libpq/fe-secure-openssl.c ## @@ src/interfaces/libpq/fe-secure-openssl.c: verify_cb(int ok, X509_STORE_CTX *ctx) @@ src/test/ssl/t/001_ssltests.pl: $node->connect_ok( + "$common_connstr sslrootcert=ssl/root+server_ca.crt sslmode=require sslcertmode=require", + "connect with sslcertmode=require fails without a client certificate", + expected_stderr => $supports_sslcertmode_require -+ ? qr/server accepted connection without a valid certificate/ ++ ? qr/server accepted connection without a valid SSL certificate/ + : qr/sslcertmode value "require" is not supported/); + # CRL tests @@ src/test/ssl/t/001_ssltests.pl: $node->connect_ok( $node->connect_fails( "$common_connstr user=ssltestuser sslcert=ssl/client.crt " + ## src/test/ssl/t/003_sslinfo.pl ## +@@ src/test/ssl/t/003_sslinfo.pl: my $SERVERHOSTADDR = '127.0.0.1'; + # This is the pattern to use in pg_hba.conf to match incoming connections. + my $SERVERHOSTCIDR = '127.0.0.1/32'; + ++# Determine whether build supports sslcertmode=require. ++my $supports_sslcertmode_require = ++ check_pg_config("#define HAVE_SSL_CTX_SET_CERT_CB 1"); ++ + # Allocation of base connection string shared among multiple tests. + my $common_connstr; + +@@ src/test/ssl/t/003_sslinfo.pl: $result = $node->safe_psql( + connstr => $common_connstr); + is($result, 'CA:FALSE|t', 'extract extension from cert'); + ++# Sanity tests for sslcertmode, using ssl_client_cert_present() ++my @cases = ( ++ { opts => "sslcertmode=allow", present => 't' }, ++ { opts => "sslcertmode=allow sslcert=invalid", present => 'f' }, ++ { opts => "sslcertmode=disable", present => 'f' }, ++); ++if ($supports_sslcertmode_require) ++{ ++ push(@cases, { opts => "sslcertmode=require", present => 't' }); ++} ++ ++foreach my $c (@cases) { ++ $result = $node->safe_psql( ++ "trustdb", ++ "SELECT ssl_client_cert_present();", ++ connstr => "$common_connstr dbname=trustdb $c->{'opts'}" ++ ); ++ is($result, $c->{'present'}, "ssl_client_cert_present() for $c->{'opts'}"); ++} ++ + done_testing(); + ## src/tools/msvc/Solution.pm ## @@ src/tools/msvc/Solution.pm: sub GenerateFiles HAVE_SETPROCTITLE_FAST => undef, 2: e2343c0089 ! 2: c9ecdd54ea require_auth: decouple SASL and SCRAM @@ src/interfaces/libpq/fe-connect.c: connectOptions2(PGconn *conn) + free(part); + continue; /* avoid the bitmask manipulation below */ } - else if (strcmp(method, "creds") == 0) + else if (strcmp(method, "none") == 0) { ## src/interfaces/libpq/libpq-int.h ##