diff options
| -rw-r--r-- | source3/include/debug.h | 1 | ||||
| -rw-r--r-- | source3/include/idmap.h | 1 | ||||
| -rw-r--r-- | source3/sam/idmap.c | 3 | ||||
| -rw-r--r-- | source3/sam/idmap_tdb.c | 469 | 
4 files changed, 474 insertions, 0 deletions
diff --git a/source3/include/debug.h b/source3/include/debug.h index d4f45539f4..70f9f7706d 100644 --- a/source3/include/debug.h +++ b/source3/include/debug.h @@ -88,6 +88,7 @@ extern int DEBUGLEVEL;  #define DBGC_AUTH		10  #define DBGC_WINBIND		11  #define DBGC_VFS		12 +#define DBGC_IDMAP		13  /* So you can define DBGC_CLASS before including debug.h */  #ifndef DBGC_CLASS diff --git a/source3/include/idmap.h b/source3/include/idmap.h index 0d358d3fbe..5a1f4fafc3 100644 --- a/source3/include/idmap.h +++ b/source3/include/idmap.h @@ -22,6 +22,7 @@     Boston, MA  02111-1307, USA.     */ +#define	ID_EMPTY	0  #define ID_USERID	1  #define ID_GROUPID	2  #define ID_OTHER	3 diff --git a/source3/sam/idmap.c b/source3/sam/idmap.c index 617cbc9d28..96638b4723 100644 --- a/source3/sam/idmap.c +++ b/source3/sam/idmap.c @@ -23,6 +23,9 @@  #include "includes.h"  #include "idmap.h" +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP +  static struct {  	const char *name; diff --git a/source3/sam/idmap_tdb.c b/source3/sam/idmap_tdb.c new file mode 100644 index 0000000000..3aaab3ac42 --- /dev/null +++ b/source3/sam/idmap_tdb.c @@ -0,0 +1,469 @@ +/*  +   Unix SMB/CIFS implementation. + +   idmap TDB 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" +#include "idmap.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_IDMAP + +/* High water mark keys */ +#define HWM_GROUP  "GROUP HWM" +#define HWM_USER   "USER HWM" + +/* idmap version determines auto-conversion */ +#define IDMAP_VERSION 2 + +/* Globals */ +static TDB_CONTEXT *idmap_tdb; + +/* FIXME: let handle conversions when all things work ok. +	  I think it is better to handle the conversion at +	  upgrade time and leave the old db intact. +	  That would also make easier to go back to 2.2 if needed +	  ---SSS */ +#if 0 + +/* convert one record to the new format */ +static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data, +			  void *ignored) +{ +	struct winbindd_domain *domain; +	char *p; +	DOM_SID sid; +	uint32 rid; +	fstring keystr; +	fstring dom_name; +	TDB_DATA key2; + +	p = strchr(key.dptr, '/'); +	if (!p) +		return 0; + +	*p = 0; +	fstrcpy(dom_name, key.dptr); +	*p++ = '/'; + +	domain = find_domain_from_name(dom_name); +	if (!domain) { +		/* We must delete the old record. */ +		DEBUG(0, +		      ("winbindd: tdb_convert_fn : Unable to find domain %s\n", +		       dom_name)); +		DEBUG(0, +		      ("winbindd: tdb_convert_fn : deleting record %s\n", +		       key.dptr)); +		tdb_delete(idmap_tdb, key); +		return 0; +	} + +	rid = atoi(p); + +	sid_copy(&sid, &domain->sid); +	sid_append_rid(&sid, rid); + +	sid_to_string(keystr, &sid); +	key2.dptr = keystr; +	key2.dsize = strlen(keystr) + 1; + +	if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) { +		/* not good! */ +		DEBUG(0, +		      ("winbindd: tdb_convert_fn : Unable to update record %s\n", +		       key2.dptr)); +		DEBUG(0, +		      ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n")); +		return -1; +	} + +	if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) { +		/* not good! */ +		DEBUG(0, +		      ("winbindd: tdb_convert_fn : Unable to update record %s\n", +		       data.dptr)); +		DEBUG(0, +		      ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n")); +		return -1; +	} + +	tdb_delete(idmap_tdb, key); + +	return 0; +} + +/***************************************************************************** + Convert the idmap database from an older version. +*****************************************************************************/ +static BOOL tdb_idmap_convert(const char *idmap_name) +{ +	int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION"); +	BOOL bigendianheader = +	    (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False; + +	if (vers == IDMAP_VERSION) +		return True; + +	if (((vers == -1) && bigendianheader) +	    || (IREV(vers) == IDMAP_VERSION)) { +		/* Arrggghh ! Bytereversed or old big-endian - make order independent ! */ +		/* +		 * high and low records were created on a +		 * big endian machine and will need byte-reversing. +		 */ + +		int32 wm; + +		wm = tdb_fetch_int32(idmap_tdb, HWM_USER); + +		if (wm != -1) { +			wm = IREV(wm); +		} else +			wm = server_state.uid_low; + +		if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) { +			DEBUG(0, +			      ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n")); +			return False; +		} + +		wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP); +		if (wm != -1) { +			wm = IREV(wm); +		} else +			wm = server_state.gid_low; + +		if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) { +			DEBUG(0, +			      ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n")); +			return False; +		} +	} + +	/* the old format stored as DOMAIN/rid - now we store the SID direct */ +	tdb_traverse(idmap_tdb, tdb_convert_fn, NULL); + +	if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == +	    -1) { +		DEBUG(0, +		      ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n")); +		return False; +	} + +	return True; +} +#endif + +/* Allocate either a user or group id from the pool */ +static NTSTATUS tdb_allocate_id(id_t *id, int id_type) +{ +	int hwm; + +	if (!id) return NT_STATUS_INVALID_PARAMETER; + +	/* Get current high water mark */ +	switch (id_type) { +		case ID_USERID: +			if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) { +				return NT_STATUS_INTERNAL_DB_ERROR; +			} + +			if (hwm > server_state.uid_high) { +				DEBUG(0, ("idmap Fatal Error: UID range full!!\n")); +				return NT_STATUS_UNSUCCESSFUL; +			} + +			*id.uid = hwm++; + +			/* Store new high water mark */ +			tdb_store_int32(idmap_tdb, HWM_USER, hwm); +			break; +		case ID_GROUPID: +			if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) { +				return NT_STATUS_INTERNAL_DB_ERROR; +			} + +			if (hwm > server_state.gid_high) { +				DEBUG(0, ("idmap Fatal Error: GID range full!!\n")); +				return NT_STATUS_UNSUCCESSFUL; +			} + +			*id.gid = hwm++; +			 +			/* Store new high water mark */ +			tdb_store_int32(idmap_tdb, HWM_GROUP, hwm); +			break; +		default: +			return NT_STATUS_INVALID_PARAMETER; +	} + +	return NT_STATUS_OK; +} + +/* Get a sid from an id */ +static NTSTATUS tdb_get_sid_from_id(DOM_SID *sid, id_t id, int id_type) +{ +	TDB_DATA key, data; +	fstring keystr; +	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + +	if (!sid) return NT_STATUS_INVALID_PARAMETER; + +	switch (id_type) { +		case ID_USERID: +		slprintf(keystr, sizeof(keystr), "UID %d", id.uid); +		break; +		case ID_GROUPID: +		slprintf(keystr, sizeof(keystr), "GID %d", id.gid); +		break; +		default: +		return NT_STATUS_UNSUCCESSFUL; +	} + +	key.dptr = keystr; +	key.dsize = strlen(keystr) + 1; + +	data = tdb_fetch(idmap_tdb, key); + +	if (data.dptr) { +		if (string_to_sid(sid, data.dptr)) { +			ret = NT_STATUS_OK; +		} +		SAFE_FREE(data.dptr); +	} + +	return ret; +} + +/* Get an id from a sid */ +static NTSTATUS tdb_get_id_from_sid(id_t *id, int *id_type, DOM_SID *sid) +{ +	TDB_DATA data, key; +	fstring keystr; +	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + +	if (!sid || !id || !id_type) return NT_STATUS_INVALID_PARAMETER; + +	/* Check if sid is present in database */ +	sid_to_string(keystr, sid); + +	key.dptr = keystr; +	key.dsize = strlen(keystr) + 1; + +	data = tdb_fetch(idmap_tdb, key); + +	if (data.dptr) { +		fstring scanstr; + +		if (*id_type == ID_EMPTY || *id_type == ID_USERID) { +			/* Parse and return existing uid */ +			fstrcpy(scanstr, "UID %d"); + +			if (sscanf(data.dptr, scanstr, *id.uid) == 1) { +				/* uid ok? */ +				if (*id_type == ID_EMPTY) { +					*id_type = ID_USERID; +				} +				ret = NT_STATUS_OK; +				goto idok; +			} +		} + +		if (*id_type == ID_EMPTY || *id_type == ID_GROUPID) { +			/* Parse and return existing gid */ +			fstrcpy(scanstr, "GID %d"); + +			if (sscanf(data.dptr, scanstr, *id.gid) == 1) { +				/* gid ok? */ +				if (*id_type == ID_EMPTY) { +					*id_type = ID_GROUPID; +				} +				ret = NT_STATUS_OK; +			} +		} +idok: +		SAFE_FREE(data.dptr); + +	} else if (*id_type == ID_USERID || *id_type == ID_GROUPID) { + +		/* Allocate a new id for this sid */ +		ret = tdb_allocate_id(id, id_type); +		if (NT_STATUS_IS_OK(ret)) { +			fstring keystr2; + +			/* Store new id */ +			slprintf(keystr2, sizeof(keystr2), "%s %d", +				 *id_type ? "GID" : "UID", *id); + +			data.dptr = keystr2; +			data.dsize = strlen(keystr2) + 1; + +			if (tdb_store(idmap_tdb, key, data, TDB_INSERT) == -1) +				return NT_STATUS_UNSUCCESSFUL; +			if (tdb_store(idmap_tdb, data, key, TDB_INSERT) == -1) +				return NT_STATUS_UNSUCCESSFUL; + +			ret = NT_STATUS_OK; +		} +	} + +	return ret; +} + +/***************************************************************************** + Initialise idmap database.  +*****************************************************************************/ +static NTSTATUS tdb_idmap_init(void) +{ +	/* Open tdb cache */ +	if (!(idmap_tdb = tdb_open_log(lock_path("idmap.tdb"), 0, +				       TDB_DEFAULT, O_RDWR | O_CREAT, +				       0600))) { +		DEBUG(0, ("idmap_init: Unable to open idmap database\n")); +		return NT_STATUS_UNSUCCESSFUL; +	} + +#if 0 +	/* possibly convert from an earlier version */ +	if (!tdb_idmap_convert(lock_path("winbind_idmap.tdb"))) { +		DEBUG(0, +		      ("idmap_init: Unable to open old idmap database\n")); +		return False; +	} +#endif + +	/* Create high water marks for group and user id */ +	if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) { +		if (tdb_store_int32(idmap_tdb, HWM_USER, server_state.uid_low) == -1) { +			DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n")); +			return NT_STATUS_INTERNAL_DB_ERROR; +		} +	} + +	if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) { +		if (tdb_store_int32(idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) { +			DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n")); +			return NT_STATUS_INTERNAL_DB_ERROR; +		} +	} + +	return NT_STATUS_OK; +} + +/* Close the tdb */ +static NTSTATUS tdb_idmap_close(void) +{ +	if (idmap_tdb) +		if (tdb_close(idmap_tdb) == 0) +			return NT_STATUS_OK; +		else +			retrun NT_STATUS_UNSUCCESSFUL; +	return NT_STATUS_OK; +} + + +/* Dump status information to log file.  Display different stuff based on +   the debug level: + +   Debug Level        Information Displayed +   ================================================================= +   0                  Percentage of [ug]id range allocated +   0                  High water marks (next allocated ids) +*/ + +#define DUMP_INFO 0 + +static void tdb_idmap_status(void) +{ +	int user_hwm, group_hwm; + +	DEBUG(0, ("winbindd idmap status:\n")); + +	/* Get current high water marks */ + +	if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) { +		DEBUG(DUMP_INFO, +		      ("\tCould not get userid high water mark!\n")); +	} + +	if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) { +		DEBUG(DUMP_INFO, +		      ("\tCould not get groupid high water mark!\n")); +	} + +	/* Display next ids to allocate */ + +	if (user_hwm != -1) { +		DEBUG(DUMP_INFO, +		      ("\tNext userid to allocate is %d\n", user_hwm)); +	} + +	if (group_hwm != -1) { +		DEBUG(DUMP_INFO, +		      ("\tNext groupid to allocate is %d\n", group_hwm)); +	} + +	/* Display percentage of id range already allocated. */ + +	if (user_hwm != -1) { +		int num_users = user_hwm - server_state.uid_low; +		int total_users = +		    server_state.uid_high - server_state.uid_low; + +		DEBUG(DUMP_INFO, +		      ("\tUser id range is %d%% full (%d of %d)\n", +		       num_users * 100 / total_users, num_users, +		       total_users)); +	} + +	if (group_hwm != -1) { +		int num_groups = group_hwm - server_state.gid_low; +		int total_groups = +		    server_state.gid_high - server_state.gid_low; + +		DEBUG(DUMP_INFO, +		      ("\tGroup id range is %d%% full (%d of %d)\n", +		       num_groups * 100 / total_groups, num_groups, +		       total_groups)); +	} + +	/* Display complete mapping of users and groups to rids */ +} + +struct idmap_methods tdb_idmap_methods = { + +	tdb_idmap_init, +	tdb_get_sid_from_id, +	tdb_get_id_from_sid, +	tdb_idmap_close, +	tdb_idmap_status + +}; + +NTSTATUS idmap_reg_tdb(struct idmap_methods **meth) +{ +	*meth = &tdb_idmap_methods; + +	return NTSTATUS_OK; +} +  | 
