aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/auth.c')
-rw-r--r--src/backend/libpq/auth.c248
1 files changed, 205 insertions, 43 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 1e080465b67..7b6b5a5a071 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.187 2009/10/16 22:08:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.188 2009/12/12 21:35:21 mha Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2096,40 +2096,18 @@ CheckPAMAuth(Port *port, char *user, char *password)
*/
#ifdef USE_LDAP
+/*
+ * Initialize a connection to the LDAP server, including setting up
+ * TLS if requested.
+ */
static int
-CheckLDAPAuth(Port *port)
+InitializeLDAPConnection(Port *port, LDAP **ldap)
{
- 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;
- }
+ int r;
- ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport);
- if (!ldap)
+ *ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport);
+ if (!*ldap)
{
#ifndef WIN32
ereport(LOG,
@@ -2143,9 +2121,9 @@ CheckLDAPAuth(Port *port)
return STATUS_ERROR;
}
- if ((r = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
+ if ((r = ldap_set_option(*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
{
- ldap_unbind(ldap);
+ ldap_unbind(*ldap);
ereport(LOG,
(errmsg("could not set LDAP protocol version: error code %d", r)));
return STATUS_ERROR;
@@ -2154,7 +2132,7 @@ CheckLDAPAuth(Port *port)
if (port->hba->ldaptls)
{
#ifndef WIN32
- if ((r = ldap_start_tls_s(ldap, NULL, NULL)) != LDAP_SUCCESS)
+ if ((r = ldap_start_tls_s(*ldap, NULL, NULL)) != LDAP_SUCCESS)
#else
static __ldap_start_tls_sA _ldap_start_tls_sA = NULL;
@@ -2174,7 +2152,7 @@ CheckLDAPAuth(Port *port)
* should never happen since we import other files from
* wldap32, but check anyway
*/
- ldap_unbind(ldap);
+ ldap_unbind(*ldap);
ereport(LOG,
(errmsg("could not load wldap32.dll")));
return STATUS_ERROR;
@@ -2182,7 +2160,7 @@ CheckLDAPAuth(Port *port)
_ldap_start_tls_sA = (__ldap_start_tls_sA) GetProcAddress(ldaphandle, "ldap_start_tls_sA");
if (_ldap_start_tls_sA == NULL)
{
- ldap_unbind(ldap);
+ ldap_unbind(*ldap);
ereport(LOG,
(errmsg("could not load function _ldap_start_tls_sA in wldap32.dll"),
errdetail("LDAP over SSL is not supported on this platform.")));
@@ -2195,21 +2173,202 @@ 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)
+ if ((r = _ldap_start_tls_sA(*ldap, NULL, NULL, NULL, NULL)) != LDAP_SUCCESS)
#endif
{
- ldap_unbind(ldap);
+ 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';
+ 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;
+ char *c;
+
+ /*
+ * Disallow any characters that we would otherwise need to escape, since they
+ * aren't really reasonable in a username anyway. Allowing them would make it
+ * possible to inject any kind of custom filters in the LDAP filter.
+ */
+ for (c = port->user_name; *c; c++)
+ {
+ if (*c == '*' ||
+ *c == '(' ||
+ *c == ')' ||
+ *c == '\\' ||
+ *c == '/')
+ {
+ ereport(LOG,
+ (errmsg("invalid character in username for LDAP authentication")));
+ return STATUS_ERROR;
+ }
+ }
+
+ /*
+ * 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);
+
+ 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_ERROR_NUMBER, &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,9 +2378,12 @@ CheckLDAPAuth(Port *port)
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 */