*** a/src/backend/libpq/auth.c --- b/src/backend/libpq/auth.c *************** *** 2096,2135 **** CheckPAMAuth(Port *port, char *user, char *password) */ #ifdef USE_LDAP static int ! CheckLDAPAuth(Port *port) { - char *passwd; - LDAP *ldap; - int r; int ldapversion = LDAP_VERSION3; ! char fulluser[NAMEDATALEN + 256 + 1]; ! ! if (!port->hba->ldapserver || port->hba->ldapserver[0] == '\0') ! { ! ereport(LOG, ! (errmsg("LDAP server not specified"))); ! return STATUS_ERROR; ! } ! ! if (port->hba->ldapport == 0) ! port->hba->ldapport = LDAP_PORT; ! ! sendAuthRequest(port, AUTH_REQ_PASSWORD); ! ! passwd = recv_password_packet(port); ! if (passwd == NULL) ! return STATUS_EOF; /* client wouldn't send password */ ! ! if (strlen(passwd) == 0) ! { ! ereport(LOG, ! (errmsg("empty password returned by client"))); ! return STATUS_ERROR; ! } ! ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport); ! if (!ldap) { #ifndef WIN32 ereport(LOG, --- 2096,2113 ---- */ #ifdef USE_LDAP + /* + * Initialize a connection to the LDAP server, including setting up + * TLS if requested. + */ static int ! InitializeLDAPConnection(Port *port, LDAP **ldap) { int ldapversion = LDAP_VERSION3; ! int r; ! *ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport); ! if (!*ldap) { #ifndef WIN32 ereport(LOG, *************** *** 2143,2151 **** CheckLDAPAuth(Port *port) return STATUS_ERROR; } ! if ((r = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS) { ! ldap_unbind(ldap); ereport(LOG, (errmsg("could not set LDAP protocol version: error code %d", r))); return STATUS_ERROR; --- 2121,2129 ---- return STATUS_ERROR; } ! if ((r = ldap_set_option(*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS) { ! ldap_unbind(*ldap); ereport(LOG, (errmsg("could not set LDAP protocol version: error code %d", r))); return STATUS_ERROR; *************** *** 2154,2160 **** CheckLDAPAuth(Port *port) if (port->hba->ldaptls) { #ifndef WIN32 ! if ((r = ldap_start_tls_s(ldap, NULL, NULL)) != LDAP_SUCCESS) #else static __ldap_start_tls_sA _ldap_start_tls_sA = NULL; --- 2132,2138 ---- if (port->hba->ldaptls) { #ifndef WIN32 ! if ((r = ldap_start_tls_s(*ldap, NULL, NULL)) != LDAP_SUCCESS) #else static __ldap_start_tls_sA _ldap_start_tls_sA = NULL; *************** *** 2195,2215 **** CheckLDAPAuth(Port *port) * per process and is automatically cleaned up on process exit. */ } ! if ((r = _ldap_start_tls_sA(ldap, NULL, NULL, NULL, NULL)) != LDAP_SUCCESS) #endif { ! ldap_unbind(ldap); ereport(LOG, (errmsg("could not start LDAP TLS session: error code %d", r))); return STATUS_ERROR; } } ! snprintf(fulluser, sizeof(fulluser), "%s%s%s", ! port->hba->ldapprefix ? port->hba->ldapprefix : "", ! port->user_name, ! port->hba->ldapsuffix ? port->hba->ldapsuffix : ""); ! fulluser[sizeof(fulluser) - 1] = '\0'; r = ldap_simple_bind_s(ldap, fulluser, passwd); ldap_unbind(ldap); --- 2173,2354 ---- * per process and is automatically cleaned up on process exit. */ } ! if ((r = _ldap_start_tls_sA(*ldap, NULL, NULL, NULL, NULL)) != LDAP_SUCCESS) #endif { ! ldap_unbind(*ldap); ereport(LOG, (errmsg("could not start LDAP TLS session: error code %d", r))); return STATUS_ERROR; } } ! return STATUS_OK; ! } ! ! /* ! * Perform LDAP authentication ! */ ! static int ! CheckLDAPAuth(Port *port) ! { ! char *passwd; ! LDAP *ldap; ! int r; ! char *fulluser; ! ! if (!port->hba->ldapserver || port->hba->ldapserver[0] == '\0') ! { ! ereport(LOG, ! (errmsg("LDAP server not specified"))); ! return STATUS_ERROR; ! } ! ! if (port->hba->ldapport == 0) ! port->hba->ldapport = LDAP_PORT; ! ! sendAuthRequest(port, AUTH_REQ_PASSWORD); ! ! passwd = recv_password_packet(port); ! if (passwd == NULL) ! return STATUS_EOF; /* client wouldn't send password */ ! ! if (strlen(passwd) == 0) ! { ! ereport(LOG, ! (errmsg("empty password returned by client"))); ! return STATUS_ERROR; ! } ! ! if (InitializeLDAPConnection(port, &ldap) == STATUS_ERROR) ! /* Error message already sent */ ! return STATUS_ERROR; ! ! if (port->hba->ldapbasedn) ! { ! /* ! * First perform an LDAP search to find the DN for the user we are trying to log ! * in as. ! */ ! char *filter; ! LDAPMessage *search_message; ! LDAPMessage *entry; ! char *attributes[2]; ! char *dn; ! ! /* ! * Bind with a pre-defined username/password (if available) for searching. If ! * none is specified, this turns into an anonymous bind. ! */ ! r = ldap_simple_bind_s(ldap, ! port->hba->ldapbinddn ? port->hba->ldapbinddn : "", ! port->hba->ldapbindpasswd ? port->hba->ldapbindpasswd : ""); ! if (r != LDAP_SUCCESS) ! { ! ereport(LOG, ! (errmsg("could not perform initial LDAP bind for ldapbinddn \"%s\" on server \"%s\": error code %d", ! port->hba->ldapbinddn, port->hba->ldapserver, r))); ! return STATUS_ERROR; ! } ! ! /* fetch just one attribute, else /all/ attributes are returned */ ! attributes[0] = port->hba->ldapsearchattribute ? port->hba->ldapsearchattribute : "uid"; ! attributes[1] = NULL; ! ! filter = palloc(strlen(attributes[0])+strlen(port->user_name)+4); ! sprintf(filter, "(%s=%s)", ! attributes[0], ! port->user_name); /* FIXME: sanitize user_name? */ ! ! r = ldap_search_s(ldap, ! port->hba->ldapbasedn, ! LDAP_SCOPE_SUBTREE, ! filter, ! attributes, ! 0, ! &search_message); ! ! if (r != LDAP_SUCCESS) ! { ! ereport(LOG, ! (errmsg("could not search LDAP for filter \"%s\" on server \"%s\": error code %d", ! filter, port->hba->ldapserver, r))); ! pfree(filter); ! return STATUS_ERROR; ! } ! ! if (ldap_count_entries(ldap, search_message) != 1) ! { ! if (ldap_count_entries(ldap, search_message) == 0) ! ereport(LOG, ! (errmsg("LDAP search failed for filter \"%s\" on server \"%s\": no such user", ! filter, port->hba->ldapserver))); ! else ! ereport(LOG, ! (errmsg("LDAP search failed for filter \"%s\" on server \"%s\": user is not unique (%d matches)", ! filter, port->hba->ldapserver, ldap_count_entries(ldap, search_message)))); ! ! pfree(filter); ! ldap_msgfree(search_message); ! return STATUS_ERROR; ! } ! ! entry = ldap_first_entry(ldap, search_message); ! dn = ldap_get_dn(ldap, entry); ! if (dn == NULL) ! { ! int error; ! (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)))); ! pfree(filter); ! ldap_msgfree(search_message); ! return STATUS_ERROR; ! } ! fulluser = pstrdup(dn); ! ! pfree(filter); ! ldap_memfree(dn); ! ldap_msgfree(search_message); ! ! /* Unbind and disconnect from the LDAP server */ ! r = ldap_unbind_s(ldap); ! if (r != LDAP_SUCCESS) ! { ! int error; ! (void)ldap_get_option(ldap, LDAP_OPT_RESULT_CODE, &error); ! ereport(LOG, ! (errmsg("could not unbind after searching for user \"%s\" on server \"%s\": %s", ! fulluser, port->hba->ldapserver, ldap_err2string(error)))); ! pfree(fulluser); ! return STATUS_ERROR; ! } ! ! /* ! * Need to re-initialize the LDAP connection, so that we can bind ! * to it with a different username. ! */ ! if (InitializeLDAPConnection(port, &ldap) == STATUS_ERROR) ! { ! pfree(fulluser); ! ! /* Error message already sent */ ! return STATUS_ERROR; ! } ! } ! else ! { ! fulluser = palloc(port->hba->ldapprefix ? strlen(port->hba->ldapprefix) : 0 + ! strlen(port->user_name) + ! port->hba->ldapsuffix ? strlen(port->hba->ldapsuffix) : 0 + ! 1); ! ! sprintf(fulluser, "%s%s%s", ! port->hba->ldapprefix ? port->hba->ldapprefix : "", ! port->user_name, ! port->hba->ldapsuffix ? port->hba->ldapsuffix : ""); ! } r = ldap_simple_bind_s(ldap, fulluser, passwd); ldap_unbind(ldap); *************** *** 2219,2227 **** CheckLDAPAuth(Port *port) --- 2358,2369 ---- ereport(LOG, (errmsg("LDAP login failed for user \"%s\" on server \"%s\": error code %d", fulluser, port->hba->ldapserver, r))); + pfree(fulluser); return STATUS_ERROR; } + pfree(fulluser); + return STATUS_OK; } #endif /* USE_LDAP */ *** a/src/backend/libpq/hba.c --- b/src/backend/libpq/hba.c *************** *** 1103,1108 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline) --- 1103,1128 ---- return false; } } + else if (strcmp(token, "ldapbinddn") == 0) + { + REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap"); + parsedline->ldapbinddn = pstrdup(c); + } + else if (strcmp(token, "ldapbindpasswd") == 0) + { + REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap"); + parsedline->ldapbindpasswd = pstrdup(c); + } + else if (strcmp(token, "ldapsearchattribute") == 0) + { + REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap"); + parsedline->ldapsearchattribute = pstrdup(c); + } + else if (strcmp(token, "ldapbasedn") == 0) + { + REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap"); + parsedline->ldapbasedn = pstrdup(c); + } else if (strcmp(token, "ldapprefix") == 0) { REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap"); *** a/src/include/libpq/hba.h --- b/src/include/libpq/hba.h *************** *** 61,66 **** typedef struct --- 61,70 ---- bool ldaptls; char *ldapserver; int ldapport; + char *ldapbinddn; + char *ldapbindpasswd; + char *ldapsearchattribute; + char *ldapbasedn; char *ldapprefix; char *ldapsuffix; bool clientcert;