Added comments to make it clearer when we're assigning a pointer that it
[Samba/gebeck_regimport.git] / source / nsswitch / winbindd_idmap.c
blob6d184fec5fe4266bf7f23664d870baab60834eb2
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 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_WINBIND
28 /* High water mark keys */
30 #define HWM_GROUP "GROUP HWM"
31 #define HWM_USER "USER HWM"
33 /* idmap version determines auto-conversion */
34 #define IDMAP_VERSION 2
36 /* Globals */
38 static TDB_CONTEXT *idmap_tdb;
40 /* Allocate either a user or group id from the pool */
42 static BOOL allocate_id(uid_t *id, BOOL isgroup)
44 int hwm;
46 /* Get current high water mark */
48 if ((hwm = tdb_fetch_int32(idmap_tdb,
49 isgroup ? HWM_GROUP : HWM_USER)) == -1) {
50 return False;
53 /* Return next available uid in list */
55 if ((isgroup && (hwm > server_state.gid_high)) ||
56 (!isgroup && (hwm > server_state.uid_high))) {
57 DEBUG(0, ("winbind %sid range full!\n", isgroup ? "g" : "u"));
58 return False;
61 if (id) {
62 *id = hwm;
65 hwm++;
67 /* Store new high water mark */
69 tdb_store_int32(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm);
71 return True;
74 /* Get an id from a rid */
75 static BOOL get_id_from_sid(DOM_SID *sid, uid_t *id, BOOL isgroup)
77 TDB_DATA data, key;
78 fstring keystr;
79 BOOL result = False;
81 /* Check if sid is present in database */
82 sid_to_string(keystr, sid);
84 key.dptr = keystr;
85 key.dsize = strlen(keystr) + 1;
87 data = tdb_fetch(idmap_tdb, key);
89 if (data.dptr) {
90 fstring scanstr;
91 int the_id;
93 /* Parse and return existing uid */
94 fstrcpy(scanstr, isgroup ? "GID" : "UID");
95 fstrcat(scanstr, " %d");
97 if (sscanf(data.dptr, scanstr, &the_id) == 1) {
98 /* Store uid */
99 if (id) {
100 *id = the_id;
103 result = True;
106 SAFE_FREE(data.dptr);
107 } else {
109 /* Allocate a new id for this sid */
111 if (id && allocate_id(id, isgroup)) {
112 fstring keystr2;
114 /* Store new id */
116 slprintf(keystr2, sizeof(keystr2), "%s %d", isgroup ? "GID" : "UID", *id);
118 data.dptr = keystr2;
119 data.dsize = strlen(keystr2) + 1;
121 tdb_store(idmap_tdb, key, data, TDB_REPLACE);
122 tdb_store(idmap_tdb, data, key, TDB_REPLACE);
124 result = True;
128 return result;
131 /* Get a uid from a user sid */
132 BOOL winbindd_idmap_get_uid_from_sid(DOM_SID *sid, uid_t *uid)
134 return get_id_from_sid(sid, uid, False);
137 /* Get a gid from a group sid */
138 BOOL winbindd_idmap_get_gid_from_sid(DOM_SID *sid, gid_t *gid)
140 return get_id_from_sid(sid, gid, True);
143 /* Get a uid from a user rid */
144 BOOL winbindd_idmap_get_uid_from_rid(const char *dom_name, uint32 rid, uid_t *uid)
146 struct winbindd_domain *domain;
147 DOM_SID sid;
149 if (!(domain = find_domain_from_name(dom_name))) {
150 return False;
153 sid_copy(&sid, &domain->sid);
154 sid_append_rid(&sid, rid);
156 return get_id_from_sid(&sid, uid, False);
159 /* Get a gid from a group rid */
160 BOOL winbindd_idmap_get_gid_from_rid(const char *dom_name, uint32 rid, gid_t *gid)
162 struct winbindd_domain *domain;
163 DOM_SID sid;
165 if (!(domain = find_domain_from_name(dom_name))) {
166 return False;
169 sid_copy(&sid, &domain->sid);
170 sid_append_rid(&sid, rid);
172 return get_id_from_sid(&sid, gid, True);
176 BOOL get_sid_from_id(int id, DOM_SID *sid, BOOL isgroup)
178 TDB_DATA key, data;
179 fstring keystr;
180 BOOL result = False;
182 slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID", id);
184 key.dptr = keystr;
185 key.dsize = strlen(keystr) + 1;
187 data = tdb_fetch(idmap_tdb, key);
189 if (data.dptr) {
190 result = string_to_sid(sid, data.dptr);
191 SAFE_FREE(data.dptr);
194 return result;
197 /* Get a sid from a uid */
198 BOOL winbindd_idmap_get_sid_from_uid(uid_t uid, DOM_SID *sid)
200 return get_sid_from_id((int)uid, sid, False);
203 /* Get a sid from a gid */
204 BOOL winbindd_idmap_get_sid_from_gid(gid_t gid, DOM_SID *sid)
206 return get_sid_from_id((int)gid, sid, True);
209 /* Get a user rid from a uid */
210 BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid,
211 struct winbindd_domain **domain)
213 DOM_SID sid;
215 if (!get_sid_from_id((int)uid, &sid, False)) {
216 return False;
219 *domain = find_domain_from_sid(&sid);
220 if (! *domain) return False;
222 sid_split_rid(&sid, user_rid);
224 return True;
227 /* Get a group rid from a gid */
229 BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid,
230 struct winbindd_domain **domain)
232 DOM_SID sid;
234 if (!get_sid_from_id((int)gid, &sid, True)) {
235 return False;
238 *domain = find_domain_from_sid(&sid);
239 if (! *domain) return False;
241 sid_split_rid(&sid, group_rid);
243 return True;
246 /* convert one record to the new format */
247 static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *ignored)
249 struct winbindd_domain *domain;
250 char *p;
251 DOM_SID sid;
252 uint32 rid;
253 fstring keystr;
254 fstring dom_name;
255 TDB_DATA key2;
257 p = strchr(key.dptr, '/');
258 if (!p)
259 return 0;
261 *p = 0;
262 fstrcpy(dom_name, key.dptr);
263 *p++ = '/';
265 domain = find_domain_from_name(dom_name);
266 if (!domain) {
267 /* We must delete the old record. */
268 DEBUG(0,("winbindd: convert_fn : Unable to find domain %s\n", dom_name ));
269 DEBUG(0,("winbindd: convert_fn : deleting record %s\n", key.dptr ));
270 tdb_delete(idmap_tdb, key);
271 return 0;
274 rid = atoi(p);
276 sid_copy(&sid, &domain->sid);
277 sid_append_rid(&sid, rid);
279 sid_to_string(keystr, &sid);
280 key2.dptr = keystr;
281 key2.dsize = strlen(keystr) + 1;
283 if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
284 /* not good! */
285 DEBUG(0,("winbindd: convert_fn : Unable to update record %s\n", key2.dptr ));
286 DEBUG(0,("winbindd: convert_fn : conversion failed - idmap corrupt ?\n"));
287 return -1;
290 if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
291 /* not good! */
292 DEBUG(0,("winbindd: convert_fn : Unable to update record %s\n", data.dptr ));
293 DEBUG(0,("winbindd: convert_fn : conversion failed - idmap corrupt ?\n"));
294 return -1;
297 tdb_delete(idmap_tdb, key);
299 return 0;
302 #if 0
303 /*****************************************************************************
304 Make a backup copy of the old idmap just to be safe.... JRA.
305 *****************************************************************************/
307 static BOOL backup_old_idmap(const char *idmap_name)
309 pstring new_name;
310 int outfd = -1;
311 SMB_OFF_T size;
312 struct stat st;
314 pstrcpy(new_name, idmap_name);
315 pstrcat(new_name, ".bak");
317 DEBUG(10,("backup_old_idmap: backing up %s to %s before upgrade.\n",
318 idmap_name, new_name ));
320 if (tdb_lockall(idmap_tdb) == -1) {
321 DEBUG(10,("backup_old_idmap: failed to lock %s. Error %s\n",
322 idmap_name, tdb_errorstr(idmap_tdb) ));
323 return False;
325 if ((outfd = open(new_name, O_CREAT|O_EXCL|O_RDWR, 0600)) == -1) {
326 DEBUG(10,("backup_old_idmap: failed to open %s. Error %s\n",
327 new_name, strerror(errno) ));
328 goto fail;
331 if (fstat(idmap_tdb->fd, &st) == -1) {
332 DEBUG(10,("backup_old_idmap: failed to fstat %s. Error %s\n",
333 idmap_name, strerror(errno) ));
334 goto fail;
337 size = (SMB_OFF_T)st.st_size;
339 if (transfer_file(idmap_tdb->fd, outfd, size) != size ) {
340 DEBUG(10,("backup_old_idmap: failed to copy %s. Error %s\n",
341 idmap_name, strerror(errno) ));
342 goto fail;
345 if (close(outfd) == -1) {
346 DEBUG(10,("backup_old_idmap: failed to close %s. Error %s\n",
347 idmap_name, strerror(errno) ));
348 outfd = -1;
349 goto fail;
351 tdb_unlockall(idmap_tdb);
352 return True;
354 fail:
356 if (outfd != -1)
357 close(outfd);
358 tdb_unlockall(idmap_tdb);
359 return False;
361 #endif
363 /*****************************************************************************
364 Convert the idmap database from an older version.
365 *****************************************************************************/
367 static BOOL idmap_convert(const char *idmap_name)
369 int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
370 BOOL bigendianheader = (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
372 if (vers == IDMAP_VERSION)
373 return True;
375 #if 0
376 /* Make a backup copy before doing anything else.... */
377 if (!backup_old_idmap(idmap_name))
378 return False;
379 #endif
381 if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
382 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
384 * high and low records were created on a
385 * big endian machine and will need byte-reversing.
388 int32 wm;
390 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
392 if (wm != -1) {
393 wm = IREV(wm);
394 } else
395 wm = server_state.uid_low;
397 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
398 DEBUG(0, ("idmap_convert: Unable to byteswap user hwm in idmap database\n"));
399 return False;
402 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
403 if (wm != -1) {
404 wm = IREV(wm);
405 } else
406 wm = server_state.gid_low;
408 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
409 DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
410 return False;
414 /* the old format stored as DOMAIN/rid - now we store the SID direct */
415 tdb_traverse(idmap_tdb, convert_fn, NULL);
417 if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
418 DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
419 return False;
422 return True;
425 /*****************************************************************************
426 Initialise idmap database.
427 *****************************************************************************/
429 BOOL winbindd_idmap_init(void)
431 /* Open tdb cache */
433 if (!(idmap_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0,
434 TDB_DEFAULT, O_RDWR | O_CREAT, 0600))) {
435 DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n"));
436 return False;
439 /* possibly convert from an earlier version */
440 if (!idmap_convert(lock_path("winbindd_idmap.tdb"))) {
441 DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n"));
442 return False;
445 /* Create high water marks for group and user id */
447 if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
448 if (tdb_store_int32(idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
449 DEBUG(0, ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n"));
450 return False;
454 if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
455 if (tdb_store_int32(idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
456 DEBUG(0, ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n"));
457 return False;
461 return True;
464 BOOL winbindd_idmap_close(void)
466 if (idmap_tdb)
467 return (tdb_close(idmap_tdb) == 0);
468 return True;
471 /* Dump status information to log file. Display different stuff based on
472 the debug level:
474 Debug Level Information Displayed
475 =================================================================
476 0 Percentage of [ug]id range allocated
477 0 High water marks (next allocated ids)
480 #define DUMP_INFO 0
482 void winbindd_idmap_status(void)
484 int user_hwm, group_hwm;
486 DEBUG(0, ("winbindd idmap status:\n"));
488 /* Get current high water marks */
490 if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
491 DEBUG(DUMP_INFO, ("\tCould not get userid high water mark!\n"));
494 if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
495 DEBUG(DUMP_INFO, ("\tCould not get groupid high water mark!\n"));
498 /* Display next ids to allocate */
500 if (user_hwm != -1) {
501 DEBUG(DUMP_INFO, ("\tNext userid to allocate is %d\n", user_hwm));
504 if (group_hwm != -1) {
505 DEBUG(DUMP_INFO, ("\tNext groupid to allocate is %d\n", group_hwm));
508 /* Display percentage of id range already allocated. */
510 if (user_hwm != -1) {
511 int num_users = user_hwm - server_state.uid_low;
512 int total_users = server_state.uid_high - server_state.uid_low;
514 DEBUG(DUMP_INFO, ("\tUser id range is %d%% full (%d of %d)\n",
515 num_users * 100 / total_users, num_users,
516 total_users));
519 if (group_hwm != -1) {
520 int num_groups = group_hwm - server_state.gid_low;
521 int total_groups = server_state.gid_high - server_state.gid_low;
523 DEBUG(DUMP_INFO, ("\tGroup id range is %d%% full (%d of %d)\n",
524 num_groups * 100 / total_groups, num_groups,
525 total_groups));
528 /* Display complete mapping of users and groups to rids */