ldap_search.patch

application/octet-stream

Filename: ldap_search.patch
Type: application/octet-stream
Part: 0
Message: Re: LDAP where DN does not include UID attribute

Patch

Format: context
File+
src/backend/libpq/auth.c 182 0
src/backend/libpq/hba.c 20 0
src/include/libpq/hba.h 4 0
*** 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;