Esempio n. 1
0
NTSTATUS sec_ace_del_sid(TALLOC_CTX *ctx, SEC_ACE **pp_new, SEC_ACE *old, uint32 *num, DOM_SID *sid)
{
	unsigned int i     = 0;
	unsigned int n_del = 0;

	if (!ctx || !pp_new || !old || !sid || !num)  return NT_STATUS_INVALID_PARAMETER;

	if (*num) {
		if((pp_new[0] = TALLOC_ZERO_ARRAY(ctx, SEC_ACE, *num )) == 0)
			return NT_STATUS_NO_MEMORY;
	} else {
		pp_new[0] = NULL;
	}

	for (i = 0; i < *num; i ++) {
		if (sid_compare(&old[i].trustee, sid) != 0)
			sec_ace_copy(&(*pp_new)[i], &old[i]);
		else
			n_del ++;
	}
	if (n_del == 0)
		return NT_STATUS_NOT_FOUND;
	else {
		*num -= n_del;
		return NT_STATUS_OK;
	}
}
Esempio n. 2
0
void init_unistr2_w(TALLOC_CTX *ctx, UNISTR2 *str, const smb_ucs2_t *buf)
{
	uint32 len = buf ? strlen_w(buf) : 0;

	ZERO_STRUCTP(str);

	/* set up string lengths. */
	str->uni_max_len = len;
	str->offset = 0;
	str->uni_str_len = len;

	str->buffer = TALLOC_ZERO_ARRAY(ctx, uint16, len + 1);
	if (str->buffer == NULL) {
		smb_panic("init_unistr2_w: talloc fail\n");
		return;
	}
	
	/*
	 * don't move this test above ! The UNISTR2 must be initialized !!!
	 * jfm, 7/7/2001.
	 */
	if (buf==NULL)
		return;
	
	/* Yes, this is a strncpy( foo, bar, strlen(bar)) - but as
           long as the buffer above is talloc()ed correctly then this
           is the correct thing to do */
	strncpy_w(str->buffer, buf, len + 1);
}
Esempio n. 3
0
void init_unistr2_from_unistr(UNISTR2 *to, const UNISTR *from)
{
	uint32 i;

	/* the destination UNISTR2 should never be NULL.
	   if it is it is a programming error */

	/* if the source UNISTR is NULL, then zero out
	   the destination string and return */
	ZERO_STRUCTP (to);
	if ((from == NULL) || (from->buffer == NULL))
		return;

	/* get the length; UNISTR must be NULL terminated */
	i = 0;
	while ((from->buffer)[i]!='\0')
		i++;
	i++;	/* one more to catch the terminating NULL */
		/* is this necessary -- jerry?  I need to think */

	/* set up string lengths; uni_max_len is set to i+1
           because we need to account for the final NULL termination */
	to->uni_max_len = i;
	to->offset = 0;
	to->uni_str_len = i;

	/* allocate the space and copy the string buffer */
	to->buffer = TALLOC_ZERO_ARRAY(get_talloc_ctx(), uint16, i);
	if (to->buffer == NULL)
		smb_panic("init_unistr2_from_unistr: malloc fail\n");
	memcpy(to->buffer, from->buffer, i*sizeof(uint16));
	return;
}
Esempio n. 4
0
void copy_unistr2(UNISTR2 *str, const UNISTR2 *from)
{
	if (from->buffer == NULL) {
		ZERO_STRUCTP(str);
		return;
	}

	SMB_ASSERT(from->uni_max_len >= from->uni_str_len);

	str->uni_max_len = from->uni_max_len;
	str->offset      = from->offset;
	str->uni_str_len = from->uni_str_len;

	/* the string buffer is allocated to the maximum size
	   (the the length of the source string) to prevent
	   reallocation of memory. */
	if (str->buffer == NULL) {
   		str->buffer = (uint16 *)TALLOC_ZERO_ARRAY(get_talloc_ctx(), uint16, str->uni_max_len);
		if ((str->buffer == NULL)) {
			smb_panic("copy_unistr2: talloc fail\n");
			return;
		}
	}

	/* copy the string */
	memcpy(str->buffer, from->buffer, str->uni_max_len*sizeof(uint16));
}
Esempio n. 5
0
BOOL prs_unistr4_array(const char *desc, prs_struct *ps, int depth, UNISTR4_ARRAY *array )
{
	unsigned int i;

	prs_debug(ps, depth, desc, "prs_unistr4_array");
	depth++;

	if(!prs_uint32("count", ps, depth, &array->count))
		return False;

	if ( array->count == 0 ) 
		return True;
	
	if (UNMARSHALLING(ps)) {
		if ( !(array->strings = TALLOC_ZERO_ARRAY( get_talloc_ctx(), UNISTR4, array->count)) )
			return False;
	}
	
	/* write the headers and then the actual string buffer */
	
	for ( i=0; i<array->count; i++ ) {
		if ( !prs_unistr4_hdr( "string", ps, depth, &array->strings[i]) )
			return False;
	}

	for (i=0;i<array->count;i++) {
		if ( !prs_unistr4_str("string", ps, depth, &array->strings[i]) ) 
			return False;
	}
	
	return True;
}
Esempio n. 6
0
void init_unistr2(UNISTR2 *str, const char *buf, enum unistr2_term_codes flags)
{
	size_t len = 0;
	uint32 num_chars = 0;

	if (buf) {
		/* We always null terminate the copy. */
		len = strlen(buf) + 1;
		if ( flags == UNI_STR_DBLTERMINATE )
			len++;
	} else {
		/* no buffer -- nothing to do */
		str->uni_max_len = 0;
		str->offset = 0;
		str->uni_str_len = 0;

		return;
	}
	

	str->buffer = TALLOC_ZERO_ARRAY(get_talloc_ctx(), uint16, len);
	if (str->buffer == NULL) {
		smb_panic("init_unistr2: malloc fail\n");
		return;
	}

	/* Ensure len is the length in *bytes* */
	len *= sizeof(uint16);

	/*
	 * The UNISTR2 must be initialized !!!
	 * jfm, 7/7/2001.
	 */
	if (buf) {
		rpcstr_push((char *)str->buffer, buf, len, STR_TERMINATE);
		num_chars = strlen_w(str->buffer);
		if (flags == UNI_STR_TERMINATE || flags == UNI_MAXLEN_TERMINATE) {
			num_chars++;
		}
		if ( flags == UNI_STR_DBLTERMINATE )
			num_chars += 2;
	}

	str->uni_max_len = num_chars;
	str->offset = 0;
	str->uni_str_len = num_chars;
	if ( num_chars && ((flags == UNI_MAXLEN_TERMINATE) || (flags == UNI_BROKEN_NON_NULL)) )
		str->uni_max_len++;
}
Esempio n. 7
0
void init_unistr3(UNISTR3 *str, const char *buf)
{
	if (buf == NULL) {
		str->uni_str_len=0;
		str->str.buffer = NULL;
		return;
	}

	str->uni_str_len = strlen(buf) + 1;

	str->str.buffer = TALLOC_ZERO_ARRAY(get_talloc_ctx(), uint16, str->uni_str_len);
	if (str->str.buffer == NULL)
		smb_panic("init_unistr3: malloc fail\n");

	rpcstr_push((char *)str->str.buffer, buf, str->uni_str_len * sizeof(uint16), STR_TERMINATE);
}
Esempio n. 8
0
void init_unistr(UNISTR *str, const char *buf)
{
	size_t len;

	if (buf == NULL) {
		str->buffer = NULL;
		return;
	}
		
	len = strlen(buf) + 1;

	str->buffer = TALLOC_ZERO_ARRAY(get_talloc_ctx(), uint16, len);
	if (str->buffer == NULL)
		smb_panic("init_unistr: malloc fail\n");

	rpcstr_push(str->buffer, buf, len*sizeof(uint16), STR_TERMINATE);
}
Esempio n. 9
0
BOOL init_unistr4_array( UNISTR4_ARRAY *array, uint32 count, const char **strings )
{
	unsigned int i;

	array->count = count;

	if ( array->count == 0 )
		return True;

	/* allocate memory for the array of UNISTR4 objects */

	if ( !(array->strings = TALLOC_ZERO_ARRAY(get_talloc_ctx(), UNISTR4, count )) )
		return False;

	for ( i=0; i<count; i++ ) 
		init_unistr4( &array->strings[i], strings[i], UNI_STR_TERMINATE );

	return True;
}
Esempio n. 10
0
NTSTATUS sec_ace_add_sid(TALLOC_CTX *ctx, SEC_ACE **pp_new, SEC_ACE *old, unsigned *num, DOM_SID *sid, uint32 mask)
{
	unsigned int i = 0;
	
	if (!ctx || !pp_new || !old || !sid || !num)  return NT_STATUS_INVALID_PARAMETER;

	*num += 1;
	
	if((pp_new[0] = TALLOC_ZERO_ARRAY(ctx, SEC_ACE, *num )) == 0)
		return NT_STATUS_NO_MEMORY;

	for (i = 0; i < *num - 1; i ++)
		sec_ace_copy(&(*pp_new)[i], &old[i]);

	(*pp_new)[i].type  = 0;
	(*pp_new)[i].flags = 0;
	(*pp_new)[i].size  = SEC_ACE_HEADER_SIZE + sid_size(sid);
	(*pp_new)[i].access_mask = mask;
	sid_copy(&(*pp_new)[i].trustee, sid);
	return NT_STATUS_OK;
}
Esempio n. 11
0
static SEC_DESC* parse_acl_string(TALLOC_CTX *mem_ctx, const char *szACL, size_t *sd_size )
{
	SEC_DESC *sd = NULL;
	SEC_ACE *ace;
	SEC_ACL *acl;
	int num_ace;
	const char *pacl;
	int i;
	
	if ( !szACL )
		return NULL;

	pacl = szACL;
	num_ace = count_chars( pacl, ',' ) + 1;
	
	if ( !(ace = TALLOC_ZERO_ARRAY( mem_ctx, SEC_ACE, num_ace )) ) 
		return NULL;
	
	for ( i=0; i<num_ace; i++ ) {
		char *end_acl = strchr_m( pacl, ',' );
		fstring acl_string;

		strncpy( acl_string, pacl, MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1) );
		acl_string[MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1)] = '\0';
		
		if ( !parse_ace( &ace[i], acl_string ) )
			return NULL;

		pacl = end_acl;
		pacl++;
	}
	
	if ( !(acl = make_sec_acl( mem_ctx, NT4_ACL_REVISION, num_ace, ace )) )
		return NULL;
		
	sd = make_sec_desc( mem_ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, 
		NULL, NULL, NULL, acl, sd_size);

	return sd;
}
Esempio n. 12
0
RPC_DATA_BLOB *cac_MakeRpcDataBlob( TALLOC_CTX * mem_ctx, uint32 data_type,
				    REG_VALUE_DATA data )
{
	RPC_DATA_BLOB *blob = NULL;
	int i;
	uint32 size = 0;
	uint8 *multi = NULL;
	uint32 multi_idx = 0;

	blob = talloc( mem_ctx, RPC_DATA_BLOB );

	if ( !blob ) {
		errno = ENOMEM;
		return NULL;
	}

	switch ( data_type ) {
	case REG_SZ:
		init_rpc_blob_str( blob, data.reg_sz,
				   strlen( data.reg_sz ) + 1 );
		break;

	case REG_EXPAND_SZ:
		init_rpc_blob_str( blob, data.reg_expand_sz,
				   strlen( data.reg_sz ) + 1 );
		break;

	case REG_BINARY:
		init_rpc_blob_bytes( blob, data.reg_binary.data,
				     data.reg_binary.data_length );
		break;

	case REG_DWORD:
		init_rpc_blob_uint32( blob, data.reg_dword );
		break;

	case REG_DWORD_BE:
		init_rpc_blob_uint32( blob, data.reg_dword_be );
		break;

	case REG_MULTI_SZ:
		/*need to find the size */
		for ( i = 0; i < data.reg_multi_sz.num_strings; i++ ) {
			size += strlen( data.reg_multi_sz.strings[i] ) + 1;
		}

	 /**need a whole bunch of unicode strings in a row (seperated by null characters), with an extra null-character on the end*/

		multi = TALLOC_ZERO_ARRAY( mem_ctx, uint8, ( size + 1 ) * 2 );	/*size +1 for the extra null character */
		if ( !multi ) {
			errno = ENOMEM;
			break;
		}

		/*do it using rpcstr_push() */
		multi_idx = 0;
		for ( i = 0; i < data.reg_multi_sz.num_strings; i++ ) {
			size_t len =
				strlen( data.reg_multi_sz.strings[i] ) + 1;

			rpcstr_push( ( multi + multi_idx ),
				     data.reg_multi_sz.strings[i], len * 2,
				     STR_TERMINATE );

			/* x2 becuase it is a uint8 buffer */
			multi_idx += len * 2;
		}

		/*now initialize the buffer as binary data */
		init_rpc_blob_bytes( blob, multi, ( size + 1 ) * 2 );

		break;

	default:
		TALLOC_FREE( blob );
		blob = NULL;
		return NULL;
	}

	if ( !( blob->buffer ) ) {
		TALLOC_FREE( blob );
		return NULL;
	}

	return blob;
}
Esempio n. 13
0
static NTSTATUS ldif_init_context(TALLOC_CTX *mem_ctx,
				  enum netr_SamDatabaseID database_id,
				  const char *ldif_filename,
				  const char *domain_sid_str,
				  struct samsync_ldif_context **ctx)
{
	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
	struct samsync_ldif_context *r;
	const char *add_template = "/tmp/add.ldif.XXXXXX";
	const char *mod_template = "/tmp/mod.ldif.XXXXXX";
	const char *builtin_sid = "S-1-5-32";

	/* Get other smb.conf data */
	if (!(lp_workgroup()) || !*(lp_workgroup())) {
		DEBUG(0,("workgroup missing from smb.conf--exiting\n"));
		exit(1);
	}

	/* Get the ldap suffix */
	if (!(lp_ldap_suffix()) || !*(lp_ldap_suffix())) {
		DEBUG(0,("ldap suffix missing from smb.conf--exiting\n"));
		exit(1);
	}

	if (*ctx && (*ctx)->initialized) {
		return NT_STATUS_OK;
	}

	r = TALLOC_ZERO_P(mem_ctx, struct samsync_ldif_context);
	NT_STATUS_HAVE_NO_MEMORY(r);

	/* Get the ldap suffix */
	r->suffix = lp_ldap_suffix();

	/* Ensure we have an output file */
	if (ldif_filename) {
		r->ldif_file = fopen(ldif_filename, "a");
	} else {
		r->ldif_file = stdout;
	}

	if (!r->ldif_file) {
		fprintf(stderr, "Could not open %s\n", ldif_filename);
		DEBUG(1, ("Could not open %s\n", ldif_filename));
		status = NT_STATUS_UNSUCCESSFUL;
		goto done;
	}

	r->add_template = talloc_strdup(mem_ctx, add_template);
	r->mod_template = talloc_strdup(mem_ctx, mod_template);
	if (!r->add_template || !r->mod_template) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	r->add_name = talloc_strdup(mem_ctx, add_template);
	r->mod_name = talloc_strdup(mem_ctx, mod_template);
	if (!r->add_name || !r->mod_name) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	/* Open the add and mod ldif files */
	if (!(r->add_file = fdopen(smb_mkstemp(r->add_name),"w"))) {
		DEBUG(1, ("Could not open %s\n", r->add_name));
		status = NT_STATUS_UNSUCCESSFUL;
		goto done;
	}
	if (!(r->mod_file = fdopen(smb_mkstemp(r->mod_name),"w"))) {
		DEBUG(1, ("Could not open %s\n", r->mod_name));
		status = NT_STATUS_UNSUCCESSFUL;
		goto done;
	}

	/* Allocate initial memory for groupmap and accountmap arrays */
	r->groupmap = TALLOC_ZERO_ARRAY(mem_ctx, GROUPMAP, 8);
	r->accountmap = TALLOC_ZERO_ARRAY(mem_ctx, ACCOUNTMAP, 8);
	if (r->groupmap == NULL || r->accountmap == NULL) {
		DEBUG(1,("GROUPMAP talloc failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	/* Remember how many we malloced */
	r->num_alloced = 8;

	/* Initial database population */
	if (database_id == SAM_DATABASE_DOMAIN) {

		status = populate_ldap_for_ldif(domain_sid_str,
						r->suffix,
						builtin_sid,
						r->add_file);
		if (!NT_STATUS_IS_OK(status)) {
			goto done;
		}

		status = map_populate_groups(mem_ctx,
					     r->groupmap,
					     r->accountmap,
					     domain_sid_str,
					     r->suffix,
					     builtin_sid);
		if (!NT_STATUS_IS_OK(status)) {
			goto done;
		}
	}

	r->initialized = true;

	*ctx = r;

	return NT_STATUS_OK;
 done:
	TALLOC_FREE(r);
	return status;
}
Esempio n. 14
0
/*
  find the members of a group, given a group rid and domain
 */
static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
				TALLOC_CTX *mem_ctx,
				const DOM_SID *group_sid,
				enum lsa_SidType type,
				uint32 *num_names,
				DOM_SID **sid_mem, char ***names,
				uint32 **name_types)
{
	ADS_STATUS rc;
	ADS_STRUCT *ads = NULL;
	char *ldap_exp;
	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
	char *sidbinstr;
	char **members = NULL;
	int i;
	size_t num_members = 0;
	ads_control args;
	DOM_SID *sid_mem_nocache = NULL;
	char **names_nocache = NULL;
	enum lsa_SidType *name_types_nocache = NULL;
	char **domains_nocache = NULL;     /* only needed for rpccli_lsa_lookup_sids */
	uint32 num_nocache = 0;
	TALLOC_CTX *tmp_ctx = NULL;

	DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
		  sid_string_dbg(group_sid)));

	*num_names = 0;

	tmp_ctx = talloc_new(mem_ctx);
	if (!tmp_ctx) {
		DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	if ( !winbindd_can_contact_domain( domain ) ) {
		DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
			  domain->name));
		return NT_STATUS_OK;
	}

	ads = ads_cached_connection(domain);

	if (!ads) {
		domain->last_status = NT_STATUS_SERVER_DISABLED;
		goto done;
	}

	if ((sidbinstr = sid_binstring(talloc_tos(), group_sid)) == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	/* search for all members of the group */
	ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
	TALLOC_FREE(sidbinstr);
	if (ldap_exp == NULL) {
		DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	args.control = ADS_EXTENDED_DN_OID;
	args.val = ADS_EXTENDED_DN_HEX_STRING;
	args.critical = True;

	rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
			       ldap_exp, &args, "member", &members, &num_members);

	if (!ADS_ERR_OK(rc)) {
		DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
		status = NT_STATUS_UNSUCCESSFUL;
		goto done;
	}

	DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));

	/* Now that we have a list of sids, we need to get the
	 * lists of names and name_types belonging to these sids.
	 * even though conceptually not quite clean,  we use the
	 * RPC call lsa_lookup_sids for this since it can handle a
	 * list of sids. ldap calls can just resolve one sid at a time.
	 *
	 * At this stage, the sids are still hidden in the exetended dn
	 * member output format. We actually do a little better than
	 * stated above: In extracting the sids from the member strings,
	 * we try to resolve as many sids as possible from the
	 * cache. Only the rest is passed to the lsa_lookup_sids call. */

	if (num_members) {
		(*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
		(*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
		(*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
		(sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members);

		if ((members == NULL) || (*sid_mem == NULL) ||
		    (*names == NULL) || (*name_types == NULL) ||
		    (sid_mem_nocache == NULL))
		{
			DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
			status = NT_STATUS_NO_MEMORY;
			goto done;
		}
	}
	else {
Esempio n. 15
0
/* Lookup groups a user is a member of - alternate method, for when
   tokenGroups are not available. */
static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
					   TALLOC_CTX *mem_ctx,
					   const char *user_dn,
					   DOM_SID *primary_group,
					   size_t *p_num_groups,
					   DOM_SID **user_sids)
{
	ADS_STATUS rc;
	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
	ADS_STRUCT *ads;
	const char *attrs[] = {"memberOf", NULL};
	size_t num_groups = 0;
	DOM_SID *group_sids = NULL;
	int i;
	char **strings = NULL;
	size_t num_strings = 0, num_sids = 0;


	DEBUG(3,("ads: lookup_usergroups_memberof\n"));

	if ( !winbindd_can_contact_domain( domain ) ) {
		DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
			  "domain %s\n", domain->name));
		return NT_STATUS_OK;
	}

	ads = ads_cached_connection(domain);

	if (!ads) {
		domain->last_status = NT_STATUS_SERVER_DISABLED;
		return NT_STATUS_UNSUCCESSFUL;
	}

	rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
						 ADS_EXTENDED_DN_HEX_STRING,
						 &strings, &num_strings);

	if (!ADS_ERR_OK(rc)) {
		DEBUG(1,("lookup_usergroups_memberof ads_search "
			"member=%s: %s\n", user_dn, ads_errstr(rc)));
		return ads_ntstatus(rc);
	}

	*user_sids = NULL;
	num_groups = 0;

	/* always add the primary group to the sid array */
	status = add_sid_to_array(mem_ctx, primary_group, user_sids,
				  &num_groups);
	if (!NT_STATUS_IS_OK(status)) {
		goto done;
	}

	group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
	if (!group_sids) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	for (i=0; i<num_strings; i++) {
		rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
						  ADS_EXTENDED_DN_HEX_STRING,
						  &(group_sids)[i]);
		if (!ADS_ERR_OK(rc)) {
			/* ignore members without SIDs */
			if (NT_STATUS_EQUAL(ads_ntstatus(rc),
			    NT_STATUS_NOT_FOUND)) {
				continue;
			}
			else {
				status = ads_ntstatus(rc);
				goto done;
			}
		}
		num_sids++;
	}

	if (i == 0) {
		DEBUG(1,("No memberOf for this user?!?\n"));
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}

	for (i=0; i<num_sids; i++) {

		/* ignore Builtin groups from ADS - Guenther */
		if (sid_check_is_in_builtin(&group_sids[i])) {
			continue;
		}

		status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
					  &num_groups);
		if (!NT_STATUS_IS_OK(status)) {
			goto done;
		}

	}

	*p_num_groups = num_groups;
	status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;

	DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
		user_dn));

done:
	TALLOC_FREE(strings);
	TALLOC_FREE(group_sids);

	return status;
}