/* 
   Unix SMB/Netbios implementation.
   Version 2.0.
   LDAP builtin group database for SAMBA
   Copyright (C) Matthew Chapman 1998
   
   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"

#ifdef WITH_LDAP

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

extern int DEBUGLEVEL;

/* Internal state */
extern LDAP *ldap_struct;
extern LDAPMessage *ldap_results;
extern LDAPMessage *ldap_entry;

/* Static structure filled for requests */
static LOCAL_GRP localgrp;


/***************************************************************
  Get group and membership information.
 ****************************************************************/

static LOCAL_GRP *ldapbuiltin_getgrp(LOCAL_GRP *group,
		     LOCAL_GRP_MEMBER **members, int *num_membs)
{
	fstring temp;
	char **values;
	LOCAL_GRP_MEMBER *memblist;
	char *value, *sep;
	int i;

	if(!ldap_entry)
		return NULL;

	if(!ldap_get_attribute("cn", group->name)) {
		DEBUG(0, ("Missing cn\n"));
                return NULL; }
	
	DEBUG(2,("Retrieving builtin alias [%s]\n", group->name));

        if(ldap_get_attribute("rid", temp)) {
		group->rid = strtol(temp, NULL, 16);
	} else {
		DEBUG(0, ("Missing rid\n"));
		return NULL;
	}

	if(!ldap_get_attribute("description", group->comment))
		group->comment[0] = 0;

	if(!members || !num_membs) {
		ldap_entry = ldap_next_entry(ldap_struct, ldap_entry);
		return group;
	}

	if(values = ldap_get_values(ldap_struct, ldap_entry, "member")) {

		*num_membs = i = ldap_count_values(values);
		*members = memblist = malloc(i * sizeof(LOCAL_GRP_MEMBER));

		do {
			value = values[--i];

		        if(!(sep = strchr(value, ','))) {
				DEBUG(0, ("Malformed alias member\n"));
				return NULL;
			}
			*(sep++) = 0;
			fstrcpy(memblist[i].name, value);

			if(!(value = strchr(sep, ','))) {
				DEBUG(0, ("Malformed alias member\n"));
				return NULL;
			}
			*(value++) = 0;
			string_to_sid(&memblist[i].sid, sep);

			if((memblist[i].sid_use = atoi(value))
					>= SID_NAME_UNKNOWN)
				DEBUG(0, ("Invalid SID use in alias"));

		} while(i > 0);

		ldap_value_free(values);

	} else {
		*num_membs = 0;
		*members = NULL;
	}

	return group;
}


/************************************************************************
  Queues the necessary modifications to save a LOCAL_GRP structure
 ************************************************************************/

static void ldapbuiltin_grpmods(LOCAL_GRP *group, LDAPMod ***mods,
			int operation)
{
	fstring temp;

	*mods = NULL;

	if(operation == LDAP_MOD_ADD) { /* immutable attributes */
		ldap_make_mod(mods, LDAP_MOD_ADD, "objectClass", "sambaBuiltin");
		ldap_make_mod(mods, LDAP_MOD_ADD, "cn", group->name);

		slprintf(temp, sizeof(temp)-1, "%x", group->rid);
		ldap_make_mod(mods, LDAP_MOD_ADD, "rid", temp);
	}

	ldap_make_mod(mods, operation, "description", group->comment);
}


/************************************************************************
  Create a builtin alias member entry
 ************************************************************************/

static BOOL ldapbuiltin_memmods(DOM_SID *user_sid, LDAPMod ***mods,
			      int operation)
{
	pstring member;
	pstring sid_str;
	fstring name;
	uint8 type;

	if (lookup_sid(user_sid, name, &type))
		return (False);
	sid_to_string(sid_str, user_sid);

	slprintf(member, sizeof(member)-1, "%s,%s,%d", name, sid_str, type);

	*mods = NULL;
	ldap_make_mod(mods, operation, "member", member);
	return True;
}


/***************************************************************
  Begin/end smbgrp enumeration.
 ****************************************************************/

static void *ldapbuiltin_enumfirst(BOOL update)
{
	if (lp_server_role() == ROLE_DOMAIN_NONE)
		return NULL;

	if (!ldap_connect())
		return NULL;

	ldap_search_for("objectClass=sambaBuiltin");

	return ldap_struct;
}

static void ldapbuiltin_enumclose(void *vp)
{
	ldap_disconnect();
}


/*************************************************************************
  Save/restore the current position in a query
 *************************************************************************/

static SMB_BIG_UINT ldapbuiltin_getdbpos(void *vp)
{
	return (SMB_BIG_UINT)((ulong)ldap_entry);
}

static BOOL ldapbuiltin_setdbpos(void *vp, SMB_BIG_UINT tok)
{
	ldap_entry = (LDAPMessage *)((ulong)tok);
	return (True);
}


/*************************************************************************
  Return limited smb_passwd information, and group membership.
 *************************************************************************/

static LOCAL_GRP *ldapbuiltin_getgrpbynam(const char *name,
	       LOCAL_GRP_MEMBER **members, int *num_membs)
{
	fstring filter;
	LOCAL_GRP *ret;

	if(!ldap_connect())
		return (False);

	slprintf(filter, sizeof(filter)-1,
		 "(&(cn=%s)(objectClass=sambaBuiltin))", name);
	ldap_search_for(filter);

	ret = ldapbuiltin_getgrp(&localgrp, members, num_membs);

	ldap_disconnect();
	return ret;
}

static LOCAL_GRP *ldapbuiltin_getgrpbygid(gid_t grp_id,
	       LOCAL_GRP_MEMBER **members, int *num_membs)
{
	fstring filter;
	LOCAL_GRP *ret;

	if(!ldap_connect())
		return (False);

	slprintf(filter, sizeof(filter)-1,
		 "(&(gidNumber=%d)(objectClass=sambaBuiltin))", grp_id);
	ldap_search_for(filter);
	ret = ldapbuiltin_getgrp(&localgrp, members, num_membs);

	ldap_disconnect();
	return ret;
}

static LOCAL_GRP *ldapbuiltin_getgrpbyrid(uint32 grp_rid,
	       LOCAL_GRP_MEMBER **members, int *num_membs)
{
	fstring filter;
	LOCAL_GRP *ret;

	if(!ldap_connect())
		return (False);

	slprintf(filter, sizeof(filter)-1,
		 "(&(rid=%x)(objectClass=sambaBuiltin))", grp_rid);
	ldap_search_for(filter);
	ret = ldapbuiltin_getgrp(&localgrp, members, num_membs);

	ldap_disconnect();
	return ret;
}

static LOCAL_GRP *ldapbuiltin_getcurrentgrp(void *vp,
	       LOCAL_GRP_MEMBER **members, int *num_membs)
{
	return ldapbuiltin_getgrp(&localgrp, members, num_membs);
}


/*************************************************************************
  Add/modify/delete builtin aliases.
 *************************************************************************/

static BOOL ldapbuiltin_addgrp(LOCAL_GRP *group)
{
	LDAPMod **mods;

	if (!ldap_allocaterid(&group->rid))
	{
	    DEBUG(0,("RID generation failed\n"));
	    return (False);
	}

	ldapbuiltin_grpmods(group, &mods, LDAP_MOD_ADD); 
	return ldap_makemods("cn", group->name, mods, True);
}

static BOOL ldapbuiltin_modgrp(LOCAL_GRP *group)
{
	LDAPMod **mods;

	ldapbuiltin_grpmods(group, &mods, LDAP_MOD_REPLACE);
	return ldap_makemods("cn", group->name, mods, False);
}

static BOOL ldapbuiltin_delgrp(uint32 grp_rid)
{
	fstring filter;
	char *dn;
	int err;

	if (!ldap_connect())
		return (False);

	slprintf(filter, sizeof(filter)-1,
		 "(&(rid=%x)(objectClass=sambaBuiltin))", grp_rid);
	ldap_search_for(filter);

	if (!ldap_entry || !(dn = ldap_get_dn(ldap_struct, ldap_entry)))
	{
		ldap_disconnect();
		return (False);
	}

	err = ldap_delete_s(ldap_struct, dn);
	free(dn);
	ldap_disconnect();

	if (err != LDAP_SUCCESS)
	{
		DEBUG(0, ("delete: %s\n", ldap_err2string(err)));
		return (False);
	}

	return True;
}


/*************************************************************************
  Add users to/remove users from aliases.
 *************************************************************************/

static BOOL ldapbuiltin_addmem(uint32 grp_rid, DOM_SID *user_sid)
{
	LDAPMod **mods;
        fstring rid_str;

	slprintf(rid_str, sizeof(rid_str)-1, "%x", grp_rid);

	if(!ldapbuiltin_memmods(user_sid, &mods, LDAP_MOD_ADD))
		return (False);

	return ldap_makemods("rid", rid_str, mods, False);
}

static BOOL ldapbuiltin_delmem(uint32 grp_rid, DOM_SID *user_sid)
{
	LDAPMod **mods;
        fstring rid_str;

	slprintf(rid_str, sizeof(rid_str)-1, "%x", grp_rid);

	if(!ldapbuiltin_memmods(user_sid, &mods, LDAP_MOD_DELETE))
		return (False);

	return ldap_makemods("rid", rid_str, mods, False);
}


/*************************************************************************
  Return builtin aliases that a user is in.
 *************************************************************************/

static BOOL ldapbuiltin_getusergroups(const char *name,
			LOCAL_GRP **groups, int *num_grps)
{
	LOCAL_GRP *grouplist;
	fstring filter;
	int i;

	if(!ldap_connect())
		return (False);

	slprintf(filter, sizeof(pstring)-1,
		 "(&(member=%s,*)(objectclass=sambaBuiltin))", name);
	ldap_search_for(filter);

	*num_grps = i = ldap_count_entries(ldap_struct, ldap_results);

	if(!i) {
		*groups = NULL;
		ldap_disconnect();
		return (True);
	}

	*groups = grouplist = malloc(i * sizeof(LOCAL_GRP));
	do {
		i--;
	} while(ldapbuiltin_getgrp(&grouplist[i], NULL, NULL) && (i > 0));

	ldap_disconnect();
	return (True);
}


static struct aliasdb_ops ldapbuiltin_ops =
{
	ldapbuiltin_enumfirst,
	ldapbuiltin_enumclose,
	ldapbuiltin_getdbpos,
	ldapbuiltin_setdbpos,

	ldapbuiltin_getgrpbynam,
	ldapbuiltin_getgrpbygid,
	ldapbuiltin_getgrpbyrid,
	ldapbuiltin_getcurrentgrp,

	ldapbuiltin_addgrp,
	ldapbuiltin_modgrp,
	ldapbuiltin_delgrp,

	ldapbuiltin_addmem,
	ldapbuiltin_delmem,

	ldapbuiltin_getusergroups
};

struct aliasdb_ops *ldap_initialise_builtin_db(void)
{
	return &ldapbuiltin_ops;
}

#else
 void builtinldap_dummy_function(void);
 void builtinldap_dummy_function(void) { } /* stop some compilers complaining */
#endif