mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03:00
Allow ldaps when using ldap authentication
While ldaptls=1 provides an RFC 4513 conforming way to do LDAP authentication with TLS encryption, there was an earlier de facto standard way to do LDAP over SSL called LDAPS. Even though it's not enshrined in a standard, it's still widely used and sometimes required by organizations' network policies. There seems to be no reason not to support it when available in the client library. Therefore, add support when using OpenLDAP 2.4+ or Windows. It can be configured with ldapscheme=ldaps or ldapurl=ldaps://... Add tests for both ways of requesting LDAPS and a test for the pre-existing ldaptls=1. Modify the 001_auth.pl test for "diagnostic messages", which was previously relying on the server rejecting ldaptls=1. Author: Thomas Munro Reviewed-By: Peter Eisentraut Discussion: https://postgr.es/m/CAEepm=1s+pA-LZUjQ-9GQz0Z4rX_eK=DFXAF1nBQ+ROPimuOYQ@mail.gmail.com
This commit is contained in:
@ -2355,22 +2355,61 @@ static int errdetail_for_ldap(LDAP *ldap);
|
||||
static int
|
||||
InitializeLDAPConnection(Port *port, LDAP **ldap)
|
||||
{
|
||||
const char *scheme;
|
||||
int ldapversion = LDAP_VERSION3;
|
||||
int r;
|
||||
|
||||
*ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport);
|
||||
scheme = port->hba->ldapscheme;
|
||||
if (scheme == NULL)
|
||||
scheme = "ldap";
|
||||
#ifdef WIN32
|
||||
*ldap = ldap_sslinit(port->hba->ldapserver,
|
||||
port->hba->ldapport,
|
||||
strcmp(scheme, "ldaps") == 0);
|
||||
if (!*ldap)
|
||||
{
|
||||
#ifndef WIN32
|
||||
ereport(LOG,
|
||||
(errmsg("could not initialize LDAP: %m")));
|
||||
#else
|
||||
ereport(LOG,
|
||||
(errmsg("could not initialize LDAP: error code %d",
|
||||
(int) LdapGetLastError())));
|
||||
#endif
|
||||
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
#else
|
||||
#ifdef HAVE_LDAP_INITIALIZE
|
||||
{
|
||||
char *uri;
|
||||
|
||||
uri = psprintf("%s://%s:%d", scheme, port->hba->ldapserver,
|
||||
port->hba->ldapport);
|
||||
r = ldap_initialize(ldap, uri);
|
||||
pfree(uri);
|
||||
if (r != LDAP_SUCCESS)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("could not initialize LDAP: %s",
|
||||
ldap_err2string(r))));
|
||||
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (strcmp(scheme, "ldaps") == 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("ldaps not supported with this LDAP library")));
|
||||
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
*ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport);
|
||||
if (!*ldap)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("could not initialize LDAP: %m")));
|
||||
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if ((r = ldap_set_option(*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
|
||||
{
|
||||
@ -2493,7 +2532,13 @@ CheckLDAPAuth(Port *port)
|
||||
}
|
||||
|
||||
if (port->hba->ldapport == 0)
|
||||
port->hba->ldapport = LDAP_PORT;
|
||||
{
|
||||
if (port->hba->ldapscheme != NULL &&
|
||||
strcmp(port->hba->ldapscheme, "ldaps") == 0)
|
||||
port->hba->ldapport = LDAPS_PORT;
|
||||
else
|
||||
port->hba->ldapport = LDAP_PORT;
|
||||
}
|
||||
|
||||
sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
|
||||
|
||||
|
@ -1728,7 +1728,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(urldata->lud_scheme, "ldap") != 0)
|
||||
if (strcmp(urldata->lud_scheme, "ldap") != 0 &&
|
||||
strcmp(urldata->lud_scheme, "ldaps") != 0)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||
@ -1739,6 +1740,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (urldata->lud_scheme)
|
||||
hbaline->ldapscheme = pstrdup(urldata->lud_scheme);
|
||||
if (urldata->lud_host)
|
||||
hbaline->ldapserver = pstrdup(urldata->lud_host);
|
||||
hbaline->ldapport = urldata->lud_port;
|
||||
@ -1766,6 +1769,17 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
|
||||
else
|
||||
hbaline->ldaptls = false;
|
||||
}
|
||||
else if (strcmp(name, "ldapscheme") == 0)
|
||||
{
|
||||
REQUIRE_AUTH_OPTION(uaLDAP, "ldapscheme", "ldap");
|
||||
if (strcmp(val, "ldap") != 0 && strcmp(val, "ldaps") != 0)
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||
errmsg("invalid ldapscheme value: \"%s\"", val),
|
||||
errcontext("line %d of configuration file \"%s\"",
|
||||
line_num, HbaFileName)));
|
||||
hbaline->ldapscheme = pstrdup(val);
|
||||
}
|
||||
else if (strcmp(name, "ldapserver") == 0)
|
||||
{
|
||||
REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
|
||||
|
@ -75,6 +75,7 @@ typedef struct HbaLine
|
||||
char *pamservice;
|
||||
bool pam_use_hostname;
|
||||
bool ldaptls;
|
||||
char *ldapscheme;
|
||||
char *ldapserver;
|
||||
int ldapport;
|
||||
char *ldapbinddn;
|
||||
|
@ -310,6 +310,9 @@
|
||||
/* Define to 1 if you have the <ldap.h> header file. */
|
||||
#undef HAVE_LDAP_H
|
||||
|
||||
/* Define to 1 if you have the `ldap_initialize' function. */
|
||||
#undef HAVE_LDAP_INITIALIZE
|
||||
|
||||
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
||||
#undef HAVE_LIBCRYPTO
|
||||
|
||||
|
@ -2,7 +2,7 @@ use strict;
|
||||
use warnings;
|
||||
use TestLib;
|
||||
use PostgresNode;
|
||||
use Test::More tests => 15;
|
||||
use Test::More tests => 19;
|
||||
|
||||
my ($slapd, $ldap_bin_dir, $ldap_schema_dir);
|
||||
|
||||
@ -33,13 +33,16 @@ elsif ($^O eq 'freebsd')
|
||||
$ENV{PATH} = "$ldap_bin_dir:$ENV{PATH}" if $ldap_bin_dir;
|
||||
|
||||
my $ldap_datadir = "${TestLib::tmp_check}/openldap-data";
|
||||
my $slapd_certs = "${TestLib::tmp_check}/slapd-certs";
|
||||
my $slapd_conf = "${TestLib::tmp_check}/slapd.conf";
|
||||
my $slapd_pidfile = "${TestLib::tmp_check}/slapd.pid";
|
||||
my $slapd_logfile = "${TestLib::tmp_check}/slapd.log";
|
||||
my $ldap_conf = "${TestLib::tmp_check}/ldap.conf";
|
||||
my $ldap_server = 'localhost';
|
||||
my $ldap_port = int(rand() * 16384) + 49152;
|
||||
my $ldaps_port = $ldap_port + 1;
|
||||
my $ldap_url = "ldap://$ldap_server:$ldap_port";
|
||||
my $ldaps_url = "ldaps://$ldap_server:$ldaps_port";
|
||||
my $ldap_basedn = 'dc=example,dc=net';
|
||||
my $ldap_rootdn = 'cn=Manager,dc=example,dc=net';
|
||||
my $ldap_rootpw = 'secret';
|
||||
@ -63,13 +66,27 @@ access to *
|
||||
database ldif
|
||||
directory $ldap_datadir
|
||||
|
||||
TLSCACertificateFile $slapd_certs/ca.crt
|
||||
TLSCertificateFile $slapd_certs/server.crt
|
||||
TLSCertificateKeyFile $slapd_certs/server.key
|
||||
|
||||
suffix "dc=example,dc=net"
|
||||
rootdn "$ldap_rootdn"
|
||||
rootpw $ldap_rootpw});
|
||||
|
||||
mkdir $ldap_datadir or die;
|
||||
# don't bother to check the server's cert (though perhaps we should)
|
||||
append_to_file($ldap_conf,
|
||||
qq{TLS_REQCERT never
|
||||
});
|
||||
|
||||
system_or_bail $slapd, '-f', $slapd_conf, '-h', $ldap_url;
|
||||
mkdir $ldap_datadir or die;
|
||||
mkdir $slapd_certs or die;
|
||||
|
||||
system_or_bail "openssl", "req", "-new", "-nodes", "-keyout", "$slapd_certs/ca.key", "-x509", "-out", "$slapd_certs/ca.crt", "-subj", "/cn=CA";
|
||||
system_or_bail "openssl", "req", "-new", "-nodes", "-keyout", "$slapd_certs/server.key", "-out", "$slapd_certs/server.csr", "-subj", "/cn=server";
|
||||
system_or_bail "openssl", "x509", "-req", "-in", "$slapd_certs/server.csr", "-CA", "$slapd_certs/ca.crt", "-CAkey", "$slapd_certs/ca.key", "-CAcreateserial", "-out", "$slapd_certs/server.crt";
|
||||
|
||||
system_or_bail $slapd, '-f', $slapd_conf, '-h', "$ldap_url $ldaps_url";
|
||||
|
||||
END
|
||||
{
|
||||
@ -81,6 +98,7 @@ chmod 0600, $ldap_pwfile or die;
|
||||
|
||||
$ENV{'LDAPURI'} = $ldap_url;
|
||||
$ENV{'LDAPBINDDN'} = $ldap_rootdn;
|
||||
$ENV{'LDAPCONF'} = $ldap_conf;
|
||||
|
||||
note "loading LDAP data";
|
||||
|
||||
@ -178,9 +196,44 @@ test_access($node, 'test1', 0, 'combined LDAP URL and search filter');
|
||||
|
||||
note "diagnostic message";
|
||||
|
||||
# note bad ldapprefix with a question mark that triggers a diagnostic message
|
||||
unlink($node->data_dir . '/pg_hba.conf');
|
||||
$node->append_conf('pg_hba.conf', qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="uid=" ldapsuffix=",dc=example,dc=net" ldaptls=1});
|
||||
$node->append_conf('pg_hba.conf', qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="?uid=" ldapsuffix=""});
|
||||
$node->reload;
|
||||
|
||||
$ENV{"PGPASSWORD"} = 'secret1';
|
||||
test_access($node, 'test1', 2, 'any attempt fails due to unsupported TLS');
|
||||
test_access($node, 'test1', 2, 'any attempt fails due to bad search pattern');
|
||||
|
||||
note "TLS";
|
||||
|
||||
# request StartTLS with ldaptls=1
|
||||
unlink($node->data_dir . '/pg_hba.conf');
|
||||
$node->append_conf('pg_hba.conf', qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)" ldaptls=1});
|
||||
$node->reload;
|
||||
|
||||
$ENV{"PGPASSWORD"} = 'secret1';
|
||||
test_access($node, 'test1', 0, 'StartTLS');
|
||||
|
||||
# request LDAPS with ldapscheme=ldaps
|
||||
unlink($node->data_dir . '/pg_hba.conf');
|
||||
$node->append_conf('pg_hba.conf', qq{local all all ldap ldapserver=$ldap_server ldapscheme=ldaps ldapport=$ldaps_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)"});
|
||||
$node->reload;
|
||||
|
||||
$ENV{"PGPASSWORD"} = 'secret1';
|
||||
test_access($node, 'test1', 0, 'LDAPS');
|
||||
|
||||
# request LDAPS with ldapurl=ldaps://...
|
||||
unlink($node->data_dir . '/pg_hba.conf');
|
||||
$node->append_conf('pg_hba.conf', qq{local all all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)"});
|
||||
$node->reload;
|
||||
|
||||
$ENV{"PGPASSWORD"} = 'secret1';
|
||||
test_access($node, 'test1', 0, 'LDAPS with URL');
|
||||
|
||||
# bad combination of LDAPS and StartTLS
|
||||
unlink($node->data_dir . '/pg_hba.conf');
|
||||
$node->append_conf('pg_hba.conf', qq{local all all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)" ldaptls=1});
|
||||
$node->reload;
|
||||
|
||||
$ENV{"PGPASSWORD"} = 'secret1';
|
||||
test_access($node, 'test1', 2, 'bad combination of LDAPS and StartTLS');
|
||||
|
Reference in New Issue
Block a user