Fixed compiler warning.
[vlendec/samba-autobuild/.git] / source3 / sam / idmap_tdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    idmap TDB backend
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Anthony Liguori 2003
8    Copyright (C) Simo Sorce 2003
9    
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.
14    
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.
19    
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.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_IDMAP
29
30 /* High water mark keys */
31 #define HWM_GROUP  "GROUP HWM"
32 #define HWM_USER   "USER HWM"
33
34 /* idmap version determines auto-conversion */
35 #define IDMAP_VERSION 2
36
37 /* Globals */
38 static TDB_CONTEXT *idmap_tdb;
39
40 static struct idmap_state {
41
42         /* User and group id pool */
43
44         uid_t uid_low, uid_high;               /* Range of uids to allocate */
45         gid_t gid_low, gid_high;               /* Range of gids to allocate */
46 } idmap_state;
47
48
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
53           ---SSS */
54 #if 0
55
56 /* convert one record to the new format */
57 static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
58                           void *ignored)
59 {
60         struct winbindd_domain *domain;
61         char *p;
62         DOM_SID sid;
63         uint32 rid;
64         fstring keystr;
65         fstring dom_name;
66         TDB_DATA key2;
67
68         p = strchr(key.dptr, '/');
69         if (!p)
70                 return 0;
71
72         *p = 0;
73         fstrcpy(dom_name, key.dptr);
74         *p++ = '/';
75
76         domain = find_domain_from_name(dom_name);
77         if (!domain) {
78                 /* We must delete the old record. */
79                 DEBUG(0,
80                       ("winbindd: tdb_convert_fn : Unable to find domain %s\n",
81                        dom_name));
82                 DEBUG(0,
83                       ("winbindd: tdb_convert_fn : deleting record %s\n",
84                        key.dptr));
85                 tdb_delete(idmap_tdb, key);
86                 return 0;
87         }
88
89         rid = atoi(p);
90
91         sid_copy(&sid, &domain->sid);
92         sid_append_rid(&sid, rid);
93
94         sid_to_string(keystr, &sid);
95         key2.dptr = keystr;
96         key2.dsize = strlen(keystr) + 1;
97
98         if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
99                 /* not good! */
100                 DEBUG(0,
101                       ("winbindd: tdb_convert_fn : Unable to update record %s\n",
102                        key2.dptr));
103                 DEBUG(0,
104                       ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
105                 return -1;
106         }
107
108         if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
109                 /* not good! */
110                 DEBUG(0,
111                       ("winbindd: tdb_convert_fn : Unable to update record %s\n",
112                        data.dptr));
113                 DEBUG(0,
114                       ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
115                 return -1;
116         }
117
118         tdb_delete(idmap_tdb, key);
119
120         return 0;
121 }
122
123 /*****************************************************************************
124  Convert the idmap database from an older version.
125 *****************************************************************************/
126 static BOOL tdb_idmap_convert(const char *idmap_name)
127 {
128         int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
129         BOOL bigendianheader =
130             (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
131
132         if (vers == IDMAP_VERSION)
133                 return True;
134
135         if (((vers == -1) && bigendianheader)
136             || (IREV(vers) == IDMAP_VERSION)) {
137                 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
138                 /*
139                  * high and low records were created on a
140                  * big endian machine and will need byte-reversing.
141                  */
142
143                 int32 wm;
144
145                 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
146
147                 if (wm != -1) {
148                         wm = IREV(wm);
149                 } else
150                         wm = server_state.uid_low;
151
152                 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
153                         DEBUG(0,
154                               ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n"));
155                         return False;
156                 }
157
158                 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
159                 if (wm != -1) {
160                         wm = IREV(wm);
161                 } else
162                         wm = server_state.gid_low;
163
164                 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
165                         DEBUG(0,
166                               ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
167                         return False;
168                 }
169         }
170
171         /* the old format stored as DOMAIN/rid - now we store the SID direct */
172         tdb_traverse(idmap_tdb, tdb_convert_fn, NULL);
173
174         if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) ==
175             -1) {
176                 DEBUG(0,
177                       ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
178                 return False;
179         }
180
181         return True;
182 }
183 #endif
184
185 /* Allocate either a user or group id from the pool */
186 static NTSTATUS db_allocate_id(unid_t *id, int id_type)
187 {
188         int hwm;
189
190         if (!id) return NT_STATUS_INVALID_PARAMETER;
191
192         /* Get current high water mark */
193         switch (id_type & ID_TYPEMASK) {
194                 case ID_USERID:
195                         if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
196                                 return NT_STATUS_INTERNAL_DB_ERROR;
197                         }
198
199                         if (hwm > idmap_state.uid_high) {
200                                 DEBUG(0, ("idmap Fatal Error: UID range full!!\n"));
201                                 return NT_STATUS_UNSUCCESSFUL;
202                         }
203
204                         (*id).uid = hwm++;
205
206                         /* Store new high water mark */
207                         tdb_store_int32(idmap_tdb, HWM_USER, hwm);
208                         break;
209                 case ID_GROUPID:
210                         if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
211                                 return NT_STATUS_INTERNAL_DB_ERROR;
212                         }
213
214                         if (hwm > idmap_state.gid_high) {
215                                 DEBUG(0, ("idmap Fatal Error: GID range full!!\n"));
216                                 return NT_STATUS_UNSUCCESSFUL;
217                         }
218
219                         (*id).gid = hwm++;
220                         
221                         /* Store new high water mark */
222                         tdb_store_int32(idmap_tdb, HWM_GROUP, hwm);
223                         break;
224                 default:
225                         return NT_STATUS_INVALID_PARAMETER;
226         }
227
228         return NT_STATUS_OK;
229 }
230
231 /* Get a sid from an id */
232 static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
233 {
234         TDB_DATA key, data;
235         fstring keystr;
236         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
237
238         if (!sid) return NT_STATUS_INVALID_PARAMETER;
239
240         switch (id_type & ID_TYPEMASK) {
241                 case ID_USERID:
242                         slprintf(keystr, sizeof(keystr), "UID %d", id.uid);
243                         break;
244                 case ID_GROUPID:
245                         slprintf(keystr, sizeof(keystr), "GID %d", id.gid);
246                         break;
247                 default:
248                         return NT_STATUS_UNSUCCESSFUL;
249         }
250
251         key.dptr = keystr;
252         key.dsize = strlen(keystr) + 1;
253
254         data = tdb_fetch(idmap_tdb, key);
255
256         if (data.dptr) {
257                 if (string_to_sid(sid, data.dptr)) {
258                         ret = NT_STATUS_OK;
259                 }
260                 SAFE_FREE(data.dptr);
261         }
262
263         return ret;
264 }
265
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)
268 {
269         TDB_DATA data, key;
270         fstring keystr;
271         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
272
273         if (!sid || !id || !id_type) return NT_STATUS_INVALID_PARAMETER;
274
275         /* Check if sid is present in database */
276         sid_to_string(keystr, sid);
277
278         key.dptr = keystr;
279         key.dsize = strlen(keystr) + 1;
280
281         data = tdb_fetch(idmap_tdb, key);
282
283         if (data.dptr) {
284                 int type = *id_type & ID_TYPEMASK;
285                 fstring scanstr;
286
287                 if (type == ID_EMPTY || type == ID_USERID) {
288                         /* Parse and return existing uid */
289                         fstrcpy(scanstr, "UID %d");
290
291                         if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
292                                 /* uid ok? */
293                                 if (type == ID_EMPTY) {
294                                         *id_type = ID_USERID;
295                                 }
296                                 ret = NT_STATUS_OK;
297                                 goto idok;
298                         }
299                 }
300
301                 if (type == ID_EMPTY || type == ID_GROUPID) {
302                         /* Parse and return existing gid */
303                         fstrcpy(scanstr, "GID %d");
304
305                         if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
306                                 /* gid ok? */
307                                 if (type == ID_EMPTY) {
308                                         *id_type = ID_GROUPID;
309                                 }
310                                 ret = NT_STATUS_OK;
311                         }
312                 }
313 idok:
314                 SAFE_FREE(data.dptr);
315
316         } else if (!(*id_type & ID_NOMAP) &&
317                    (((*id_type & ID_TYPEMASK) == ID_USERID)
318                     || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
319
320                 /* Allocate a new id for this sid */
321                 ret = db_allocate_id(id, *id_type);
322                 if (NT_STATUS_IS_OK(ret)) {
323                         fstring keystr2;
324
325                         /* Store new id */
326                         if (*id_type & ID_USERID) {
327                                 slprintf(keystr2, sizeof(keystr2), "UID %d", (*id).uid);
328                         } else {
329                                 slprintf(keystr2, sizeof(keystr2), "GID %d", (*id).gid);
330                         }
331
332                         data.dptr = keystr2;
333                         data.dsize = strlen(keystr2) + 1;
334
335                         if (tdb_store(idmap_tdb, key, data, TDB_REPLACE) == -1) {
336                                 /* TODO: print tdb error !! */
337                                 return NT_STATUS_UNSUCCESSFUL;
338                         }
339                         if (tdb_store(idmap_tdb, data, key, TDB_REPLACE) == -1) {
340                                 /* TODO: print tdb error !! */
341                                 return NT_STATUS_UNSUCCESSFUL;
342                         }
343
344                         ret = NT_STATUS_OK;
345                 }
346         }
347         
348         return ret;
349 }
350
351 static NTSTATUS db_set_mapping(DOM_SID *sid, unid_t id, int id_type)
352 {
353         TDB_DATA ksid, kid;
354         fstring ksidstr;
355         fstring kidstr;
356
357         if (!sid) return NT_STATUS_INVALID_PARAMETER;
358
359         sid_to_string(ksidstr, sid);
360
361         ksid.dptr = ksidstr;
362         ksid.dsize = strlen(ksidstr) + 1;
363
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);
369         } else {
370                 return NT_STATUS_INVALID_PARAMETER;
371         }
372
373         kid.dptr = kidstr;
374         kid.dsize = strlen(kidstr) + 1;
375
376         if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
377                 /* TODO: print tdb error !! */
378                 return NT_STATUS_UNSUCCESSFUL;
379         }
380         if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
381                 /* TODO: print tdb error !! */
382                 return NT_STATUS_UNSUCCESSFUL;
383         }
384         return NT_STATUS_OK;
385 }
386
387 /*****************************************************************************
388  Initialise idmap database. 
389 *****************************************************************************/
390 static NTSTATUS db_idmap_init(const char *db_name)
391 {
392         /* Open tdb cache */
393         if (!(idmap_tdb = tdb_open_log(lock_path(db_name), 0,
394                                        TDB_DEFAULT, O_RDWR | O_CREAT,
395                                        0600))) {
396                 DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
397                 return NT_STATUS_UNSUCCESSFUL;
398         }
399
400 #if 0
401         /* possibly convert from an earlier version */
402         if (!tdb_idmap_convert(lock_path("winbind_idmap.tdb"))) {
403                 DEBUG(0,
404                       ("idmap_init: Unable to open old idmap database\n"));
405                 return False;
406         }
407 #endif
408
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;
414                 }
415         }
416
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;
421                 }
422         }
423
424         return NT_STATUS_OK;
425 }
426
427 /* Close the tdb */
428 static NTSTATUS db_idmap_close(void)
429 {
430         if (idmap_tdb) {
431                 if (tdb_close(idmap_tdb) == 0) {
432                         return NT_STATUS_OK;
433                 } else {
434                         return NT_STATUS_UNSUCCESSFUL;
435                 }
436         }
437         return NT_STATUS_OK;
438 }
439
440
441 /* Dump status information to log file.  Display different stuff based on
442    the debug level:
443
444    Debug Level        Information Displayed
445    =================================================================
446    0                  Percentage of [ug]id range allocated
447    0                  High water marks (next allocated ids)
448 */
449
450 #define DUMP_INFO 0
451
452 static void db_idmap_status(void)
453 {
454         int user_hwm, group_hwm;
455
456         DEBUG(0, ("winbindd idmap status:\n"));
457
458         /* Get current high water marks */
459
460         if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
461                 DEBUG(DUMP_INFO,
462                       ("\tCould not get userid high water mark!\n"));
463         }
464
465         if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
466                 DEBUG(DUMP_INFO,
467                       ("\tCould not get groupid high water mark!\n"));
468         }
469
470         /* Display next ids to allocate */
471
472         if (user_hwm != -1) {
473                 DEBUG(DUMP_INFO,
474                       ("\tNext userid to allocate is %d\n", user_hwm));
475         }
476
477         if (group_hwm != -1) {
478                 DEBUG(DUMP_INFO,
479                       ("\tNext groupid to allocate is %d\n", group_hwm));
480         }
481
482         /* Display percentage of id range already allocated. */
483
484         if (user_hwm != -1) {
485                 int num_users = user_hwm - idmap_state.uid_low;
486                 int total_users =
487                     idmap_state.uid_high - idmap_state.uid_low;
488
489                 DEBUG(DUMP_INFO,
490                       ("\tUser id range is %d%% full (%d of %d)\n",
491                        num_users * 100 / total_users, num_users,
492                        total_users));
493         }
494
495         if (group_hwm != -1) {
496                 int num_groups = group_hwm - idmap_state.gid_low;
497                 int total_groups =
498                     idmap_state.gid_high - idmap_state.gid_low;
499
500                 DEBUG(DUMP_INFO,
501                       ("\tGroup id range is %d%% full (%d of %d)\n",
502                        num_groups * 100 / total_groups, num_groups,
503                        total_groups));
504         }
505
506         /* Display complete mapping of users and groups to rids */
507 }
508
509 struct idmap_methods db_methods = {
510
511         db_idmap_init,
512         db_get_sid_from_id,
513         db_get_id_from_sid,
514         db_set_mapping,
515         db_idmap_close,
516         db_idmap_status
517
518 };
519
520 NTSTATUS idmap_reg_tdb(struct idmap_methods **meth)
521 {
522         *meth = &db_methods;
523
524         return NT_STATUS_OK;
525 }
526