24ef11836e1107865b3aca579aa45f37f6d69a60
[amitay/samba.git] / source3 / winbindd / idmap_tdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    idmap TDB backend
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8    Copyright (C) Jeremy Allison 2006
9    Copyright (C) Simo Sorce 2003-2006
10    Copyright (C) Michael Adam 2009-2010
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "idmap.h"
30 #include "idmap_rw.h"
31 #include "idmap_tdb_common.h"
32 #include "dbwrap/dbwrap.h"
33 #include "dbwrap/dbwrap_open.h"
34 #include "../libcli/security/security.h"
35 #include "util_tdb.h"
36
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_IDMAP
39
40 /* idmap version determines auto-conversion - this is the database
41    structure version specifier. */
42
43 #define IDMAP_VERSION 2
44
45 /* High water mark keys */
46 #define HWM_GROUP  "GROUP HWM"
47 #define HWM_USER   "USER HWM"
48
49 struct convert_fn_state {
50         struct db_context *db;
51         bool failed;
52 };
53
54 /*****************************************************************************
55  For idmap conversion: convert one record to new format
56  Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
57  instead of the SID.
58 *****************************************************************************/
59 static int convert_fn(struct db_record *rec, void *private_data)
60 {
61         struct winbindd_domain *domain;
62         char *p;
63         NTSTATUS status;
64         struct dom_sid sid;
65         uint32_t rid;
66         fstring keystr;
67         fstring dom_name;
68         TDB_DATA key;
69         TDB_DATA key2;
70         TDB_DATA value;
71         struct convert_fn_state *s = (struct convert_fn_state *)private_data;
72
73         key = dbwrap_record_get_key(rec);
74
75         DEBUG(10,("Converting %s\n", (const char *)key.dptr));
76
77         p = strchr((const char *)key.dptr, '/');
78         if (!p)
79                 return 0;
80
81         *p = 0;
82         fstrcpy(dom_name, (const char *)key.dptr);
83         *p++ = '/';
84
85         domain = find_domain_from_name(dom_name);
86         if (domain == NULL) {
87                 /* We must delete the old record. */
88                 DEBUG(0,("Unable to find domain %s\n", dom_name ));
89                 DEBUG(0,("deleting record %s\n", (const char *)key.dptr ));
90
91                 status = dbwrap_record_delete(rec);
92                 if (!NT_STATUS_IS_OK(status)) {
93                         DEBUG(0, ("Unable to delete record %s:%s\n",
94                                 (const char *)key.dptr,
95                                 nt_errstr(status)));
96                         s->failed = true;
97                         return -1;
98                 }
99
100                 return 0;
101         }
102
103         rid = atoi(p);
104
105         sid_compose(&sid, &domain->sid, rid);
106
107         sid_to_fstring(keystr, &sid);
108         key2 = string_term_tdb_data(keystr);
109
110         value = dbwrap_record_get_value(rec);
111
112         status = dbwrap_store(s->db, key2, value, TDB_INSERT);
113         if (!NT_STATUS_IS_OK(status)) {
114                 DEBUG(0,("Unable to add record %s:%s\n",
115                         (const char *)key2.dptr,
116                         nt_errstr(status)));
117                 s->failed = true;
118                 return -1;
119         }
120
121         status = dbwrap_store(s->db, value, key2, TDB_REPLACE);
122         if (!NT_STATUS_IS_OK(status)) {
123                 DEBUG(0,("Unable to update record %s:%s\n",
124                         (const char *)value.dptr,
125                         nt_errstr(status)));
126                 s->failed = true;
127                 return -1;
128         }
129
130         status = dbwrap_record_delete(rec);
131         if (!NT_STATUS_IS_OK(status)) {
132                 DEBUG(0,("Unable to delete record %s:%s\n",
133                         (const char *)key.dptr,
134                         nt_errstr(status)));
135                 s->failed = true;
136                 return -1;
137         }
138
139         return 0;
140 }
141
142 /*****************************************************************************
143  Convert the idmap database from an older version.
144 *****************************************************************************/
145
146 static bool idmap_tdb_upgrade(struct idmap_domain *dom, struct db_context *db)
147 {
148         int32_t vers;
149         struct convert_fn_state s;
150         NTSTATUS status;
151
152         status = dbwrap_fetch_int32_bystring(db, "IDMAP_VERSION", &vers);
153         if (!NT_STATUS_IS_OK(status)) {
154                 vers = -1;
155         }
156
157         if (IREV(vers) == IDMAP_VERSION) {
158                 /* Arrggghh ! Bytereversed - make order independent ! */
159                 /*
160                  * high and low records were created on a
161                  * big endian machine and will need byte-reversing.
162                  */
163
164                 int32_t wm;
165
166                 status = dbwrap_fetch_int32_bystring(db, HWM_USER, &wm);
167                 if (!NT_STATUS_IS_OK(status)) {
168                         wm = -1;
169                 }
170
171                 if (wm != -1) {
172                         wm = IREV(wm);
173                 }  else {
174                         wm = dom->low_id;
175                 }
176
177                 status = dbwrap_store_int32_bystring(db, HWM_USER, wm);
178                 if (!NT_STATUS_IS_OK(status)) {
179                         DEBUG(0, ("Unable to byteswap user hwm in idmap "
180                                   "database: %s\n", nt_errstr(status)));
181                         return False;
182                 }
183
184                 status = dbwrap_fetch_int32_bystring(db, HWM_GROUP, &wm);
185                 if (!NT_STATUS_IS_OK(status)) {
186                         wm = -1;
187                 }
188
189                 if (wm != -1) {
190                         wm = IREV(wm);
191                 } else {
192                         wm = dom->low_id;
193                 }
194
195                 status = dbwrap_store_int32_bystring(db, HWM_GROUP, wm);
196                 if (!NT_STATUS_IS_OK(status)) {
197                         DEBUG(0, ("Unable to byteswap group hwm in idmap "
198                                   "database: %s\n", nt_errstr(status)));
199                         return False;
200                 }
201         }
202
203         s.db = db;
204         s.failed = false;
205
206         /* the old format stored as DOMAIN/rid - now we store the SID direct */
207         status = dbwrap_traverse(db, convert_fn, &s, NULL);
208
209         if (!NT_STATUS_IS_OK(status)) {
210                 DEBUG(0, ("Database traverse failed during conversion\n"));
211                 return false;
212         }
213
214         if (s.failed) {
215                 DEBUG(0, ("Problem during conversion\n"));
216                 return False;
217         }
218
219         status = dbwrap_store_int32_bystring(db, "IDMAP_VERSION",
220                                              IDMAP_VERSION);
221         if (!NT_STATUS_IS_OK(status)) {
222                 DEBUG(0, ("Unable to store idmap version in database: %s\n",
223                           nt_errstr(status)));
224                 return False;
225         }
226
227         return True;
228 }
229
230 static NTSTATUS idmap_tdb_init_hwm(struct idmap_domain *dom)
231 {
232         uint32_t low_uid;
233         uint32_t low_gid;
234         bool update_uid = false;
235         bool update_gid = false;
236         struct idmap_tdb_common_context *ctx;
237         NTSTATUS status;
238
239         ctx = talloc_get_type(dom->private_data,
240                               struct idmap_tdb_common_context);
241
242         status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_USER, &low_uid);
243         if (!NT_STATUS_IS_OK(status) || low_uid < dom->low_id) {
244                 update_uid = true;
245         }
246
247         status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_GROUP, &low_gid);
248         if (!NT_STATUS_IS_OK(status) || low_gid < dom->low_id) {
249                 update_gid = true;
250         }
251
252         if (!update_uid && !update_gid) {
253                 return NT_STATUS_OK;
254         }
255
256         if (dbwrap_transaction_start(ctx->db) != 0) {
257                 DEBUG(0, ("Unable to start upgrade transaction!\n"));
258                 return NT_STATUS_INTERNAL_DB_ERROR;
259         }
260
261         if (update_uid) {
262                 status = dbwrap_store_uint32_bystring(ctx->db, HWM_USER,
263                                                       dom->low_id);
264                 if (!NT_STATUS_IS_OK(status)) {
265                         dbwrap_transaction_cancel(ctx->db);
266                         DEBUG(0, ("Unable to initialise user hwm in idmap "
267                                   "database: %s\n", nt_errstr(status)));
268                         return NT_STATUS_INTERNAL_DB_ERROR;
269                 }
270         }
271
272         if (update_gid) {
273                 status = dbwrap_store_uint32_bystring(ctx->db, HWM_GROUP,
274                                                       dom->low_id);
275                 if (!NT_STATUS_IS_OK(status)) {
276                         dbwrap_transaction_cancel(ctx->db);
277                         DEBUG(0, ("Unable to initialise group hwm in idmap "
278                                   "database: %s\n", nt_errstr(status)));
279                         return NT_STATUS_INTERNAL_DB_ERROR;
280                 }
281         }
282
283         if (dbwrap_transaction_commit(ctx->db) != 0) {
284                 DEBUG(0, ("Unable to commit upgrade transaction!\n"));
285                 return NT_STATUS_INTERNAL_DB_ERROR;
286         }
287
288         return NT_STATUS_OK;
289 }
290
291 static NTSTATUS idmap_tdb_open_db(struct idmap_domain *dom)
292 {
293         NTSTATUS ret;
294         TALLOC_CTX *mem_ctx;
295         char *tdbfile = NULL;
296         struct db_context *db = NULL;
297         int32_t version;
298         bool config_error = false;
299         struct idmap_tdb_common_context *ctx;
300
301         ctx = talloc_get_type(dom->private_data,
302                               struct idmap_tdb_common_context);
303
304         if (ctx->db) {
305                 /* it is already open */
306                 return NT_STATUS_OK;
307         }
308
309         /* use our own context here */
310         mem_ctx = talloc_stackframe();
311
312         /* use the old database if present */
313         tdbfile = state_path("winbindd_idmap.tdb");
314         if (!tdbfile) {
315                 DEBUG(0, ("Out of memory!\n"));
316                 ret = NT_STATUS_NO_MEMORY;
317                 goto done;
318         }
319
320         DEBUG(10,("Opening tdbfile %s\n", tdbfile ));
321
322         /* Open idmap repository */
323         db = db_open(mem_ctx, tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
324                      DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
325         if (!db) {
326                 DEBUG(0, ("Unable to open idmap database\n"));
327                 ret = NT_STATUS_UNSUCCESSFUL;
328                 goto done;
329         }
330
331         /* check against earlier versions */
332         ret = dbwrap_fetch_int32_bystring(db, "IDMAP_VERSION", &version);
333         if (!NT_STATUS_IS_OK(ret)) {
334                 version = -1;
335         }
336
337         if (version != IDMAP_VERSION) {
338                 if (config_error) {
339                         DEBUG(0,("Upgrade of IDMAP_VERSION from %d to %d is not "
340                                  "possible with incomplete configuration\n",
341                                  version, IDMAP_VERSION));
342                         ret = NT_STATUS_UNSUCCESSFUL;
343                         goto done;
344                 }
345                 if (dbwrap_transaction_start(db) != 0) {
346                         DEBUG(0, ("Unable to start upgrade transaction!\n"));
347                         ret = NT_STATUS_INTERNAL_DB_ERROR;
348                         goto done;
349                 }
350
351                 if (!idmap_tdb_upgrade(dom, db)) {
352                         dbwrap_transaction_cancel(db);
353                         DEBUG(0, ("Unable to open idmap database, it's in an old format, and upgrade failed!\n"));
354                         ret = NT_STATUS_INTERNAL_DB_ERROR;
355                         goto done;
356                 }
357
358                 if (dbwrap_transaction_commit(db) != 0) {
359                         DEBUG(0, ("Unable to commit upgrade transaction!\n"));
360                         ret = NT_STATUS_INTERNAL_DB_ERROR;
361                         goto done;
362                 }
363         }
364
365         ctx->db = talloc_move(ctx, &db);
366
367         ret = idmap_tdb_init_hwm(dom);
368
369 done:
370         talloc_free(mem_ctx);
371         return ret;
372 }
373
374 /**********************************************************************
375  IDMAP MAPPING TDB BACKEND
376 **********************************************************************/
377
378 /*****************************
379  Initialise idmap database. 
380 *****************************/
381
382 static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom)
383 {
384         NTSTATUS ret;
385         struct idmap_tdb_common_context *ctx;
386
387         DEBUG(10, ("idmap_tdb_db_init called for domain '%s'\n", dom->name));
388
389         ctx = talloc_zero(dom, struct idmap_tdb_common_context);
390         if ( ! ctx) {
391                 DEBUG(0, ("Out of memory!\n"));
392                 return NT_STATUS_NO_MEMORY;
393         }
394
395         /* load backend specific configuration here: */
396 #if 0
397         if (strequal(dom->name, "*")) {
398         } else {
399         }
400 #endif
401
402         ctx->rw_ops = talloc_zero(ctx, struct idmap_rw_ops);
403         if (ctx->rw_ops == NULL) {
404                 DEBUG(0, ("Out of memory!\n"));
405                 ret = NT_STATUS_NO_MEMORY;
406                 goto failed;
407         }
408
409         ctx->max_id = dom->high_id;
410         ctx->hwmkey_uid = HWM_USER;
411         ctx->hwmkey_gid = HWM_GROUP;
412
413         ctx->rw_ops->get_new_id = idmap_tdb_common_get_new_id;
414         ctx->rw_ops->set_mapping = idmap_tdb_common_set_mapping;
415
416         dom->private_data = ctx;
417
418         ret = idmap_tdb_open_db(dom);
419         if ( ! NT_STATUS_IS_OK(ret)) {
420                 goto failed;
421         }
422
423         return NT_STATUS_OK;
424
425 failed:
426         talloc_free(ctx);
427         return ret;
428 }
429
430 static struct idmap_methods db_methods = {
431         .init = idmap_tdb_db_init,
432         .unixids_to_sids = idmap_tdb_common_unixids_to_sids,
433         .sids_to_unixids = idmap_tdb_common_sids_to_unixids,
434         .allocate_id = idmap_tdb_common_get_new_id,
435 };
436
437 NTSTATUS idmap_tdb_init(TALLOC_CTX *mem_ctx)
438 {
439         DEBUG(10, ("calling idmap_tdb_init\n"));
440
441         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
442 }