Log diagnostic messages if errors occur during LDAP auth.
authorPeter Eisentraut <peter_e@gmx.net>
Fri, 13 Oct 2017 02:33:34 +0000 (22:33 -0400)
committerPeter Eisentraut <peter_e@gmx.net>
Fri, 13 Oct 2017 02:37:14 +0000 (22:37 -0400)
Diagnostic messages seem likely to help users diagnose root
causes more easily, so let's report them as errdetail.

Author: Thomas Munro
Reviewed-By: Ashutosh Bapat, Christoph Berg, Alvaro Herrera, Peter Eisentraut
Discussion: https://postgr.es/m/CAEepm=2_dA-SYpFdmNVwvKsEBXOUj=K4ooKovHmvj6jnMdt8dw@mail.gmail.com

src/backend/libpq/auth.c
src/test/ldap/t/001_auth.pl

index 11ef4a585884df789481491832e6a426e0e57155..2728c66a352840ce6ff43d0a524ac31fa4878b5f 100644 (file)
@@ -2305,6 +2305,8 @@ CheckBSDAuth(Port *port, char *user)
  */
 #ifdef USE_LDAP
 
+static int errdetail_for_ldap(LDAP *ldap);
+
 /*
  * Initialize a connection to the LDAP server, including setting up
  * TLS if requested.
@@ -2332,7 +2334,9 @@ InitializeLDAPConnection(Port *port, LDAP **ldap)
    if ((r = ldap_set_option(*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
    {
        ereport(LOG,
-               (errmsg("could not set LDAP protocol version: %s", ldap_err2string(r))));
+               (errmsg("could not set LDAP protocol version: %s",
+                       ldap_err2string(r)),
+                errdetail_for_ldap(*ldap)));
        ldap_unbind(*ldap);
        return STATUS_ERROR;
    }
@@ -2385,7 +2389,9 @@ InitializeLDAPConnection(Port *port, LDAP **ldap)
 #endif
        {
            ereport(LOG,
-                   (errmsg("could not start LDAP TLS session: %s", ldap_err2string(r))));
+                   (errmsg("could not start LDAP TLS session: %s",
+                           ldap_err2string(r)),
+                    errdetail_for_ldap(*ldap)));
            ldap_unbind(*ldap);
            return STATUS_ERROR;
        }
@@ -2508,7 +2514,9 @@ CheckLDAPAuth(Port *port)
        {
            ereport(LOG,
                    (errmsg("could not perform initial LDAP bind for ldapbinddn \"%s\" on server \"%s\": %s",
-                           port->hba->ldapbinddn, port->hba->ldapserver, ldap_err2string(r))));
+                           port->hba->ldapbinddn, port->hba->ldapserver,
+                           ldap_err2string(r)),
+                    errdetail_for_ldap(ldap)));
            ldap_unbind(ldap);
            pfree(passwd);
            return STATUS_ERROR;
@@ -2534,7 +2542,8 @@ CheckLDAPAuth(Port *port)
        {
            ereport(LOG,
                    (errmsg("could not search LDAP for filter \"%s\" on server \"%s\": %s",
-                           filter, port->hba->ldapserver, ldap_err2string(r))));
+                           filter, port->hba->ldapserver, ldap_err2string(r)),
+                    errdetail_for_ldap(ldap)));
            ldap_unbind(ldap);
            pfree(passwd);
            pfree(filter);
@@ -2573,7 +2582,9 @@ CheckLDAPAuth(Port *port)
            (void) ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &error);
            ereport(LOG,
                    (errmsg("could not get dn for the first entry matching \"%s\" on server \"%s\": %s",
-                           filter, port->hba->ldapserver, ldap_err2string(error))));
+                           filter, port->hba->ldapserver,
+                           ldap_err2string(error)),
+                    errdetail_for_ldap(ldap)));
            ldap_unbind(ldap);
            pfree(passwd);
            pfree(filter);
@@ -2618,23 +2629,46 @@ CheckLDAPAuth(Port *port)
                            port->hba->ldapsuffix ? port->hba->ldapsuffix : "");
 
    r = ldap_simple_bind_s(ldap, fulluser, passwd);
-   ldap_unbind(ldap);
 
    if (r != LDAP_SUCCESS)
    {
        ereport(LOG,
                (errmsg("LDAP login failed for user \"%s\" on server \"%s\": %s",
-                       fulluser, port->hba->ldapserver, ldap_err2string(r))));
+                       fulluser, port->hba->ldapserver, ldap_err2string(r)),
+                errdetail_for_ldap(ldap)));
+       ldap_unbind(ldap);
        pfree(passwd);
        pfree(fulluser);
        return STATUS_ERROR;
    }
 
+   ldap_unbind(ldap);
    pfree(passwd);
    pfree(fulluser);
 
    return STATUS_OK;
 }
+
+/*
+ * Add a detail error message text to the current error if one can be
+ * constructed from the LDAP 'diagnostic message'.
+ */
+static int
+errdetail_for_ldap(LDAP *ldap)
+{
+   char       *message;
+   int         rc;
+
+   rc = ldap_get_option(ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, &message);
+   if (rc == LDAP_SUCCESS && message != NULL)
+   {
+       errdetail("LDAP diagnostics: %s", message);
+       ldap_memfree(message);
+   }
+
+   return 0;
+}
+
 #endif                         /* USE_LDAP */
 
 
index a7cac6210b5c909768acc5fb6e23f673fc8fcb38..38760ece617e445add3b02ece9da4ea73f3c72a1 100644 (file)
@@ -2,7 +2,7 @@ use strict;
 use warnings;
 use TestLib;
 use PostgresNode;
-use Test::More tests => 14;
+use Test::More tests => 15;
 
 my ($slapd, $ldap_bin_dir, $ldap_schema_dir);
 
@@ -175,3 +175,12 @@ $node->reload;
 
 $ENV{"PGPASSWORD"} = 'secret1';
 test_access($node, 'test1', 0, 'combined LDAP URL and search filter');
+
+note "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->reload;
+
+$ENV{"PGPASSWORD"} = 'secret1';
+test_access($node, 'test1', 2, 'any attempt fails due to unsupported TLS');