[PATCH] smb_register_idmap()

Stefan (metze) Metzmacher metze at metzemix.de
Wed May 14 08:06:46 GMT 2003


Hi Simo,

here's the smb_register_idmap() patch...

and a fixed version of idmap_ldap.c
- but it needs to converted to the new schema system
- and I'm not sure if there're some ldap_free_*()'s missing in some places...
   Anthony can you check that please.


metze
-----------------------------------------------------------------------------
Stefan "metze" Metzmacher <metze at metzemix.de> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 3_0-idmap-01.diff
Type: application/octet-stream
Size: 13959 bytes
Desc: not available
Url : http://lists.samba.org/archive/samba-technical/attachments/20030514/3e989da8/3_0-idmap-01.obj
-------------- next part --------------
/* 
   Unix SMB/CIFS implementation.

   idmap LDAP backend

   Copyright (C) Tim Potter 2000
   Copyright (C) Anthony Liguori 2003
   Copyright (C) Simo Sorce 2003
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"

#undef DBGC_CLASS
#define DBGC_CLASS DBGC_IDMAP


#include <lber.h>
#include <ldap.h>

struct ldap_idmap_state {
	LDAP *ldap_struct;
	time_t last_ping;
	const char *uri;
	char *bind_dn;
	char *bind_secret;
	unsigned int num_failures;
	struct ldap_idmap_state *prev, *next;
};

#define LDAP_IDMAP_DONT_PING_TIME 10       /* ping only all 10 seconds */
#define LDAP_MAX_ALLOC_ID 128              /* number tries while allocating
					      new id */

static struct ldap_idmap_state ldap_state;

static int ldap_idmap_connect_system(struct ldap_idmap_state *state);
static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type);
static NTSTATUS ldap_idmap_close(void);


/*******************************************************************
 find the ldap password
******************************************************************/
static BOOL fetch_ldapsam_pw(char **dn, char** pw)
{
	char *key = NULL;
	size_t size;
	
	*dn = smb_xstrdup(lp_ldap_admin_dn());
	
	if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) {
		SAFE_FREE(*dn);
		DEBUG(0, ("fetch_ldapsam_pw: asprintf failed!\n"));
	}
	
	*pw=secrets_fetch(key, &size);
	SAFE_FREE(key);

	if (!size) {
		/* Upgrade 2.2 style entry */
		char *p;
	        char* old_style_key = strdup(*dn);
		char *data;
		fstring old_style_pw;
		
		if (!old_style_key) {
			DEBUG(0, ("fetch_ldapsam_pw: strdup failed!\n"));
			return False;
		}

		for (p=old_style_key; *p; p++)
			if (*p == ',') *p = '/';
	
		data=secrets_fetch(old_style_key, &size);
		if (!size && size < sizeof(old_style_pw)) {
			DEBUG(0,("fetch_ldap_pw: neither ldap secret retrieved!\n"));
			SAFE_FREE(old_style_key);
			SAFE_FREE(*dn);
			return False;
		}

		strncpy(old_style_pw, data, size);
		old_style_pw[size] = 0;

		SAFE_FREE(data);

		if (!secrets_store_ldap_pw(*dn, old_style_pw)) {
			DEBUG(0,("fetch_ldap_pw: ldap secret could not be upgraded!\n"));
			SAFE_FREE(old_style_key);
			SAFE_FREE(*dn);
			return False;			
		}
		if (!secrets_delete(old_style_key)) {
			DEBUG(0,("fetch_ldap_pw: old ldap secret could not be deleted!\n"));
		}

		SAFE_FREE(old_style_key);

		*pw = smb_xstrdup(old_style_pw);		
	}
	
	return True;
}

/*******************************************************************
 open a connection to the ldap server.
******************************************************************/
static int ldap_idmap_open_connection(struct ldap_idmap_state *state)
{
	int rc = LDAP_SUCCESS;
	int version;
	BOOL ldap_v3 = False;

#ifdef HAVE_LDAP_INITIALIZE
	DEBUG(10, ("ldap_idmap_open_connection: %s\n", state->uri));
	
	if ((rc = ldap_initialize(&state->ldap_struct, state->uri)) 
	    != LDAP_SUCCESS) {
		DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc)));
		return rc;
	}
#else 
	/* Parse the string manually */
	{
		int port = 0;
		fstring protocol;
		fstring host;
		const char *p = state->uri; 
		SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254);
		
		/* skip leading "URL:" (if any) */
		if ( strncasecmp( p, "URL:", 4 ) == 0 ) {
			p += 4;
		}
		
		sscanf(p, "%10[^:]://%254s[^:]:%d", protocol, host, &port);
		
		if (port == 0) {
			if (strequal(protocol, "ldap")) {
				port = LDAP_PORT;
			} else if (strequal(protocol, "ldaps")) {
				port = LDAPS_PORT;
			} else {
				DEBUG(0, ("unrecognised protocol (%s)!\n",
					  protocol));
			}
		}
		
		if ((state->ldap_struct = ldap_init(host, port)) == NULL) {
			DEBUG(0, ("ldap_init failed !\n"));
			return LDAP_OPERATIONS_ERROR;
		}
		
	        if (strequal(protocol, "ldaps")) {
#ifdef LDAP_OPT_X_TLS
			int tls = LDAP_OPT_X_TLS_HARD;
			if (ldap_set_option (state->ldap_struct, 
					     LDAP_OPT_X_TLS, &tls) != 
			    LDAP_SUCCESS)
			{
				DEBUG(0, ("Failed to setup a TLS session\n"));
			}
			
			DEBUG(3,("LDAPS option set...!\n"));
#else
			DEBUG(0,("ldap_idmap_open_connection: Secure "
				 "connection not supported by LDAP client "
				 "libraries!\n"));
			return LDAP_OPERATIONS_ERROR;
#endif
		}
	}
#endif

	if (ldap_get_option(state->ldap_struct, LDAP_OPT_PROTOCOL_VERSION,
			    &version) == LDAP_OPT_SUCCESS) {
		if (version != LDAP_VERSION3) {
			version = LDAP_VERSION3;
			if (ldap_set_option(state->ldap_struct,
					    LDAP_OPT_PROTOCOL_VERSION,
					    &version) == LDAP_OPT_SUCCESS) {
				ldap_v3 = True;
			}
		} else {
			ldap_v3 = True;
		}
	}

	if (lp_ldap_ssl() == LDAP_SSL_START_TLS) {
#ifdef LDAP_OPT_X_TLS
		if (ldap_v3) {
			if ((rc = ldap_start_tls_s(state->ldap_struct, NULL,
						   NULL)) != LDAP_SUCCESS) {
				DEBUG(0,("Failed to issue the StartTLS "
					 "instruction: %s\n",
					 ldap_err2string(rc)));
				return rc;
			}
			DEBUG (3, ("StartTLS issued: using a TLS "
				   "connection\n"));
		} else {
			
			DEBUG(0, ("Need LDAPv3 for Start TLS\n"));
			return LDAP_OPERATIONS_ERROR;
		}
#else
		DEBUG(0,("ldap_idmap_open_connection: StartTLS not supported by "
			 "LDAP client libraries!\n"));
		return LDAP_OPERATIONS_ERROR;
#endif
	}

	DEBUG(2, ("ldap_idmap_open_connection: connection opened\n"));
	return rc;
}

/**********************************************************************
Connect to LDAP server 
*********************************************************************/
static int ldap_idmap_open(struct ldap_idmap_state *state)
{
	int rc;
	SMB_ASSERT(state);
		
#ifndef NO_LDAP_SECURITY
	if (geteuid() != 0) {
		DEBUG(0, 
		      ("ldap_idmap_open: cannot access LDAP when not root\n"));
		return  LDAP_INSUFFICIENT_ACCESS;
	}
#endif

	if ((state->ldap_struct != NULL) && 
	    ((state->last_ping + LDAP_IDMAP_DONT_PING_TIME)<time(NULL))) {
		struct sockaddr_un addr;
		socklen_t len = sizeof(addr);
		int sd;

		if (!ldap_get_option(state->ldap_struct,  LDAP_OPT_DESC, &sd)&&
		    getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
		    	/* the other end has died. reopen. */
		    	ldap_unbind_ext(state->ldap_struct, NULL, NULL);
		    	state->ldap_struct = NULL;
		    	state->last_ping = (time_t)0;
		} else {
			state->last_ping = time(NULL);
		} 
    	}

	if (state->ldap_struct != NULL) {
		DEBUG(5,("ldap_idmap_open: already connected to the LDAP "
			 "server\n"));
		return LDAP_SUCCESS;
	}

	if ((rc = ldap_idmap_open_connection(state))) {
		return rc;
	}

	if ((rc = ldap_idmap_connect_system(state))) {
		ldap_unbind_ext(state->ldap_struct, NULL, NULL);
		state->ldap_struct = NULL;
		return rc;
	}


	state->last_ping = time(NULL);
	DEBUG(4,("The LDAP server is succesful connected\n"));

	return LDAP_SUCCESS;
}

static int ldap_idmap_retry_open(struct ldap_idmap_state *state, int *attempts)
{
	int rc;

	SMB_ASSERT(state && attempts);

	if (*attempts != 0) {
		unsigned int sleep_time;
		uint8 rand_byte = 128; /* a reasonable place to start */

		generate_random_buffer(&rand_byte, 1, False);

		sleep_time = (((*attempts)*(*attempts))/2)*rand_byte*2; 
		/* we retry after (0.5, 1, 2, 3, 4.5, 6) seconds
		   on average.  
		 */
		DEBUG(3, ("Sleeping for %u milliseconds before reconnecting\n",
			  sleep_time));
		msleep(sleep_time);
	}
	(*attempts)++;

	if ((rc = ldap_idmap_open(state))) {
		DEBUG(1,("Connection to LDAP Server failed for the %d try!\n",
			 *attempts));
		return rc;
	} 
	
	return LDAP_SUCCESS;		
}

/*******************************************************************
 a rebind function for authenticated referrals
 This version takes a void* that we can shove useful stuff in :-)
******************************************************************/
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
#else
static int rebindproc_with_state  (LDAP * ld, char **whop, char **credp, 
				   int *methodp, int freeit, void *arg)
{
	struct ldap_idmap_state *state = arg;
	
	/** @TODO Should we be doing something to check what servers we rebind
	    to?  Could we get a referral to a machine that we don't want to
	    give our username and password to? */
	
	if (freeit) {
		SAFE_FREE(*whop);
		memset(*credp, '\0', strlen(*credp));
		SAFE_FREE(*credp);
	} else {
		DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n", 
			  state->bind_dn));

		*whop = strdup(state->bind_dn);
		if (!*whop) {
			return LDAP_NO_MEMORY;
		}
		*credp = strdup(state->bind_secret);
		if (!*credp) {
			SAFE_FREE(*whop);
			return LDAP_NO_MEMORY;
		}
		*methodp = LDAP_AUTH_SIMPLE;
	}
	return 0;
}
#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/

/*******************************************************************
 a rebind function for authenticated referrals
 This version takes a void* that we can shove useful stuff in :-)
 and actually does the connection.
******************************************************************/
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
static int rebindproc_connect_with_state (LDAP *ldap_struct, 
					  LDAP_CONST char *url, 
					  ber_tag_t request,
					  ber_int_t msgid, void *arg)
{
	struct ldap_idmap_state *state = arg;
	int rc;
	DEBUG(5,("rebindproc_connect_with_state: Rebinding as \"%s\"\n", 
		 state->bind_dn));
	
	/** @TODO Should we be doing something to check what servers we rebind
	    to?  Could we get a referral to a machine that we don't want to
	    give our username and password to? */

	rc = ldap_simple_bind_s(ldap_struct, state->bind_dn, 
				state->bind_secret);
	
	return rc;
}
#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/

/*******************************************************************
 Add a rebind function for authenticated referrals
******************************************************************/
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
#else
# if LDAP_SET_REBIND_PROC_ARGS == 2
static int rebindproc (LDAP *ldap_struct, char **whop, char **credp,
		       int *method, int freeit )
{
	return rebindproc_with_state(ldap_struct, whop, credp,
				   method, freeit, &ldap_state);
	
}
# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/

/*******************************************************************
 a rebind function for authenticated referrals
 this also does the connection, but no void*.
******************************************************************/
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
# if LDAP_SET_REBIND_PROC_ARGS == 2
static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request,
			       ber_int_t msgid)
{
	return rebindproc_connect_with_state(ld, url, (ber_tag_t)request,
					     msgid, &ldap_state);
}
# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/

/*******************************************************************
 connect to the ldap server under system privilege.
******************************************************************/
static int ldap_idmap_connect_system(struct ldap_idmap_state *state)
{
	int rc;
	char *ldap_dn;
	char *ldap_secret;

	/* get the password */
	if (!fetch_ldapsam_pw(&ldap_dn, &ldap_secret))
	{
		DEBUG(0, ("ldap_idmap_connect_system: Failed to retrieve "
			  "password from secrets.tdb\n"));
		return LDAP_INVALID_CREDENTIALS;
	}

	state->bind_dn = ldap_dn;
	state->bind_secret = ldap_secret;

	/* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite 
	   (OpenLDAP) doesnt' seem to support it */
	   
	DEBUG(10,("ldap_idmap_connect_system: Binding to ldap server %s as "
		  "\"%s\"\n", state->uri, ldap_dn));

#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
# if LDAP_SET_REBIND_PROC_ARGS == 2	
	ldap_set_rebind_proc(state->ldap_struct, &rebindproc_connect);	
# endif
# if LDAP_SET_REBIND_PROC_ARGS == 3	
	ldap_set_rebind_proc(state->ldap_struct, 
			     &rebindproc_connect_with_state, (void *)state);
# endif
#else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
# if LDAP_SET_REBIND_PROC_ARGS == 2	
	ldap_set_rebind_proc(state->ldap_struct, &rebindproc);	
# endif
# if LDAP_SET_REBIND_PROC_ARGS == 3	
	ldap_set_rebind_proc(state->ldap_struct, &rebindproc_with_state,
			     (void *)state);	
# endif
#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/

	rc = ldap_simple_bind_s(state->ldap_struct, ldap_dn, ldap_secret);

	if (rc != LDAP_SUCCESS) {
		char *ld_error = NULL;
		ldap_get_option(state->ldap_struct, LDAP_OPT_ERROR_STRING,
				&ld_error);
		DEBUG(state->num_failures ? 2 : 0,
		      ("failed to bind to server with dn= %s Error: "
		       "%s\n\t%s\n",
		       ldap_dn ? ld_error : "(unknown)",
		       ldap_err2string(rc), ld_error));
		SAFE_FREE(ld_error);
		state->num_failures++;
		return rc;
	}

	state->num_failures = 0;

	DEBUG(3, ("ldap_idmap_connect_system: succesful connection to the "
		  "LDAP server\n"));
	return rc;
}

static int ldap_idmap_search(struct ldap_idmap_state *state, 
			     const char *base, int scope, const char *filter, 
			     const char *attrs[], int attrsonly, 
			     LDAPMessage **res)
{
	int rc = LDAP_SERVER_DOWN;
	int attempts = 0;
	char *utf8_filter;

	SMB_ASSERT(state);

	if (push_utf8_allocate(&utf8_filter, filter) == (size_t)-1) {
		return LDAP_NO_MEMORY;
	}

	while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) {
		if ((rc = ldap_idmap_retry_open(state, &attempts)) !=
		    LDAP_SUCCESS) continue;
		
		rc = ldap_search_s(state->ldap_struct, base, scope, 
				   utf8_filter, (char**)attrs, attrsonly, res);
	}
	
	if (rc == LDAP_SERVER_DOWN) {
		DEBUG(0,("ldap_idmap_search: LDAP server is down!\n"));
		ldap_idmap_close();
	}

	SAFE_FREE(utf8_filter);
	return rc;
}

/*******************************************************************
search an attribute and return the first value found.
******************************************************************/
static BOOL ldap_idmap_attribute (struct ldap_idmap_state *state,
				  LDAPMessage * entry,
				  const char *attribute, pstring value)
{
	char **values;
	value[0] = '\0';

	if ((values = ldap_get_values (state->ldap_struct, entry, attribute))
	    == NULL) {
		DEBUG(10,("get_single_attribute: [%s] = [<does not exist>]\n",
			  attribute));
		return False;
	}
	if (convert_string(CH_UTF8, CH_UNIX,
		values[0], -1,
		value, sizeof(pstring)) == (size_t)-1)
	{
		DEBUG(1, ("ldap_idmap_attribute: string conversion of [%s] = "
			  "[%s] failed!\n",  attribute, values[0]));
		ldap_value_free(values);
		return False;
	}
	ldap_value_free(values);

	return True;
}

static const char *attrs[] = {"objectClass", "uidNumber", "gidNumber", 
			      "ntSid", NULL};
static const char *pool_attr[] = {"uidNumber", "gidNumber", NULL};

static NTSTATUS ldap_allocate_id(unid_t *id, int id_type)
{
	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
	int rc = LDAP_SERVER_DOWN;
	int count = 0;
	LDAPMessage *result = 0;
	LDAPMessage *entry = 0;
	pstring id_str, new_id_str;
	LDAPMod mod[2];
	LDAPMod *mods[3];
	const char *type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
	char *val[4];
	char *dn;

	rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(),
			       LDAP_SCOPE_SUBTREE, "(objectClass=unixIdPool)",
			       pool_attr, 0, &result);
	if (rc != LDAP_SUCCESS) {
		DEBUG(0,("ldap_allocate_id: unixIdPool object not found\n"));
		goto out;
	}
	
	count = ldap_count_entries(ldap_state.ldap_struct, result);
	if (count != 1) {
		DEBUG(0,("ldap_allocate_id: single unixIdPool not found\n"));
		goto out;
	}

	dn = ldap_get_dn(ldap_state.ldap_struct, result);
	entry = ldap_first_entry(ldap_state.ldap_struct, result);

	if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) {
		DEBUG(0,("ldap_allocate_id: %s attribute not found\n",
			 type));
		goto out;
	}
	if (id_type & ID_USERID) {
		id->uid = strtoul(id_str, NULL, 10);
	} else {
		id->gid = strtoul(id_str, NULL, 10);
	}

	mod[0].mod_op = LDAP_MOD_DELETE;
	mod[0].mod_type = strdup(type);
	val[0] = id_str; val[1] = NULL;
	mod[0].mod_values = val;

	pstr_sprintf(new_id_str, "%ud", 
		 ((id_type & ID_USERID) ? id->uid : id->gid) + 1);
	mod[1].mod_op = LDAP_MOD_ADD;
	mod[1].mod_type = strdup(type);
	val[3] = new_id_str; val[4] = NULL;
	mod[1].mod_values = val + 2;

	mods[0] = mod; mods[1] = mod + 1; mods[2] = NULL;
	rc = ldap_modify_s(ldap_state.ldap_struct, dn, mods);
	ldap_memfree(dn);

	if (rc == LDAP_SUCCESS) ret = NT_STATUS_OK;
out:
	return ret;
}

/* Get a sid from an id */
static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
{
	LDAPMessage *result = 0;
	LDAPMessage *entry = 0;
	pstring sid_str;
	pstring filter;
	char type = (id_type & ID_USERID) ? 'u' : 'g';
	int rc;
	int count;
	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
	   
	pstr_sprintf(filter, "(&(%cidNumber=%ud)(objectClass=sambaAccount))",
		 type, ((id_type & ID_USERID) ? id.uid : id.gid));
	rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), 
			       LDAP_SCOPE_SUBTREE, filter, attrs, 0, 
			       &result);
	if (rc != LDAP_SUCCESS) {
		goto out;
	}
	
	count = ldap_count_entries(ldap_state.ldap_struct, result);
	if (count == 0) {
		   pstr_sprintf(filter,
			    "(&(objectClass=idmapEntry)(%cidNumber=%ud))",
			    type, ((id_type & ID_USERID) ? id.uid : id.gid));
		   rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(),
					  LDAP_SCOPE_SUBTREE, filter,
					  attrs, 0, &result);
		   if (rc != LDAP_SUCCESS) {
			   goto out;
		   }
		   count = ldap_count_entries(ldap_state.ldap_struct, result);
	}
	
	if (count != 1) {
		DEBUG(0,("ldap_get_sid_from_id: mapping not found for "
			 "%cid: %ud\n", (id_type&ID_USERID)?'u':'g',
			 ((id_type & ID_USERID) ? id.uid : id.gid)));
		goto out;
	}
	
	entry = ldap_first_entry(ldap_state.ldap_struct, result);
	
	if (!ldap_idmap_attribute(&ldap_state, entry, "ntSid", sid_str)) {
		goto out;
	}
	   
	if (!string_to_sid(sid, sid_str)) {
		goto out;
	}

	ret = NT_STATUS_OK;
out:
	return ret;
}

/* Get an id from a sid */
static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type,
				     const DOM_SID *sid)
{
	LDAPMessage *result = 0;
	LDAPMessage *entry = 0;
	pstring sid_str;
	pstring filter;
	pstring id_str;
	const char *type = (*id_type & ID_USERID) ? "uidNumber" : "gidNumber";
	const char *class =
		(*id_type & ID_USERID) ? "sambaAccount" : "sambaGroupMapping";
	int rc;
	int count;
	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
	   
	sid_to_string(sid_str, sid);
	pstr_sprintf(filter, "(&(objectClass=%s)(ntSid=%s)", class, sid_str);
	rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), 
			       LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
	if (rc != LDAP_SUCCESS) {
		goto out;
	}
	count = ldap_count_entries(ldap_state.ldap_struct, result);
	if (count == 0) {
		   pstr_sprintf(filter,
			    "(&(objectClass=idmapEntry)(ntSid=%s))", sid_str);

		   rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(),
					  LDAP_SCOPE_SUBTREE, filter,
					  attrs, 0, &result);
		   if (rc != LDAP_SUCCESS) {
			   goto out;
		   }
		   count = ldap_count_entries(ldap_state.ldap_struct, result);
	}

	/* our search filters may 2 objects in the case that a user and group
	   rid are the same */
	if (count != 1 && count != 2) {
		DEBUG(0,
		      ("ldap_get_id_from_sid: incorrect number of objects\n"));
		goto out;
	}

	entry = ldap_first_entry(ldap_state.ldap_struct, result);
	if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) {
		entry = ldap_next_entry(ldap_state.ldap_struct, entry);

		if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) {
			int i;

			for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) {
				ret = ldap_allocate_id(id, *id_type);
				if (NT_STATUS_IS_OK(ret)) {
					break;
				}
			}
			if (NT_STATUS_IS_OK(ret)) {
				ret = ldap_set_mapping(sid, *id, *id_type);
			} else {
				DEBUG(0,("ldap_allocate_id: cannot acquire id"
					 " lock\n"));
			}
		} else {
			if ((*id_type & ID_USERID)) {
				id->uid = strtoul(id_str, NULL, 10);
			} else {
				id->gid = strtoul(id_str, NULL, 10);
			}
			ret = NT_STATUS_OK;
		}
	} else {
		if ((*id_type & ID_USERID)) {
			id->uid = strtoul(id_str, NULL, 10);
		} else {
			id->gid = strtoul(id_str, NULL, 10);
		}
		ret = NT_STATUS_OK;
	}
out:
	return ret;
}

/* This function cannot be called to modify a mapping, only set a new one */
static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
{
	pstring dn, sid_str, id_str;
	const char *type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber";
	LDAPMod *mods[3];
	LDAPMod mod[2];
	char *val[4];
	int rc;
	int attempts = 0;

	pstr_sprintf(id_str, "%ud", ((id_type & ID_USERID) ? id.uid : id.gid));
	sid_to_string(sid_str, sid);
	pstr_sprintf(dn, "%s=%ud,%s", type, ((id_type & ID_USERID) ? id.uid : id.gid), lp_ldap_suffix());
	mod[0].mod_op = LDAP_MOD_REPLACE;
	mod[0].mod_type = strdup(type);
	val[0] = id_str; val[1] = NULL;
	mod[0].mod_values = val;

	mod[1].mod_op = LDAP_MOD_REPLACE;
	mod[1].mod_type = strdup("ntSid");
	val[2] = sid_str; val[3] = NULL;
	mod[1].mod_values = val + 2;

	mods[0] = mod; mods[1] = mod + 1; mods[2] = NULL;

	do {
		if ((rc = ldap_idmap_retry_open(&ldap_state, &attempts)) !=
		    LDAP_SUCCESS) continue;
		
		rc = ldap_modify_s(ldap_state.ldap_struct, dn, mods);
	} while ((rc == LDAP_SERVER_DOWN) && (attempts <= 8));

	if (rc != LDAP_SUCCESS) {
		return NT_STATUS_UNSUCCESSFUL;
	}

	return NT_STATUS_OK;
}

/*****************************************************************************
 Initialise idmap database. 
*****************************************************************************/
static NTSTATUS ldap_idmap_init(void)
{
	/* We wait for the first search request before we try to connect to
	   the LDAP server.  We may want to connect upon initialization though
	   -- aliguori */
	return NT_STATUS_OK;
}

/* End the LDAP session */
static NTSTATUS ldap_idmap_close(void)
{
	if (ldap_state.ldap_struct != NULL) {
		ldap_unbind_ext(ldap_state.ldap_struct, NULL, NULL);
		ldap_state.ldap_struct = NULL;
	}
	
	DEBUG(5,("The connection to the LDAP server was closed\n"));
	/* maybe free the results here --metze */
	
	return NT_STATUS_OK;
}


/* This function doesn't make as much sense in an LDAP world since the calling
   node doesn't really control the ID ranges */
static void ldap_idmap_status(void)
{
	DEBUG(0, ("LDAP IDMAP Status not available\n"));
}

static struct idmap_methods ldap_methods = {
	ldap_idmap_init,
	ldap_get_sid_from_id,
	ldap_get_id_from_sid,
	ldap_set_mapping,
	ldap_idmap_close,
	ldap_idmap_status

};

NTSTATUS idmap_ldap_init(void)
{
	DEBUG(0,("idmap_reg_ldap: no LDAP support\n"));
	return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", &ldap_methods);
}


More information about the samba-technical mailing list