This commit was generated by cvs2svn to compensate for changes in r30,
[samba.git] / source4 / 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(const char *idmap_name)
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         /* Open tdb cache */
284         if (!(idmap_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0,
285                                        TDB_DEFAULT, O_RDWR | O_CREAT,
286                                        0600))) {
287                 DEBUG(0,
288                       ("winbindd_idmap_init: Unable to open idmap database\n"));
289                 return False;
290         }
291
292         /* possibly convert from an earlier version */
293         if (!tdb_idmap_convert(lock_path("winbindd_idmap.tdb"))) {
294                 DEBUG(0,
295                       ("winbindd_idmap_init: Unable to open idmap database\n"));
296                 return False;
297         }
298
299         /* Create high water marks for group and user id */
300         if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
301                 if (tdb_store_int32
302                     (idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
303                         DEBUG(0,
304                               ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n"));
305                         return False;
306                 }
307         }
308
309         if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
310                 if (tdb_store_int32
311                     (idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
312                         DEBUG(0,
313                               ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n"));
314                         return False;
315                 }
316         }
317
318         return True;
319 }
320
321 /* Get a sid from a uid */
322 static BOOL tdb_get_sid_from_uid(uid_t uid, DOM_SID * sid)
323 {
324         return tdb_get_sid_from_id((int) uid, sid, False);
325 }
326
327 /* Get a sid from a gid */
328 static BOOL tdb_get_sid_from_gid(gid_t gid, DOM_SID * sid)
329 {
330         return tdb_get_sid_from_id((int) gid, sid, True);
331 }
332
333 /* Get a uid from a sid */
334 static BOOL tdb_get_uid_from_sid(DOM_SID * sid, uid_t * uid)
335 {
336         return tdb_get_id_from_sid(sid, uid, False);
337 }
338
339 /* Get a gid from a group sid */
340 static BOOL tdb_get_gid_from_sid(DOM_SID * sid, gid_t * gid)
341 {
342         return tdb_get_id_from_sid(sid, gid, True);
343 }
344
345 /* Close the tdb */
346 static BOOL tdb_idmap_close(void)
347 {
348         if (idmap_tdb)
349                 return (tdb_close(idmap_tdb) == 0);
350         return True;
351 }
352
353
354 /* Dump status information to log file.  Display different stuff based on
355    the debug level:
356
357    Debug Level        Information Displayed
358    =================================================================
359    0                  Percentage of [ug]id range allocated
360    0                  High water marks (next allocated ids)
361 */
362
363 #define DUMP_INFO 0
364
365 static void tdb_idmap_status(void)
366 {
367         int user_hwm, group_hwm;
368
369         DEBUG(0, ("winbindd idmap status:\n"));
370
371         /* Get current high water marks */
372
373         if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
374                 DEBUG(DUMP_INFO,
375                       ("\tCould not get userid high water mark!\n"));
376         }
377
378         if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
379                 DEBUG(DUMP_INFO,
380                       ("\tCould not get groupid high water mark!\n"));
381         }
382
383         /* Display next ids to allocate */
384
385         if (user_hwm != -1) {
386                 DEBUG(DUMP_INFO,
387                       ("\tNext userid to allocate is %d\n", user_hwm));
388         }
389
390         if (group_hwm != -1) {
391                 DEBUG(DUMP_INFO,
392                       ("\tNext groupid to allocate is %d\n", group_hwm));
393         }
394
395         /* Display percentage of id range already allocated. */
396
397         if (user_hwm != -1) {
398                 int num_users = user_hwm - server_state.uid_low;
399                 int total_users =
400                     server_state.uid_high - server_state.uid_low;
401
402                 DEBUG(DUMP_INFO,
403                       ("\tUser id range is %d%% full (%d of %d)\n",
404                        num_users * 100 / total_users, num_users,
405                        total_users));
406         }
407
408         if (group_hwm != -1) {
409                 int num_groups = group_hwm - server_state.gid_low;
410                 int total_groups =
411                     server_state.gid_high - server_state.gid_low;
412
413                 DEBUG(DUMP_INFO,
414                       ("\tGroup id range is %d%% full (%d of %d)\n",
415                        num_groups * 100 / total_groups, num_groups,
416                        total_groups));
417         }
418
419         /* Display complete mapping of users and groups to rids */
420 }
421
422 struct idmap_methods tdb_idmap_methods = {
423         tdb_idmap_init,
424
425         tdb_get_sid_from_uid,
426         tdb_get_sid_from_gid,
427
428         tdb_get_uid_from_sid,
429         tdb_get_gid_from_sid,
430
431         tdb_idmap_close,
432
433         tdb_idmap_status
434 };
435
436 BOOL winbind_idmap_reg_tdb(struct idmap_methods **meth)
437 {
438         *meth = &tdb_idmap_methods;
439
440         return True;
441 }