more compile fixes for become/unbecome_root()
[samba.git] / source3 / nsswitch / winbindd_idmap_tdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - user related function
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Anthony Liguori 2003
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "winbindd.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_WINBIND
28
29 /* High water mark keys */
30 #define HWM_GROUP  "GROUP HWM"
31 #define HWM_USER   "USER HWM"
32
33 /* idmap version determines auto-conversion */
34 #define IDMAP_VERSION 2
35
36 /* Globals */
37 static TDB_CONTEXT *idmap_tdb;
38
39 /* convert one record to the new format */
40 static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
41                           void *ignored)
42 {
43         struct winbindd_domain *domain;
44         char *p;
45         DOM_SID sid;
46         uint32 rid;
47         fstring keystr;
48         fstring dom_name;
49         TDB_DATA key2;
50
51         p = strchr(key.dptr, '/');
52         if (!p)
53                 return 0;
54
55         *p = 0;
56         fstrcpy(dom_name, key.dptr);
57         *p++ = '/';
58
59         domain = find_domain_from_name(dom_name);
60         if (!domain) {
61                 /* We must delete the old record. */
62                 DEBUG(0,
63                       ("winbindd: tdb_convert_fn : Unable to find domain %s\n",
64                        dom_name));
65                 DEBUG(0,
66                       ("winbindd: tdb_convert_fn : deleting record %s\n",
67                        key.dptr));
68                 tdb_delete(idmap_tdb, key);
69                 return 0;
70         }
71
72         rid = atoi(p);
73
74         sid_copy(&sid, &domain->sid);
75         sid_append_rid(&sid, rid);
76
77         sid_to_string(keystr, &sid);
78         key2.dptr = keystr;
79         key2.dsize = strlen(keystr) + 1;
80
81         if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
82                 /* not good! */
83                 DEBUG(0,
84                       ("winbindd: tdb_convert_fn : Unable to update record %s\n",
85                        key2.dptr));
86                 DEBUG(0,
87                       ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
88                 return -1;
89         }
90
91         if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
92                 /* not good! */
93                 DEBUG(0,
94                       ("winbindd: tdb_convert_fn : Unable to update record %s\n",
95                        data.dptr));
96                 DEBUG(0,
97                       ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
98                 return -1;
99         }
100
101         tdb_delete(idmap_tdb, key);
102
103         return 0;
104 }
105
106 /*****************************************************************************
107  Convert the idmap database from an older version.
108 *****************************************************************************/
109 static BOOL tdb_idmap_convert(void)
110 {
111         int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
112         BOOL bigendianheader =
113             (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
114
115         if (vers == IDMAP_VERSION)
116                 return True;
117
118         if (((vers == -1) && bigendianheader)
119             || (IREV(vers) == IDMAP_VERSION)) {
120                 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
121                 /*
122                  * high and low records were created on a
123                  * big endian machine and will need byte-reversing.
124                  */
125
126                 int32 wm;
127
128                 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
129
130                 if (wm != -1) {
131                         wm = IREV(wm);
132                 } else
133                         wm = server_state.uid_low;
134
135                 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
136                         DEBUG(0,
137                               ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n"));
138                         return False;
139                 }
140
141                 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
142                 if (wm != -1) {
143                         wm = IREV(wm);
144                 } else
145                         wm = server_state.gid_low;
146
147                 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
148                         DEBUG(0,
149                               ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
150                         return False;
151                 }
152         }
153
154         /* the old format stored as DOMAIN/rid - now we store the SID direct */
155         tdb_traverse(idmap_tdb, tdb_convert_fn, NULL);
156
157         if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) ==
158             -1) {
159                 DEBUG(0,
160                       ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
161                 return False;
162         }
163
164         return True;
165 }
166
167 /* Allocate either a user or group id from the pool */
168 static BOOL tdb_allocate_id(uid_t * id, BOOL isgroup)
169 {
170         int hwm;
171
172         /* Get current high water mark */
173         if ((hwm = tdb_fetch_int32(idmap_tdb,
174                                    isgroup ? HWM_GROUP : HWM_USER)) ==
175             -1) {
176                 return False;
177         }
178
179         /* Return next available uid in list */
180         if ((isgroup && (hwm > server_state.gid_high)) ||
181             (!isgroup && (hwm > server_state.uid_high))) {
182                 DEBUG(0,
183                       ("winbind %sid range full!\n", isgroup ? "g" : "u"));
184                 return False;
185         }
186
187         if (id) {
188                 *id = hwm;
189         }
190
191         hwm++;
192
193         /* Store new high water mark */
194         tdb_store_int32(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm);
195
196         return True;
197 }
198
199 /* Get a sid from an id */
200 static BOOL tdb_get_sid_from_id(int id, DOM_SID * sid, BOOL isgroup)
201 {
202         TDB_DATA key, data;
203         fstring keystr;
204         BOOL result = False;
205
206         slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID",
207                  id);
208
209         key.dptr = keystr;
210         key.dsize = strlen(keystr) + 1;
211
212         data = tdb_fetch(idmap_tdb, key);
213
214         if (data.dptr) {
215                 result = string_to_sid(sid, data.dptr);
216                 SAFE_FREE(data.dptr);
217         }
218
219         return result;
220 }
221
222 /* Get an id from a sid */
223 static BOOL tdb_get_id_from_sid(DOM_SID * sid, uid_t * id, BOOL isgroup)
224 {
225         TDB_DATA data, key;
226         fstring keystr;
227         BOOL result = False;
228
229         /* Check if sid is present in database */
230         sid_to_string(keystr, sid);
231
232         key.dptr = keystr;
233         key.dsize = strlen(keystr) + 1;
234
235         data = tdb_fetch(idmap_tdb, key);
236
237         if (data.dptr) {
238                 fstring scanstr;
239                 int the_id;
240
241                 /* Parse and return existing uid */
242                 fstrcpy(scanstr, isgroup ? "GID" : "UID");
243                 fstrcat(scanstr, " %d");
244
245                 if (sscanf(data.dptr, scanstr, &the_id) == 1) {
246                         /* Store uid */
247                         if (id) {
248                                 *id = the_id;
249                         }
250
251                         result = True;
252                 }
253
254                 SAFE_FREE(data.dptr);
255         } else {
256
257                 /* Allocate a new id for this sid */
258                 if (id && tdb_allocate_id(id, isgroup)) {
259                         fstring keystr2;
260
261                         /* Store new id */
262                         slprintf(keystr2, sizeof(keystr2), "%s %d",
263                                  isgroup ? "GID" : "UID", *id);
264
265                         data.dptr = keystr2;
266                         data.dsize = strlen(keystr2) + 1;
267
268                         tdb_store(idmap_tdb, key, data, TDB_REPLACE);
269                         tdb_store(idmap_tdb, data, key, TDB_REPLACE);
270
271                         result = True;
272                 }
273         }
274
275         return result;
276 }
277
278 /*****************************************************************************
279  Initialise idmap database. 
280 *****************************************************************************/
281 static BOOL tdb_idmap_init(void)
282 {
283         SMB_STRUCT_STAT stbuf;
284
285         /* move to the new database on first startup */
286         if (!file_exist(lock_path("idmap.tdb"), &stbuf)) {
287                 if (file_exist(lock_path("winbindd_idmap.tdb"), &stbuf)) {
288                         char *cmd = NULL;
289                         
290                         /* lazy file copy */
291                         if (asprintf(&cmd, "cp -p %s/winbindd_idmap.tdb %s/idmap.tdb", lp_lockdir(), lp_lockdir()) != -1) {
292                                 system(cmd);
293                                 free(cmd);
294                         }
295                         if (!file_exist(lock_path("idmap.tdb"), &stbuf)) {
296                                 DEBUG(0, ("idmap_init: Unable to make a new database copy\n"));
297                                 return False;
298                         }
299                 }
300         }
301
302         /* Open tdb cache */
303         if (!(idmap_tdb = tdb_open_log(lock_path("idmap.tdb"), 0,
304                                        TDB_DEFAULT, O_RDWR | O_CREAT,
305                                        0600))) {
306                 DEBUG(0,
307                       ("winbindd_idmap_init: Unable to open idmap database\n"));
308                 return False;
309         }
310
311         /* possibly convert from an earlier version */
312         if (!tdb_idmap_convert()) {
313                 DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n"));
314                 return False;
315         }
316
317         /* Create high water marks for group and user id */
318         if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
319                 if (tdb_store_int32
320                     (idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
321                         DEBUG(0,
322                               ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n"));
323                         return False;
324                 }
325         }
326
327         if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
328                 if (tdb_store_int32
329                     (idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
330                         DEBUG(0,
331                               ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n"));
332                         return False;
333                 }
334         }
335
336         return True;
337 }
338
339 /* Get a sid from a uid */
340 static BOOL tdb_get_sid_from_uid(uid_t uid, DOM_SID * sid)
341 {
342         return tdb_get_sid_from_id((int) uid, sid, False);
343 }
344
345 /* Get a sid from a gid */
346 static BOOL tdb_get_sid_from_gid(gid_t gid, DOM_SID * sid)
347 {
348         return tdb_get_sid_from_id((int) gid, sid, True);
349 }
350
351 /* Get a uid from a sid */
352 static BOOL tdb_get_uid_from_sid(DOM_SID * sid, uid_t * uid)
353 {
354         return tdb_get_id_from_sid(sid, uid, False);
355 }
356
357 /* Get a gid from a group sid */
358 static BOOL tdb_get_gid_from_sid(DOM_SID * sid, gid_t * gid)
359 {
360         return tdb_get_id_from_sid(sid, gid, True);
361 }
362
363 /* Close the tdb */
364 static BOOL tdb_idmap_close(void)
365 {
366         if (idmap_tdb)
367                 return (tdb_close(idmap_tdb) == 0);
368         return True;
369 }
370
371
372 /* Dump status information to log file.  Display different stuff based on
373    the debug level:
374
375    Debug Level        Information Displayed
376    =================================================================
377    0                  Percentage of [ug]id range allocated
378    0                  High water marks (next allocated ids)
379 */
380
381 #define DUMP_INFO 0
382
383 static void tdb_idmap_status(void)
384 {
385         int user_hwm, group_hwm;
386
387         DEBUG(0, ("winbindd idmap status:\n"));
388
389         /* Get current high water marks */
390
391         if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
392                 DEBUG(DUMP_INFO,
393                       ("\tCould not get userid high water mark!\n"));
394         }
395
396         if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
397                 DEBUG(DUMP_INFO,
398                       ("\tCould not get groupid high water mark!\n"));
399         }
400
401         /* Display next ids to allocate */
402
403         if (user_hwm != -1) {
404                 DEBUG(DUMP_INFO,
405                       ("\tNext userid to allocate is %d\n", user_hwm));
406         }
407
408         if (group_hwm != -1) {
409                 DEBUG(DUMP_INFO,
410                       ("\tNext groupid to allocate is %d\n", group_hwm));
411         }
412
413         /* Display percentage of id range already allocated. */
414
415         if (user_hwm != -1) {
416                 int num_users = user_hwm - server_state.uid_low;
417                 int total_users =
418                     server_state.uid_high - server_state.uid_low;
419
420                 DEBUG(DUMP_INFO,
421                       ("\tUser id range is %d%% full (%d of %d)\n",
422                        num_users * 100 / total_users, num_users,
423                        total_users));
424         }
425
426         if (group_hwm != -1) {
427                 int num_groups = group_hwm - server_state.gid_low;
428                 int total_groups =
429                     server_state.gid_high - server_state.gid_low;
430
431                 DEBUG(DUMP_INFO,
432                       ("\tGroup id range is %d%% full (%d of %d)\n",
433                        num_groups * 100 / total_groups, num_groups,
434                        total_groups));
435         }
436
437         /* Display complete mapping of users and groups to rids */
438 }
439
440 struct winbindd_idmap_methods tdb_idmap_methods = {
441         tdb_idmap_init,
442
443         tdb_get_sid_from_uid,
444         tdb_get_sid_from_gid,
445
446         tdb_get_uid_from_sid,
447         tdb_get_gid_from_sid,
448
449         tdb_idmap_close,
450
451         tdb_idmap_status
452 };
453
454 BOOL winbind_idmap_reg_tdb(struct winbindd_idmap_methods **meth)
455 {
456         *meth = &tdb_idmap_methods;
457
458         return True;
459 }