1/*
2   Unix SMB/CIFS implementation.
3
4   idmap TDB backend
5
6   Copyright (C) Tim Potter 2000
7   Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8   Copyright (C) Simo Sorce 2003
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23*/
24
25#include "includes.h"
26
27#undef DBGC_CLASS
28#define DBGC_CLASS DBGC_IDMAP
29
30/* High water mark keys */
31#define HWM_GROUP  "GROUP HWM"
32#define HWM_USER   "USER HWM"
33
34/* idmap version determines auto-conversion */
35#define IDMAP_VERSION 2
36
37/* Globals */
38static TDB_CONTEXT *idmap_tdb;
39
40static struct idmap_state {
41
42	/* User and group id pool */
43
44	uid_t uid_low, uid_high;               /* Range of uids to allocate */
45	gid_t gid_low, gid_high;               /* Range of gids to allocate */
46} idmap_state;
47
48/**********************************************************************
49 allocate a new RID; We don't care if is a user or group
50**********************************************************************/
51
52static NTSTATUS db_allocate_rid(uint32 *rid, int rid_type)
53{
54	uint32 lowrid, highrid;
55	uint32 tmp_rid;
56
57	/* can't handle group rids right now.  This is such a mess.... */
58
59	if ( rid_type == GROUP_RID_TYPE )
60		return NT_STATUS_UNSUCCESSFUL;
61
62	/* cannot fail since idmap is only called winbindd */
63
64	get_free_rid_range( &lowrid, &highrid );
65
66	tmp_rid = lowrid;
67
68	if ( !tdb_change_uint32_atomic(idmap_tdb, "RID_COUNTER", &tmp_rid, RID_MULTIPLIER) ) {
69		DEBUG(3,("db_allocate_rid: Failed to locate next rid record in idmap db\n"));
70		return NT_STATUS_UNSUCCESSFUL;
71	}
72
73	if ( tmp_rid > highrid ) {
74		DEBUG(0, ("db_allocate_rid: no RIDs available!\n"));
75		return NT_STATUS_UNSUCCESSFUL;
76	}
77
78	*rid = tmp_rid;
79
80	return NT_STATUS_OK;
81}
82
83/**********************************************************************
84 Allocate either a user or group id from the pool
85**********************************************************************/
86
87static NTSTATUS db_allocate_id(unid_t *id, int id_type)
88{
89	BOOL ret;
90	int hwm;
91
92	if (!id)
93		return NT_STATUS_INVALID_PARAMETER;
94
95	/* Get current high water mark */
96	switch (id_type & ID_TYPEMASK) {
97		case ID_USERID:
98
99			if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
100				return NT_STATUS_INTERNAL_DB_ERROR;
101			}
102
103			/* check it is in the range */
104			if (hwm > idmap_state.uid_high) {
105				DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
106					  (unsigned long)idmap_state.uid_high));
107				return NT_STATUS_UNSUCCESSFUL;
108			}
109
110			/* fetch a new id and increment it */
111			ret = tdb_change_uint32_atomic(idmap_tdb, HWM_USER, (unsigned int *)&hwm, 1);
112			if (!ret) {
113				DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
114				return NT_STATUS_UNSUCCESSFUL;
115			}
116
117			/* recheck it is in the range */
118			if (hwm > idmap_state.uid_high) {
119				DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
120					  (unsigned long)idmap_state.uid_high));
121				return NT_STATUS_UNSUCCESSFUL;
122			}
123
124			(*id).uid = hwm;
125			DEBUG(10,("db_allocate_id: ID_USERID (*id).uid = %d\n", (unsigned int)hwm));
126
127			break;
128		case ID_GROUPID:
129			if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
130				return NT_STATUS_INTERNAL_DB_ERROR;
131			}
132
133			/* check it is in the range */
134			if (hwm > idmap_state.gid_high) {
135				DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
136					  (unsigned long)idmap_state.gid_high));
137				return NT_STATUS_UNSUCCESSFUL;
138			}
139
140			/* fetch a new id and increment it */
141			ret = tdb_change_uint32_atomic(idmap_tdb, HWM_GROUP, (unsigned int *)&hwm, 1);
142
143			if (!ret) {
144				DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
145				return NT_STATUS_UNSUCCESSFUL;
146			}
147
148			/* recheck it is in the range */
149			if (hwm > idmap_state.gid_high) {
150				DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
151					  (unsigned long)idmap_state.gid_high));
152				return NT_STATUS_UNSUCCESSFUL;
153			}
154
155			(*id).gid = hwm;
156			DEBUG(10,("db_allocate_id: ID_GROUPID (*id).gid = %d\n", (unsigned int)hwm));
157
158			break;
159		default:
160			return NT_STATUS_INVALID_PARAMETER;
161	}
162
163	return NT_STATUS_OK;
164}
165
166/* Get a sid from an id */
167static NTSTATUS internal_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
168{
169	TDB_DATA key, data;
170	fstring keystr;
171	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
172
173	if (!sid)
174		return NT_STATUS_INVALID_PARAMETER;
175
176	switch (id_type & ID_TYPEMASK) {
177		case ID_USERID:
178			slprintf(keystr, sizeof(keystr), "UID %lu", (unsigned long)id.uid);
179			break;
180		case ID_GROUPID:
181			slprintf(keystr, sizeof(keystr), "GID %lu", (unsigned long)id.gid);
182			break;
183		default:
184			return NT_STATUS_UNSUCCESSFUL;
185	}
186
187	key.dptr = keystr;
188	key.dsize = strlen(keystr) + 1;
189
190	DEBUG(10,("internal_get_sid_from_id: fetching record %s\n", keystr ));
191
192	data = tdb_fetch(idmap_tdb, key);
193
194	if (data.dptr) {
195		if (string_to_sid(sid, data.dptr)) {
196			DEBUG(10,("internal_get_sid_from_id: fetching record %s -> %s\n", keystr, data.dptr ));
197			ret = NT_STATUS_OK;
198		}
199		SAFE_FREE(data.dptr);
200	}
201
202	return ret;
203}
204
205/* Error codes for get_id_from_sid */
206enum getidfromsiderr { GET_ID_FROM_SID_OK = 0, GET_ID_FROM_SID_NOTFOUND, GET_ID_FROM_SID_WRONG_TYPE, GET_ID_FROM_SID_ERR };
207
208static enum getidfromsiderr internal_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
209{
210	enum getidfromsiderr ret = GET_ID_FROM_SID_ERR;
211	fstring keystr;
212	TDB_DATA key, data;
213	int type = *id_type & ID_TYPEMASK;
214
215	/* Check if sid is present in database */
216	sid_to_string(keystr, sid);
217
218	key.dptr = keystr;
219	key.dsize = strlen(keystr) + 1;
220
221	DEBUG(10,("internal_get_id_from_sid: fetching record %s of type 0x%x\n", keystr, type ));
222
223	data = tdb_fetch(idmap_tdb, key);
224	if (!data.dptr) {
225		DEBUG(10,("internal_get_id_from_sid: record %s not found\n", keystr ));
226		return GET_ID_FROM_SID_NOTFOUND;
227	} else {
228		DEBUG(10,("internal_get_id_from_sid: record %s -> %s\n", keystr, data.dptr ));
229	}
230
231	if (type == ID_EMPTY || type == ID_USERID) {
232		fstring scanstr;
233		/* Parse and return existing uid */
234		fstrcpy(scanstr, "UID %d");
235
236		if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
237			/* uid ok? */
238			if (type == ID_EMPTY) {
239				*id_type = ID_USERID;
240			}
241			DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
242						(type == ID_EMPTY) ? "ID_EMPTY" : "ID_USERID",
243						keystr, data.dptr ));
244			ret = GET_ID_FROM_SID_OK;
245		} else {
246			ret = GET_ID_FROM_SID_WRONG_TYPE;
247		}
248	}
249
250	if ((ret != GET_ID_FROM_SID_OK) && (type == ID_EMPTY || type == ID_GROUPID)) {
251		fstring scanstr;
252		/* Parse and return existing gid */
253		fstrcpy(scanstr, "GID %d");
254
255		if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
256			/* gid ok? */
257			if (type == ID_EMPTY) {
258				*id_type = ID_GROUPID;
259			}
260			DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
261						(type == ID_EMPTY) ? "ID_EMPTY" : "ID_GROUPID",
262						keystr, data.dptr ));
263			ret = GET_ID_FROM_SID_OK;
264		} else {
265			ret = GET_ID_FROM_SID_WRONG_TYPE;
266		}
267	}
268
269	SAFE_FREE(data.dptr);
270
271	return ret;
272}
273
274/* Get a sid from an id */
275static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type_in)
276{
277	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
278	enum getidfromsiderr iderr;
279	int id_type = id_type_in & ID_TYPEMASK;
280	unid_t id_tmp = id;
281	int id_type_tmp = id_type;
282
283	DEBUG(10,("db_get_sid_from_id: id_type_in = 0x%x\n", id_type_in));
284
285	ret = internal_get_sid_from_id(sid, id, id_type);
286	if (!NT_STATUS_IS_OK(ret)) {
287		return ret;
288	}
289
290	iderr = internal_get_id_from_sid(&id_tmp, &id_type_tmp, sid);
291	if (iderr != GET_ID_FROM_SID_OK) {
292		return NT_STATUS_UNSUCCESSFUL;
293	}
294	if (id_type_tmp != id_type) {
295		return NT_STATUS_UNSUCCESSFUL;
296	} else if (id_type == ID_USERID) {
297		if (id_tmp.uid != id.uid) {
298			return NT_STATUS_UNSUCCESSFUL;
299		}
300	} else if (id_type == ID_GROUPID) {
301		if (id_tmp.gid != id.gid) {
302			return NT_STATUS_UNSUCCESSFUL;
303		}
304	} else {
305		return NT_STATUS_UNSUCCESSFUL;
306	}
307	return ret;
308}
309/* Get an id from a sid */
310static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
311{
312	NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
313	enum getidfromsiderr iderr;
314
315	DEBUG(10,("db_get_id_from_sid\n"));
316
317	if (!sid || !id || !id_type)
318		return NT_STATUS_INVALID_PARAMETER;
319
320	iderr = internal_get_id_from_sid(id, id_type, sid);
321	if (iderr == GET_ID_FROM_SID_OK) {
322		DOM_SID sid_tmp;
323		ret = internal_get_sid_from_id(&sid_tmp, *id, *id_type);
324		if (NT_STATUS_IS_OK(ret)) {
325			if (!sid_equal(&sid_tmp, sid)) {
326				return NT_STATUS_UNSUCCESSFUL;
327			}
328		}
329	} else if (iderr == GET_ID_FROM_SID_WRONG_TYPE) {
330		/* We found a record but not the type we wanted.
331		 * This is an error, not an opportunity to overwrite...
332		 * JRA.
333		 */
334		return NT_STATUS_UNSUCCESSFUL;
335	}
336
337	if (!(*id_type & ID_QUERY_ONLY) && (iderr != GET_ID_FROM_SID_OK) &&
338		   (((*id_type & ID_TYPEMASK) == ID_USERID)
339		    || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
340		TDB_DATA sid_data;
341		TDB_DATA ugid_data;
342		fstring sid_string;
343
344		sid_to_string(sid_string, sid);
345
346		sid_data.dptr = sid_string;
347		sid_data.dsize = strlen(sid_string)+1;
348
349		/* Lock the record for this SID. */
350		if (tdb_chainlock(idmap_tdb, sid_data) != 0) {
351			DEBUG(10,("db_get_id_from_sid: failed to lock record %s. Error %s\n",
352					sid_string, tdb_errorstr(idmap_tdb) ));
353			return NT_STATUS_UNSUCCESSFUL;
354		}
355
356		do {
357			fstring ugid_str;
358
359			/* Allocate a new id for this sid */
360			ret = db_allocate_id(id, *id_type);
361			if (!NT_STATUS_IS_OK(ret))
362				break;
363
364			/* Store the UID side */
365			/* Store new id */
366			if (*id_type & ID_USERID) {
367				slprintf(ugid_str, sizeof(ugid_str), "UID %lu",
368					 (unsigned long)((*id).uid));
369			} else {
370				slprintf(ugid_str, sizeof(ugid_str), "GID %lu",
371					 (unsigned long)((*id).gid));
372			}
373
374			ugid_data.dptr = ugid_str;
375			ugid_data.dsize = strlen(ugid_str) + 1;
376
377			DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
378					ugid_data.dptr, sid_data.dptr ));
379
380			if (tdb_store(idmap_tdb, ugid_data, sid_data, TDB_INSERT) != -1) {
381				ret = NT_STATUS_OK;
382				break;
383			}
384			if (tdb_error(idmap_tdb) != TDB_ERR_EXISTS)
385				DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
386			ret = NT_STATUS_UNSUCCESSFUL;
387		} while (tdb_error(idmap_tdb) == TDB_ERR_EXISTS);
388
389		if (NT_STATUS_IS_OK(ret)) {
390
391			DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
392				sid_data.dptr, ugid_data.dptr ));
393
394			if (tdb_store(idmap_tdb, sid_data, ugid_data, TDB_REPLACE) == -1) {
395				DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
396				/* TODO: print tdb error !! */
397				tdb_chainunlock(idmap_tdb, sid_data);
398				return NT_STATUS_UNSUCCESSFUL;
399			}
400		}
401
402		tdb_chainunlock(idmap_tdb, sid_data);
403	}
404
405	return ret;
406}
407
408static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
409{
410	TDB_DATA ksid, kid, data;
411	fstring ksidstr;
412	fstring kidstr;
413
414	DEBUG(10,("db_set_mapping: id_type = 0x%x\n", id_type));
415
416	if (!sid)
417		return NT_STATUS_INVALID_PARAMETER;
418
419	sid_to_string(ksidstr, sid);
420
421	ksid.dptr = ksidstr;
422	ksid.dsize = strlen(ksidstr) + 1;
423
424	if (id_type & ID_USERID) {
425		slprintf(kidstr, sizeof(kidstr), "UID %lu", (unsigned long)id.uid);
426	} else if (id_type & ID_GROUPID) {
427		slprintf(kidstr, sizeof(kidstr), "GID %lu", (unsigned long)id.gid);
428	} else {
429		return NT_STATUS_INVALID_PARAMETER;
430	}
431
432	kid.dptr = kidstr;
433	kid.dsize = strlen(kidstr) + 1;
434
435	/* *DELETE* prevoius mappings if any.
436	 * This is done both SID and [U|G]ID passed in */
437
438	/* Lock the record for this SID. */
439	if (tdb_chainlock(idmap_tdb, ksid) != 0) {
440		DEBUG(10,("db_set_mapping: failed to lock record %s. Error %s\n",
441				ksidstr, tdb_errorstr(idmap_tdb) ));
442		return NT_STATUS_UNSUCCESSFUL;
443	}
444
445	DEBUG(10,("db_set_mapping: fetching %s\n", ksid.dptr));
446
447	data = tdb_fetch(idmap_tdb, ksid);
448	if (data.dptr) {
449		DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, ksid.dptr ));
450		tdb_delete(idmap_tdb, data);
451		tdb_delete(idmap_tdb, ksid);
452		SAFE_FREE(data.dptr);
453	}
454	data = tdb_fetch(idmap_tdb, kid);
455	if (data.dptr) {
456		DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, kid.dptr ));
457		tdb_delete(idmap_tdb, data);
458		tdb_delete(idmap_tdb, kid);
459		SAFE_FREE(data.dptr);
460	}
461
462	if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
463		DEBUG(0, ("idb_set_mapping: tdb_store 1 error: %s\n", tdb_errorstr(idmap_tdb)));
464		tdb_chainunlock(idmap_tdb, ksid);
465		return NT_STATUS_UNSUCCESSFUL;
466	}
467	if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
468		DEBUG(0, ("idb_set_mapping: tdb_store 2 error: %s\n", tdb_errorstr(idmap_tdb)));
469		tdb_chainunlock(idmap_tdb, ksid);
470		return NT_STATUS_UNSUCCESSFUL;
471	}
472
473	tdb_chainunlock(idmap_tdb, ksid);
474	DEBUG(10,("db_set_mapping: stored %s -> %s and %s -> %s\n", ksid.dptr, kid.dptr, kid.dptr, ksid.dptr ));
475	return NT_STATUS_OK;
476}
477
478/*****************************************************************************
479 Initialise idmap database.
480*****************************************************************************/
481
482static NTSTATUS db_idmap_init( char *params )
483{
484	SMB_STRUCT_STAT stbuf;
485	char *tdbfile = NULL;
486	int32 version;
487	BOOL tdb_is_new = False;
488
489	/* use the old database if present */
490	tdbfile = SMB_STRDUP(lock_path("winbindd_idmap.tdb"));
491	if (!tdbfile) {
492		DEBUG(0, ("idmap_init: out of memory!\n"));
493		return NT_STATUS_NO_MEMORY;
494	}
495
496	if (!file_exist(tdbfile, &stbuf)) {
497		tdb_is_new = True;
498	}
499
500	DEBUG(10,("db_idmap_init: Opening tdbfile %s\n", tdbfile ));
501
502	/* Open idmap repository */
503	if (!(idmap_tdb = tdb_open_log(tdbfile, 0,
504				       TDB_DEFAULT, O_RDWR | O_CREAT,
505				       0644))) {
506		DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
507		SAFE_FREE(tdbfile);
508		return NT_STATUS_UNSUCCESSFUL;
509	}
510
511	SAFE_FREE(tdbfile);
512
513	if (tdb_is_new) {
514		/* the file didn't existed before opening it, let's
515		 * store idmap version as nobody else yet opened and
516		 * stored it. I do not like this method but didn't
517		 * found a way to understand if an opened tdb have
518		 * been just created or not --- SSS */
519		tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION);
520	}
521
522	/* check against earlier versions */
523	version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
524	if (version != IDMAP_VERSION) {
525		DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n"));
526		return NT_STATUS_INTERNAL_DB_ERROR;
527	}
528
529	/* Create high water marks for group and user id */
530	if (!lp_idmap_uid(&idmap_state.uid_low, &idmap_state.uid_high)) {
531		DEBUG(1, ("idmap uid range missing or invalid\n"));
532		DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
533	} else {
534		if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
535			if (tdb_store_int32(idmap_tdb, HWM_USER, idmap_state.uid_low) == -1) {
536				DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
537				return NT_STATUS_INTERNAL_DB_ERROR;
538			}
539		}
540	}
541
542	if (!lp_idmap_gid(&idmap_state.gid_low, &idmap_state.gid_high)) {
543		DEBUG(1, ("idmap gid range missing or invalid\n"));
544		DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
545	} else {
546		if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
547			if (tdb_store_int32(idmap_tdb, HWM_GROUP, idmap_state.gid_low) == -1) {
548				DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
549				return NT_STATUS_INTERNAL_DB_ERROR;
550			}
551		}
552	}
553
554	return NT_STATUS_OK;
555}
556
557/* Close the tdb */
558static NTSTATUS db_idmap_close(void)
559{
560	if (idmap_tdb) {
561		if (tdb_close(idmap_tdb) == 0) {
562			return NT_STATUS_OK;
563		} else {
564			return NT_STATUS_UNSUCCESSFUL;
565		}
566	}
567	return NT_STATUS_OK;
568}
569
570
571/* Dump status information to log file.  Display different stuff based on
572   the debug level:
573
574   Debug Level        Information Displayed
575   =================================================================
576   0                  Percentage of [ug]id range allocated
577   0                  High water marks (next allocated ids)
578*/
579
580#define DUMP_INFO 0
581
582static void db_idmap_status(void)
583{
584	int user_hwm, group_hwm;
585
586	DEBUG(0, ("winbindd idmap status:\n"));
587
588	/* Get current high water marks */
589
590	if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
591		DEBUG(DUMP_INFO,
592		      ("\tCould not get userid high water mark!\n"));
593	}
594
595	if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
596		DEBUG(DUMP_INFO,
597		      ("\tCould not get groupid high water mark!\n"));
598	}
599
600	/* Display next ids to allocate */
601
602	if (user_hwm != -1) {
603		DEBUG(DUMP_INFO,
604		      ("\tNext userid to allocate is %d\n", user_hwm));
605	}
606
607	if (group_hwm != -1) {
608		DEBUG(DUMP_INFO,
609		      ("\tNext groupid to allocate is %d\n", group_hwm));
610	}
611
612	/* Display percentage of id range already allocated. */
613
614	if (user_hwm != -1) {
615		int num_users = user_hwm - idmap_state.uid_low;
616		int total_users =
617		    idmap_state.uid_high - idmap_state.uid_low;
618
619		DEBUG(DUMP_INFO,
620		      ("\tUser id range is %d%% full (%d of %d)\n",
621		       num_users * 100 / total_users, num_users,
622		       total_users));
623	}
624
625	if (group_hwm != -1) {
626		int num_groups = group_hwm - idmap_state.gid_low;
627		int total_groups =
628		    idmap_state.gid_high - idmap_state.gid_low;
629
630		DEBUG(DUMP_INFO,
631		      ("\tGroup id range is %d%% full (%d of %d)\n",
632		       num_groups * 100 / total_groups, num_groups,
633		       total_groups));
634	}
635
636	/* Display complete mapping of users and groups to rids */
637}
638
639/**********************************************************************
640 Return the TDB_CONTEXT* for winbindd_idmap.  I **really** feel
641 dirty doing this, but not so dirty that I want to create another
642 tdb
643***********************************************************************/
644
645TDB_CONTEXT *idmap_tdb_handle( void )
646{
647	if ( idmap_tdb )
648		return idmap_tdb;
649
650	/* go ahead an open it;  db_idmap_init() doesn't use any params
651	   right now */
652
653	db_idmap_init( NULL );
654	if ( idmap_tdb )
655		return idmap_tdb;
656
657	return NULL;
658}
659
660static struct idmap_methods db_methods = {
661
662	db_idmap_init,
663	db_allocate_rid,
664	db_allocate_id,
665	db_get_sid_from_id,
666	db_get_id_from_sid,
667	db_set_mapping,
668	db_idmap_close,
669	db_idmap_status
670
671};
672
673NTSTATUS idmap_tdb_init(void)
674{
675	return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
676}
677