removed two unneeded files after Richard backed out these changes.
[Samba.git] / source / nsswitch / winbindd_idmap.c
blob0594f61680173a8dd0f6cacc01b7716ebd61ae16
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - user related function
6 Copyright (C) Tim Potter 2000
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "winbindd.h"
25 /* High water mark keys */
27 #define HWM_GROUP "GROUP HWM"
28 #define HWM_USER "USER HWM"
30 /* idmap version determines auto-conversion */
31 #define IDMAP_VERSION 2
33 /* Globals */
35 static TDB_CONTEXT *idmap_tdb;
37 /* Allocate either a user or group id from the pool */
39 static BOOL allocate_id(uid_t *id, BOOL isgroup)
41 int hwm;
43 /* Get current high water mark */
45 if ((hwm = tdb_fetch_int32(idmap_tdb,
46 isgroup ? HWM_GROUP : HWM_USER)) == -1) {
47 return False;
50 /* Return next available uid in list */
52 if ((isgroup && (hwm > server_state.gid_high)) ||
53 (!isgroup && (hwm > server_state.uid_high))) {
54 DEBUG(0, ("winbind %sid range full!\n", isgroup ? "g" : "u"));
55 return False;
58 if (id) {
59 *id = hwm;
62 hwm++;
64 /* Store new high water mark */
66 tdb_store_int32(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm);
68 return True;
71 /* Get an id from a rid */
72 static BOOL get_id_from_sid(DOM_SID *sid, uid_t *id, BOOL isgroup)
74 TDB_DATA data, key;
75 fstring keystr;
76 BOOL result = False;
78 /* Check if sid is present in database */
79 sid_to_string(keystr, sid);
81 key.dptr = keystr;
82 key.dsize = strlen(keystr) + 1;
84 data = tdb_fetch(idmap_tdb, key);
86 if (data.dptr) {
87 fstring scanstr;
88 int the_id;
90 /* Parse and return existing uid */
91 fstrcpy(scanstr, isgroup ? "GID" : "UID");
92 fstrcat(scanstr, " %d");
94 if (sscanf(data.dptr, scanstr, &the_id) == 1) {
95 /* Store uid */
96 if (id) {
97 *id = the_id;
100 result = True;
103 SAFE_FREE(data.dptr);
104 } else {
106 /* Allocate a new id for this sid */
108 if (id && allocate_id(id, isgroup)) {
109 fstring keystr2;
111 /* Store new id */
113 slprintf(keystr2, sizeof(keystr2), "%s %d", isgroup ? "GID" : "UID", *id);
115 data.dptr = keystr2;
116 data.dsize = strlen(keystr2) + 1;
118 tdb_store(idmap_tdb, key, data, TDB_REPLACE);
119 tdb_store(idmap_tdb, data, key, TDB_REPLACE);
121 result = True;
125 return result;
128 /* Get a uid from a user sid */
129 BOOL winbindd_idmap_get_uid_from_sid(DOM_SID *sid, uid_t *uid)
131 return get_id_from_sid(sid, uid, False);
134 /* Get a gid from a group sid */
135 BOOL winbindd_idmap_get_gid_from_sid(DOM_SID *sid, gid_t *gid)
137 return get_id_from_sid(sid, gid, True);
140 /* Get a uid from a user rid */
141 BOOL winbindd_idmap_get_uid_from_rid(const char *dom_name, uint32 rid, uid_t *uid)
143 struct winbindd_domain *domain;
144 DOM_SID sid;
146 if (!(domain = find_domain_from_name(dom_name))) {
147 return False;
150 sid_copy(&sid, &domain->sid);
151 sid_append_rid(&sid, rid);
153 return get_id_from_sid(&sid, uid, False);
156 /* Get a gid from a group rid */
157 BOOL winbindd_idmap_get_gid_from_rid(const char *dom_name, uint32 rid, gid_t *gid)
159 struct winbindd_domain *domain;
160 DOM_SID sid;
162 if (!(domain = find_domain_from_name(dom_name))) {
163 return False;
166 sid_copy(&sid, &domain->sid);
167 sid_append_rid(&sid, rid);
169 return get_id_from_sid(&sid, gid, True);
173 BOOL get_sid_from_id(int id, DOM_SID *sid, BOOL isgroup)
175 TDB_DATA key, data;
176 fstring keystr;
177 BOOL result = False;
179 slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID", id);
181 key.dptr = keystr;
182 key.dsize = strlen(keystr) + 1;
184 data = tdb_fetch(idmap_tdb, key);
186 if (data.dptr) {
187 result = string_to_sid(sid, data.dptr);
188 SAFE_FREE(data.dptr);
191 return result;
194 /* Get a sid from a uid */
195 BOOL winbindd_idmap_get_sid_from_uid(uid_t uid, DOM_SID *sid)
197 return get_sid_from_id((int)uid, sid, False);
200 /* Get a sid from a gid */
201 BOOL winbindd_idmap_get_sid_from_gid(gid_t gid, DOM_SID *sid)
203 return get_sid_from_id((int)gid, sid, True);
206 /* Get a user rid from a uid */
207 BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid,
208 struct winbindd_domain **domain)
210 DOM_SID sid;
212 if (!get_sid_from_id((int)uid, &sid, False)) {
213 return False;
216 *domain = find_domain_from_sid(&sid);
217 if (! *domain) return False;
219 sid_split_rid(&sid, user_rid);
221 return True;
224 /* Get a group rid from a gid */
226 BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid,
227 struct winbindd_domain **domain)
229 DOM_SID sid;
231 if (!get_sid_from_id((int)gid, &sid, True)) {
232 return False;
235 *domain = find_domain_from_sid(&sid);
236 if (! *domain) return False;
238 sid_split_rid(&sid, group_rid);
240 return True;
243 /* convert one record to the new format */
244 static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *ignored)
246 struct winbindd_domain *domain;
247 char *p;
248 DOM_SID sid;
249 uint32 rid;
250 fstring keystr;
251 fstring dom_name;
252 TDB_DATA key2;
254 p = strchr(key.dptr, '/');
255 if (!p)
256 return 0;
258 *p = 0;
259 fstrcpy(dom_name, key.dptr);
260 *p++ = '/';
262 domain = find_domain_from_name(dom_name);
263 if (!domain) {
264 /* We must delete the old record. */
265 DEBUG(0,("winbindd: convert_fn : Unable to find domain %s\n", dom_name ));
266 DEBUG(0,("winbindd: convert_fn : deleting record %s\n", key.dptr ));
267 tdb_delete(idmap_tdb, key);
268 return 0;
271 rid = atoi(p);
273 sid_copy(&sid, &domain->sid);
274 sid_append_rid(&sid, rid);
276 sid_to_string(keystr, &sid);
277 key2.dptr = keystr;
278 key2.dsize = strlen(keystr) + 1;
280 if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
281 /* not good! */
282 DEBUG(0,("winbindd: convert_fn : Unable to update record %s\n", key2.dptr ));
283 DEBUG(0,("winbindd: convert_fn : conversion failed - idmap corrupt ?\n"));
284 return -1;
287 if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
288 /* not good! */
289 DEBUG(0,("winbindd: convert_fn : Unable to update record %s\n", data.dptr ));
290 DEBUG(0,("winbindd: convert_fn : conversion failed - idmap corrupt ?\n"));
291 return -1;
294 tdb_delete(idmap_tdb, key);
296 return 0;
299 #if 0
300 /*****************************************************************************
301 Make a backup copy of the old idmap just to be safe.... JRA.
302 *****************************************************************************/
304 static BOOL backup_old_idmap(const char *idmap_name)
306 pstring new_name;
307 int outfd = -1;
308 SMB_OFF_T size;
309 struct stat st;
311 pstrcpy(new_name, idmap_name);
312 pstrcat(new_name, ".bak");
314 DEBUG(10,("backup_old_idmap: backing up %s to %s before upgrade.\n",
315 idmap_name, new_name ));
317 if (tdb_lockall(idmap_tdb) == -1) {
318 DEBUG(10,("backup_old_idmap: failed to lock %s. Error %s\n",
319 idmap_name, tdb_errorstr(idmap_tdb) ));
320 return False;
322 if ((outfd = open(new_name, O_CREAT|O_EXCL|O_RDWR, 0600)) == -1) {
323 DEBUG(10,("backup_old_idmap: failed to open %s. Error %s\n",
324 new_name, strerror(errno) ));
325 goto fail;
328 if (fstat(idmap_tdb->fd, &st) == -1) {
329 DEBUG(10,("backup_old_idmap: failed to fstat %s. Error %s\n",
330 idmap_name, strerror(errno) ));
331 goto fail;
334 size = (SMB_OFF_T)st.st_size;
336 if (transfer_file(idmap_tdb->fd, outfd, size) != size ) {
337 DEBUG(10,("backup_old_idmap: failed to copy %s. Error %s\n",
338 idmap_name, strerror(errno) ));
339 goto fail;
342 if (close(outfd) == -1) {
343 DEBUG(10,("backup_old_idmap: failed to close %s. Error %s\n",
344 idmap_name, strerror(errno) ));
345 outfd = -1;
346 goto fail;
348 tdb_unlockall(idmap_tdb);
349 return True;
351 fail:
353 if (outfd != -1)
354 close(outfd);
355 tdb_unlockall(idmap_tdb);
356 return False;
358 #endif
360 /*****************************************************************************
361 Convert the idmap database from an older version.
362 *****************************************************************************/
364 static BOOL idmap_convert(const char *idmap_name)
366 int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
367 BOOL bigendianheader = (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
369 if (vers == IDMAP_VERSION)
370 return True;
372 #if 0
373 /* Make a backup copy before doing anything else.... */
374 if (!backup_old_idmap(idmap_name))
375 return False;
376 #endif
378 if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
379 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
381 * high and low records were created on a
382 * big endian machine and will need byte-reversing.
385 int32 wm;
387 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
389 if (wm != -1) {
390 wm = IREV(wm);
391 } else
392 wm = server_state.uid_low;
394 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
395 DEBUG(0, ("idmap_convert: Unable to byteswap user hwm in idmap database\n"));
396 return False;
399 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
400 if (wm != -1) {
401 wm = IREV(wm);
402 } else
403 wm = server_state.gid_low;
405 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
406 DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
407 return False;
411 /* the old format stored as DOMAIN/rid - now we store the SID direct */
412 tdb_traverse(idmap_tdb, convert_fn, NULL);
414 if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
415 DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
416 return False;
419 return True;
422 /*****************************************************************************
423 Initialise idmap database.
424 *****************************************************************************/
426 BOOL winbindd_idmap_init(void)
428 /* Open tdb cache */
430 if (!(idmap_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0,
431 TDB_DEFAULT, O_RDWR | O_CREAT, 0600))) {
432 DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n"));
433 return False;
436 /* possibly convert from an earlier version */
437 if (!idmap_convert(lock_path("winbindd_idmap.tdb"))) {
438 DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n"));
439 return False;
442 /* Create high water marks for group and user id */
444 if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
445 if (tdb_store_int32(idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
446 DEBUG(0, ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n"));
447 return False;
451 if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
452 if (tdb_store_int32(idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
453 DEBUG(0, ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n"));
454 return False;
458 return True;
461 BOOL winbindd_idmap_close(void)
463 if (idmap_tdb)
464 return (tdb_close(idmap_tdb) == 0);
465 return True;
468 /* Dump status information to log file. Display different stuff based on
469 the debug level:
471 Debug Level Information Displayed
472 =================================================================
473 0 Percentage of [ug]id range allocated
474 0 High water marks (next allocated ids)
477 #define DUMP_INFO 0
479 void winbindd_idmap_status(void)
481 int user_hwm, group_hwm;
483 DEBUG(0, ("winbindd idmap status:\n"));
485 /* Get current high water marks */
487 if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
488 DEBUG(DUMP_INFO, ("\tCould not get userid high water mark!\n"));
491 if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
492 DEBUG(DUMP_INFO, ("\tCould not get groupid high water mark!\n"));
495 /* Display next ids to allocate */
497 if (user_hwm != -1) {
498 DEBUG(DUMP_INFO, ("\tNext userid to allocate is %d\n", user_hwm));
501 if (group_hwm != -1) {
502 DEBUG(DUMP_INFO, ("\tNext groupid to allocate is %d\n", group_hwm));
505 /* Display percentage of id range already allocated. */
507 if (user_hwm != -1) {
508 int num_users = user_hwm - server_state.uid_low;
509 int total_users = server_state.uid_high - server_state.uid_low;
511 DEBUG(DUMP_INFO, ("\tUser id range is %d%% full (%d of %d)\n",
512 num_users * 100 / total_users, num_users,
513 total_users));
516 if (group_hwm != -1) {
517 int num_groups = group_hwm - server_state.gid_low;
518 int total_groups = server_state.gid_high - server_state.gid_low;
520 DEBUG(DUMP_INFO, ("\tGroup id range is %d%% full (%d of %d)\n",
521 num_groups * 100 / total_groups, num_groups,
522 total_groups));
525 /* Display complete mapping of users and groups to rids */