2 Unix SMB/CIFS implementation.
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Anthony Liguori 2003
8 Copyright (C) Simo Sorce 2003
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.
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.
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.
28 #define DBGC_CLASS DBGC_IDMAP
30 /* High water mark keys */
31 #define HWM_GROUP "GROUP HWM"
32 #define HWM_USER "USER HWM"
34 /* idmap version determines auto-conversion */
35 #define IDMAP_VERSION 2
38 static TDB_CONTEXT *idmap_tdb;
40 static struct idmap_state {
42 /* User and group id pool */
44 uid_t uid_low, uid_high; /* Range of uids to allocate */
45 gid_t gid_low, gid_high; /* Range of gids to allocate */
49 /* FIXME: let handle conversions when all things work ok.
50 I think it is better to handle the conversion at
51 upgrade time and leave the old db intact.
52 That would also make easier to go back to 2.2 if needed
56 /* convert one record to the new format */
57 static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
60 struct winbindd_domain *domain;
68 p = strchr(key.dptr, '/');
73 fstrcpy(dom_name, key.dptr);
76 domain = find_domain_from_name(dom_name);
78 /* We must delete the old record. */
80 ("winbindd: tdb_convert_fn : Unable to find domain %s\n",
83 ("winbindd: tdb_convert_fn : deleting record %s\n",
85 tdb_delete(idmap_tdb, key);
91 sid_copy(&sid, &domain->sid);
92 sid_append_rid(&sid, rid);
94 sid_to_string(keystr, &sid);
96 key2.dsize = strlen(keystr) + 1;
98 if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
101 ("winbindd: tdb_convert_fn : Unable to update record %s\n",
104 ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
108 if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
111 ("winbindd: tdb_convert_fn : Unable to update record %s\n",
114 ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
118 tdb_delete(idmap_tdb, key);
123 /*****************************************************************************
124 Convert the idmap database from an older version.
125 *****************************************************************************/
126 static BOOL tdb_idmap_convert(const char *idmap_name)
128 int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
129 BOOL bigendianheader =
130 (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
132 if (vers == IDMAP_VERSION)
135 if (((vers == -1) && bigendianheader)
136 || (IREV(vers) == IDMAP_VERSION)) {
137 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
139 * high and low records were created on a
140 * big endian machine and will need byte-reversing.
145 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
150 wm = server_state.uid_low;
152 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
154 ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n"));
158 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
162 wm = server_state.gid_low;
164 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
166 ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
171 /* the old format stored as DOMAIN/rid - now we store the SID direct */
172 tdb_traverse(idmap_tdb, tdb_convert_fn, NULL);
174 if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) ==
177 ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
185 /* Allocate either a user or group id from the pool */
186 static NTSTATUS db_allocate_id(unid_t *id, int id_type)
190 if (!id) return NT_STATUS_INVALID_PARAMETER;
192 /* Get current high water mark */
193 switch (id_type & ID_TYPEMASK) {
195 if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
196 return NT_STATUS_INTERNAL_DB_ERROR;
199 if (hwm > idmap_state.uid_high) {
200 DEBUG(0, ("idmap Fatal Error: UID range full!!\n"));
201 return NT_STATUS_UNSUCCESSFUL;
206 /* Store new high water mark */
207 tdb_store_int32(idmap_tdb, HWM_USER, hwm);
210 if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
211 return NT_STATUS_INTERNAL_DB_ERROR;
214 if (hwm > idmap_state.gid_high) {
215 DEBUG(0, ("idmap Fatal Error: GID range full!!\n"));
216 return NT_STATUS_UNSUCCESSFUL;
221 /* Store new high water mark */
222 tdb_store_int32(idmap_tdb, HWM_GROUP, hwm);
225 return NT_STATUS_INVALID_PARAMETER;
231 /* Get a sid from an id */
232 static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
236 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
238 if (!sid) return NT_STATUS_INVALID_PARAMETER;
240 switch (id_type & ID_TYPEMASK) {
242 slprintf(keystr, sizeof(keystr), "UID %d", id.uid);
245 slprintf(keystr, sizeof(keystr), "GID %d", id.gid);
248 return NT_STATUS_UNSUCCESSFUL;
252 key.dsize = strlen(keystr) + 1;
254 data = tdb_fetch(idmap_tdb, key);
257 if (string_to_sid(sid, data.dptr)) {
260 SAFE_FREE(data.dptr);
266 /* Get an id from a sid */
267 static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
271 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
273 if (!sid || !id || !id_type) return NT_STATUS_INVALID_PARAMETER;
275 /* Check if sid is present in database */
276 sid_to_string(keystr, sid);
279 key.dsize = strlen(keystr) + 1;
281 data = tdb_fetch(idmap_tdb, key);
284 int type = *id_type & ID_TYPEMASK;
287 if (type == ID_EMPTY || type == ID_USERID) {
288 /* Parse and return existing uid */
289 fstrcpy(scanstr, "UID %d");
291 if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
293 if (type == ID_EMPTY) {
294 *id_type = ID_USERID;
301 if (type == ID_EMPTY || type == ID_GROUPID) {
302 /* Parse and return existing gid */
303 fstrcpy(scanstr, "GID %d");
305 if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
307 if (type == ID_EMPTY) {
308 *id_type = ID_GROUPID;
314 SAFE_FREE(data.dptr);
316 } else if (!(*id_type & ID_NOMAP) &&
317 (((*id_type & ID_TYPEMASK) == ID_USERID)
318 || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
320 /* Allocate a new id for this sid */
321 ret = db_allocate_id(id, *id_type);
322 if (NT_STATUS_IS_OK(ret)) {
326 if (*id_type & ID_USERID) {
327 slprintf(keystr2, sizeof(keystr2), "UID %d", (*id).uid);
329 slprintf(keystr2, sizeof(keystr2), "GID %d", (*id).gid);
333 data.dsize = strlen(keystr2) + 1;
335 if (tdb_store(idmap_tdb, key, data, TDB_REPLACE) == -1) {
336 /* TODO: print tdb error !! */
337 return NT_STATUS_UNSUCCESSFUL;
339 if (tdb_store(idmap_tdb, data, key, TDB_REPLACE) == -1) {
340 /* TODO: print tdb error !! */
341 return NT_STATUS_UNSUCCESSFUL;
351 static NTSTATUS db_set_mapping(DOM_SID *sid, unid_t id, int id_type)
357 if (!sid) return NT_STATUS_INVALID_PARAMETER;
359 sid_to_string(ksidstr, sid);
362 ksid.dsize = strlen(ksidstr) + 1;
364 id_type &= ID_TYPEMASK;
365 if (id_type & ID_USERID) {
366 slprintf(kidstr, sizeof(kidstr), "UID %d", id.uid);
367 } else if (id_type & ID_GROUPID) {
368 slprintf(kidstr, sizeof(kidstr), "GID %d", id.gid);
370 return NT_STATUS_INVALID_PARAMETER;
374 kid.dsize = strlen(kidstr) + 1;
376 if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
377 /* TODO: print tdb error !! */
378 return NT_STATUS_UNSUCCESSFUL;
380 if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
381 /* TODO: print tdb error !! */
382 return NT_STATUS_UNSUCCESSFUL;
387 /*****************************************************************************
388 Initialise idmap database.
389 *****************************************************************************/
390 static NTSTATUS db_idmap_init(const char *db_name)
393 if (!(idmap_tdb = tdb_open_log(lock_path(db_name), 0,
394 TDB_DEFAULT, O_RDWR | O_CREAT,
396 DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
397 return NT_STATUS_UNSUCCESSFUL;
401 /* possibly convert from an earlier version */
402 if (!tdb_idmap_convert(lock_path("winbind_idmap.tdb"))) {
404 ("idmap_init: Unable to open old idmap database\n"));
409 /* Create high water marks for group and user id */
410 if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
411 if (tdb_store_int32(idmap_tdb, HWM_USER, idmap_state.uid_low) == -1) {
412 DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
413 return NT_STATUS_INTERNAL_DB_ERROR;
417 if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
418 if (tdb_store_int32(idmap_tdb, HWM_GROUP, idmap_state.gid_low) == -1) {
419 DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
420 return NT_STATUS_INTERNAL_DB_ERROR;
428 static NTSTATUS db_idmap_close(void)
431 if (tdb_close(idmap_tdb) == 0) {
434 return NT_STATUS_UNSUCCESSFUL;
441 /* Dump status information to log file. Display different stuff based on
444 Debug Level Information Displayed
445 =================================================================
446 0 Percentage of [ug]id range allocated
447 0 High water marks (next allocated ids)
452 static void db_idmap_status(void)
454 int user_hwm, group_hwm;
456 DEBUG(0, ("winbindd idmap status:\n"));
458 /* Get current high water marks */
460 if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
462 ("\tCould not get userid high water mark!\n"));
465 if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
467 ("\tCould not get groupid high water mark!\n"));
470 /* Display next ids to allocate */
472 if (user_hwm != -1) {
474 ("\tNext userid to allocate is %d\n", user_hwm));
477 if (group_hwm != -1) {
479 ("\tNext groupid to allocate is %d\n", group_hwm));
482 /* Display percentage of id range already allocated. */
484 if (user_hwm != -1) {
485 int num_users = user_hwm - idmap_state.uid_low;
487 idmap_state.uid_high - idmap_state.uid_low;
490 ("\tUser id range is %d%% full (%d of %d)\n",
491 num_users * 100 / total_users, num_users,
495 if (group_hwm != -1) {
496 int num_groups = group_hwm - idmap_state.gid_low;
498 idmap_state.gid_high - idmap_state.gid_low;
501 ("\tGroup id range is %d%% full (%d of %d)\n",
502 num_groups * 100 / total_groups, num_groups,
506 /* Display complete mapping of users and groups to rids */
509 struct idmap_methods db_methods = {
520 NTSTATUS idmap_reg_tdb(struct idmap_methods **meth)