Sorry idra for messing around with your stuff, but this was obviously not
[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 /* Allocate either a user or group id from the pool */
49 static NTSTATUS db_allocate_id(unid_t *id, int id_type)
50 {
51         BOOL ret;
52         int hwm;
53
54         if (!id) return NT_STATUS_INVALID_PARAMETER;
55
56         /* Get current high water mark */
57         switch (id_type & ID_TYPEMASK) {
58                 case ID_USERID:
59                         if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
60                                 return NT_STATUS_INTERNAL_DB_ERROR;
61                         }
62
63                         /* check it is in the range */
64                         if (hwm > idmap_state.uid_high) {
65                                 DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %u)\n", idmap_state.uid_high));
66                                 return NT_STATUS_UNSUCCESSFUL;
67                         }
68
69                         /* fetch a new id and increment it */
70                         ret = tdb_change_uint32_atomic(idmap_tdb, HWM_USER, &hwm, 1);
71                         if (!ret) {
72                                 DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
73                                 return NT_STATUS_UNSUCCESSFUL;
74                         }
75
76                         /* recheck it is in the range */
77                         if (hwm > idmap_state.uid_high) {
78                                 DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %u)\n", idmap_state.uid_high));
79                                 return NT_STATUS_UNSUCCESSFUL;
80                         }
81                         
82                         (*id).uid = hwm;
83
84                         break;
85                 case ID_GROUPID:
86                         if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
87                                 return NT_STATUS_INTERNAL_DB_ERROR;
88                         }
89
90                         /* check it is in the range */
91                         if (hwm > idmap_state.gid_high) {
92                                 DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %u)\n", idmap_state.gid_high));
93                                 return NT_STATUS_UNSUCCESSFUL;
94                         }
95
96                         /* fetch a new id and increment it */
97                         ret = tdb_change_uint32_atomic(idmap_tdb, HWM_GROUP, &hwm, 1);
98
99                         if (!ret) {
100                                 DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
101                                 return NT_STATUS_UNSUCCESSFUL;
102                         }
103
104                         /* recheck it is in the range */
105                         if (hwm > idmap_state.gid_high) {
106                                 DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %u)\n", idmap_state.gid_high));
107                                 return NT_STATUS_UNSUCCESSFUL;
108                         }
109                         
110                         (*id).gid = hwm;
111                         
112                         break;
113                 default:
114                         return NT_STATUS_INVALID_PARAMETER;
115         }
116
117         return NT_STATUS_OK;
118 }
119
120 /* Get a sid from an id */
121 static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
122 {
123         TDB_DATA key, data;
124         fstring keystr;
125         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
126
127         if (!sid) return NT_STATUS_INVALID_PARAMETER;
128
129         switch (id_type & ID_TYPEMASK) {
130                 case ID_USERID:
131                         slprintf(keystr, sizeof(keystr), "UID %d", id.uid);
132                         break;
133                 case ID_GROUPID:
134                         slprintf(keystr, sizeof(keystr), "GID %d", id.gid);
135                         break;
136                 default:
137                         return NT_STATUS_UNSUCCESSFUL;
138         }
139
140         key.dptr = keystr;
141         key.dsize = strlen(keystr) + 1;
142
143         data = tdb_fetch(idmap_tdb, key);
144
145         if (data.dptr) {
146                 if (string_to_sid(sid, data.dptr)) {
147                         ret = NT_STATUS_OK;
148                 }
149                 SAFE_FREE(data.dptr);
150         }
151
152         return ret;
153 }
154
155 /* Get an id from a sid */
156 static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
157 {
158         TDB_DATA data, key;
159         fstring keystr;
160         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
161
162         if (!sid || !id || !id_type) return NT_STATUS_INVALID_PARAMETER;
163
164         /* Check if sid is present in database */
165         sid_to_string(keystr, sid);
166
167         key.dptr = keystr;
168         key.dsize = strlen(keystr) + 1;
169
170         data = tdb_fetch(idmap_tdb, key);
171
172         if (data.dptr) {
173                 int type = *id_type & ID_TYPEMASK;
174                 fstring scanstr;
175
176                 if (type == ID_EMPTY || type == ID_USERID) {
177                         /* Parse and return existing uid */
178                         fstrcpy(scanstr, "UID %d");
179
180                         if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
181                                 /* uid ok? */
182                                 if (type == ID_EMPTY) {
183                                         *id_type = ID_USERID;
184                                 }
185                                 ret = NT_STATUS_OK;
186                                 goto idok;
187                         }
188                 }
189
190                 if (type == ID_EMPTY || type == ID_GROUPID) {
191                         /* Parse and return existing gid */
192                         fstrcpy(scanstr, "GID %d");
193
194                         if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
195                                 /* gid ok? */
196                                 if (type == ID_EMPTY) {
197                                         *id_type = ID_GROUPID;
198                                 }
199                                 ret = NT_STATUS_OK;
200                         }
201                 }
202 idok:
203                 SAFE_FREE(data.dptr);
204
205         } else if (!(*id_type & ID_NOMAP) &&
206                    (((*id_type & ID_TYPEMASK) == ID_USERID)
207                     || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
208
209                 /* Allocate a new id for this sid */
210                 ret = db_allocate_id(id, *id_type);
211                 if (NT_STATUS_IS_OK(ret)) {
212                         fstring keystr2;
213
214                         /* Store new id */
215                         if (*id_type & ID_USERID) {
216                                 slprintf(keystr2, sizeof(keystr2), "UID %d", (*id).uid);
217                         } else {
218                                 slprintf(keystr2, sizeof(keystr2), "GID %d", (*id).gid);
219                         }
220
221                         data.dptr = keystr2;
222                         data.dsize = strlen(keystr2) + 1;
223
224                         if (tdb_store(idmap_tdb, key, data, TDB_REPLACE) == -1) {
225                                 /* TODO: print tdb error !! */
226                                 return NT_STATUS_UNSUCCESSFUL;
227                         }
228                         if (tdb_store(idmap_tdb, data, key, TDB_REPLACE) == -1) {
229                                 /* TODO: print tdb error !! */
230                                 return NT_STATUS_UNSUCCESSFUL;
231                         }
232
233                         ret = NT_STATUS_OK;
234                 }
235         }
236         
237         return ret;
238 }
239
240 static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
241 {
242         TDB_DATA ksid, kid, data;
243         fstring ksidstr;
244         fstring kidstr;
245
246         if (!sid) return NT_STATUS_INVALID_PARAMETER;
247
248         sid_to_string(ksidstr, sid);
249
250         ksid.dptr = ksidstr;
251         ksid.dsize = strlen(ksidstr) + 1;
252
253         if (id_type & ID_USERID) {
254                 slprintf(kidstr, sizeof(kidstr), "UID %d", id.uid);
255         } else if (id_type & ID_GROUPID) {
256                 slprintf(kidstr, sizeof(kidstr), "GID %d", id.gid);
257         } else {
258                 return NT_STATUS_INVALID_PARAMETER;
259         }
260
261         kid.dptr = kidstr;
262         kid.dsize = strlen(kidstr) + 1;
263
264         /* *DELETE* prevoius mappings if any.
265          * This is done both SID and [U|G]ID passed in */
266         
267         data = tdb_fetch(idmap_tdb, ksid);
268         if (data.dptr) {
269                 tdb_delete(idmap_tdb, data);
270                 tdb_delete(idmap_tdb, ksid);
271         }
272         data = tdb_fetch(idmap_tdb, kid);
273         if (data.dptr) {
274                 tdb_delete(idmap_tdb, data);
275                 tdb_delete(idmap_tdb, kid);
276         }
277
278         if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
279                 DEBUG(0, ("idb_set_mapping: tdb_store 1 error: %s\n", tdb_errorstr(idmap_tdb)));
280                 return NT_STATUS_UNSUCCESSFUL;
281         }
282         if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
283                 DEBUG(0, ("idb_set_mapping: tdb_store 2 error: %s\n", tdb_errorstr(idmap_tdb)));
284                 return NT_STATUS_UNSUCCESSFUL;
285         }
286         return NT_STATUS_OK;
287 }
288
289 /*****************************************************************************
290  Initialise idmap database. 
291 *****************************************************************************/
292 static NTSTATUS db_idmap_init( char *params )
293 {
294         SMB_STRUCT_STAT stbuf;
295         char *tdbfile = NULL;
296         int32 version;
297         BOOL tdb_is_new = False;
298
299         /* use the old database if present */
300         if (!file_exist(lock_path("idmap.tdb"), &stbuf)) {
301                 if (file_exist(lock_path("winbindd_idmap.tdb"), &stbuf)) {
302                         DEBUG(0, ("idmap_init: using winbindd_idmap.tdb file!\n"));
303                         tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
304                         if (!tdbfile) {
305                                 DEBUG(0, ("idmap_init: out of memory!\n"));
306                                 return NT_STATUS_NO_MEMORY;
307                         }
308                 } else {
309                         tdb_is_new = True;
310                 }
311         }
312         if (!tdbfile) {
313                 tdbfile = strdup(lock_path("idmap.tdb"));
314                 if (!tdbfile) {
315                         DEBUG(0, ("idmap_init: out of memory!\n"));
316                         return NT_STATUS_NO_MEMORY;
317                 }
318         }
319
320         /* Open tdb cache */
321         if (!(idmap_tdb = tdb_open_log(tdbfile, 0,
322                                        TDB_DEFAULT, O_RDWR | O_CREAT,
323                                        0600))) {
324                 DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
325                 SAFE_FREE(tdbfile);
326                 return NT_STATUS_UNSUCCESSFUL;
327         }
328
329         SAFE_FREE(tdbfile);
330
331         /* check against earlier versions */
332         if (tdb_is_new) {
333                 /* TODO: delete the file if this fail */
334                 tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION);
335         } else {
336                 version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
337                 if (version != IDMAP_VERSION) {
338                         DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n"));
339                         return NT_STATUS_INTERNAL_DB_ERROR;
340                 }
341         }
342
343         /* Create high water marks for group and user id */
344         if (!lp_idmap_uid(&idmap_state.uid_low, &idmap_state.uid_high)) {
345                 DEBUG(1, ("idmap uid range missing or invalid\n"));
346                 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
347         } else {
348                 if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
349                         if (tdb_store_int32(idmap_tdb, HWM_USER, idmap_state.uid_low) == -1) {
350                                 DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
351                                 return NT_STATUS_INTERNAL_DB_ERROR;
352                         }
353                 }
354         }
355
356         if (!lp_idmap_gid(&idmap_state.gid_low, &idmap_state.gid_high)) {
357                 DEBUG(1, ("idmap gid range missing or invalid\n"));
358                 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
359         } else {
360                 if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
361                         if (tdb_store_int32(idmap_tdb, HWM_GROUP, idmap_state.gid_low) == -1) {
362                                 DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
363                                 return NT_STATUS_INTERNAL_DB_ERROR;
364                         }
365                 }
366         }
367
368         return NT_STATUS_OK;
369 }
370
371 /* Close the tdb */
372 static NTSTATUS db_idmap_close(void)
373 {
374         if (idmap_tdb) {
375                 if (tdb_close(idmap_tdb) == 0) {
376                         return NT_STATUS_OK;
377                 } else {
378                         return NT_STATUS_UNSUCCESSFUL;
379                 }
380         }
381         return NT_STATUS_OK;
382 }
383
384
385 /* Dump status information to log file.  Display different stuff based on
386    the debug level:
387
388    Debug Level        Information Displayed
389    =================================================================
390    0                  Percentage of [ug]id range allocated
391    0                  High water marks (next allocated ids)
392 */
393
394 #define DUMP_INFO 0
395
396 static void db_idmap_status(void)
397 {
398         int user_hwm, group_hwm;
399
400         DEBUG(0, ("winbindd idmap status:\n"));
401
402         /* Get current high water marks */
403
404         if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
405                 DEBUG(DUMP_INFO,
406                       ("\tCould not get userid high water mark!\n"));
407         }
408
409         if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
410                 DEBUG(DUMP_INFO,
411                       ("\tCould not get groupid high water mark!\n"));
412         }
413
414         /* Display next ids to allocate */
415
416         if (user_hwm != -1) {
417                 DEBUG(DUMP_INFO,
418                       ("\tNext userid to allocate is %d\n", user_hwm));
419         }
420
421         if (group_hwm != -1) {
422                 DEBUG(DUMP_INFO,
423                       ("\tNext groupid to allocate is %d\n", group_hwm));
424         }
425
426         /* Display percentage of id range already allocated. */
427
428         if (user_hwm != -1) {
429                 int num_users = user_hwm - idmap_state.uid_low;
430                 int total_users =
431                     idmap_state.uid_high - idmap_state.uid_low;
432
433                 DEBUG(DUMP_INFO,
434                       ("\tUser id range is %d%% full (%d of %d)\n",
435                        num_users * 100 / total_users, num_users,
436                        total_users));
437         }
438
439         if (group_hwm != -1) {
440                 int num_groups = group_hwm - idmap_state.gid_low;
441                 int total_groups =
442                     idmap_state.gid_high - idmap_state.gid_low;
443
444                 DEBUG(DUMP_INFO,
445                       ("\tGroup id range is %d%% full (%d of %d)\n",
446                        num_groups * 100 / total_groups, num_groups,
447                        total_groups));
448         }
449
450         /* Display complete mapping of users and groups to rids */
451 }
452
453 static struct idmap_methods db_methods = {
454
455         db_idmap_init,
456         db_get_sid_from_id,
457         db_get_id_from_sid,
458         db_set_mapping,
459         db_idmap_close,
460         db_idmap_status
461
462 };
463
464 NTSTATUS idmap_tdb_init(void)
465 {
466         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
467 }