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