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;
		int			count;

		/*
		 * 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 user name 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\": %s",
							port->hba->ldapbinddn, port->hba->ldapserver, ldap_err2string(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 = psprintf("(%s=%s)",
						  attributes[0],
						  port->user_name);

		r = ldap_search_s(ldap,
						  port->hba->ldapbasedn,
						  port->hba->ldapscope,
						  filter,
						  attributes,
						  0,
						  &search_message);

		if (r != LDAP_SUCCESS)
		{
			ereport(LOG,
					(errmsg("could not search LDAP for filter \"%s\" on server \"%s\": %s",
						filter, port->hba->ldapserver, ldap_err2string(r))));
			pfree(filter);
			return STATUS_ERROR;
		}

		count = ldap_count_entries(ldap, search_message);
		if (count != 1)
		{
			if (count == 0)
				ereport(LOG,
				 (errmsg("LDAP user \"%s\" does not exist", port->user_name),
				  errdetail("LDAP search for filter \"%s\" on server \"%s\" returned no entries.",
							filter, port->hba->ldapserver)));
			else
				ereport(LOG,
				  (errmsg("LDAP user \"%s\" is not unique", port->user_name),
				   errdetail_plural("LDAP search for filter \"%s\" on server \"%s\" returned %d entry.",
									"LDAP search for filter \"%s\" on server \"%s\" returned %d entries.",
									count,
									filter, port->hba->ldapserver, count)));

			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 = psprintf("%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);

	if (r != LDAP_SUCCESS)
	{
		ereport(LOG,
			(errmsg("LDAP login failed for user \"%s\" on server \"%s\": %s",
					fulluser, port->hba->ldapserver, ldap_err2string(r))));
		pfree(fulluser);
		return STATUS_ERROR;
	}

	pfree(fulluser);

	return STATUS_OK;
}