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