Line data Source code
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 : #include "lib/util/string_wrappers.h"
37 :
38 : #undef DBGC_CLASS
39 : #define DBGC_CLASS DBGC_IDMAP
40 :
41 : /* idmap version determines auto-conversion - this is the database
42 : structure version specifier. */
43 :
44 : #define IDMAP_VERSION 2
45 :
46 : /* High water mark keys */
47 : #define HWM_GROUP "GROUP HWM"
48 : #define HWM_USER "USER HWM"
49 :
50 : struct convert_fn_state {
51 : struct db_context *db;
52 : bool failed;
53 : };
54 :
55 : /*****************************************************************************
56 : For idmap conversion: convert one record to new format
57 : Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
58 : instead of the SID.
59 : *****************************************************************************/
60 0 : static int convert_fn(struct db_record *rec, void *private_data)
61 : {
62 0 : struct winbindd_domain *domain;
63 0 : char *p;
64 0 : NTSTATUS status;
65 0 : struct dom_sid sid;
66 0 : uint32_t rid;
67 0 : struct dom_sid_buf keystr;
68 0 : fstring dom_name;
69 0 : TDB_DATA key;
70 0 : TDB_DATA key2;
71 0 : TDB_DATA value;
72 0 : struct convert_fn_state *s = (struct convert_fn_state *)private_data;
73 :
74 0 : key = dbwrap_record_get_key(rec);
75 :
76 0 : DEBUG(10,("Converting %s\n", (const char *)key.dptr));
77 :
78 0 : p = strchr((const char *)key.dptr, '/');
79 0 : if (!p)
80 0 : return 0;
81 :
82 0 : *p = 0;
83 0 : fstrcpy(dom_name, (const char *)key.dptr);
84 0 : *p++ = '/';
85 :
86 0 : domain = find_domain_from_name(dom_name);
87 0 : if (domain == NULL) {
88 : /* We must delete the old record. */
89 0 : DEBUG(0,("Unable to find domain %s\n", dom_name ));
90 0 : DEBUG(0,("deleting record %s\n", (const char *)key.dptr ));
91 :
92 0 : status = dbwrap_record_delete(rec);
93 0 : if (!NT_STATUS_IS_OK(status)) {
94 0 : DEBUG(0, ("Unable to delete record %s:%s\n",
95 : (const char *)key.dptr,
96 : nt_errstr(status)));
97 0 : s->failed = true;
98 0 : return -1;
99 : }
100 :
101 0 : return 0;
102 : }
103 :
104 0 : rid = atoi(p);
105 :
106 0 : sid_compose(&sid, &domain->sid, rid);
107 :
108 0 : key2 = string_term_tdb_data(dom_sid_str_buf(&sid, &keystr));
109 :
110 0 : value = dbwrap_record_get_value(rec);
111 :
112 0 : status = dbwrap_store(s->db, key2, value, TDB_INSERT);
113 0 : if (!NT_STATUS_IS_OK(status)) {
114 0 : DEBUG(0,("Unable to add record %s:%s\n",
115 : (const char *)key2.dptr,
116 : nt_errstr(status)));
117 0 : s->failed = true;
118 0 : return -1;
119 : }
120 :
121 0 : status = dbwrap_store(s->db, value, key2, TDB_REPLACE);
122 0 : if (!NT_STATUS_IS_OK(status)) {
123 0 : DEBUG(0,("Unable to update record %s:%s\n",
124 : (const char *)value.dptr,
125 : nt_errstr(status)));
126 0 : s->failed = true;
127 0 : return -1;
128 : }
129 :
130 0 : status = dbwrap_record_delete(rec);
131 0 : if (!NT_STATUS_IS_OK(status)) {
132 0 : DEBUG(0,("Unable to delete record %s:%s\n",
133 : (const char *)key.dptr,
134 : nt_errstr(status)));
135 0 : s->failed = true;
136 0 : return -1;
137 : }
138 :
139 0 : return 0;
140 : }
141 :
142 : /*****************************************************************************
143 : Convert the idmap database from an older version.
144 : *****************************************************************************/
145 :
146 0 : static bool idmap_tdb_upgrade(struct idmap_domain *dom, struct db_context *db)
147 : {
148 0 : int32_t vers;
149 0 : struct convert_fn_state s;
150 0 : NTSTATUS status;
151 :
152 0 : status = dbwrap_fetch_int32_bystring(db, "IDMAP_VERSION", &vers);
153 0 : if (!NT_STATUS_IS_OK(status)) {
154 0 : vers = -1;
155 : }
156 :
157 0 : 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 0 : int32_t wm;
165 :
166 0 : status = dbwrap_fetch_int32_bystring(db, HWM_USER, &wm);
167 0 : if (!NT_STATUS_IS_OK(status)) {
168 0 : wm = -1;
169 : }
170 :
171 0 : if (wm != -1) {
172 0 : wm = IREV(wm);
173 : } else {
174 0 : wm = dom->low_id;
175 : }
176 :
177 0 : status = dbwrap_store_int32_bystring(db, HWM_USER, wm);
178 0 : if (!NT_STATUS_IS_OK(status)) {
179 0 : DEBUG(0, ("Unable to byteswap user hwm in idmap "
180 : "database: %s\n", nt_errstr(status)));
181 0 : return False;
182 : }
183 :
184 0 : status = dbwrap_fetch_int32_bystring(db, HWM_GROUP, &wm);
185 0 : if (!NT_STATUS_IS_OK(status)) {
186 0 : wm = -1;
187 : }
188 :
189 0 : if (wm != -1) {
190 0 : wm = IREV(wm);
191 : } else {
192 0 : wm = dom->low_id;
193 : }
194 :
195 0 : status = dbwrap_store_int32_bystring(db, HWM_GROUP, wm);
196 0 : if (!NT_STATUS_IS_OK(status)) {
197 0 : DEBUG(0, ("Unable to byteswap group hwm in idmap "
198 : "database: %s\n", nt_errstr(status)));
199 0 : return False;
200 : }
201 : }
202 :
203 0 : s.db = db;
204 0 : s.failed = false;
205 :
206 : /* the old format stored as DOMAIN/rid - now we store the SID direct */
207 0 : status = dbwrap_traverse(db, convert_fn, &s, NULL);
208 :
209 0 : if (!NT_STATUS_IS_OK(status)) {
210 0 : DEBUG(0, ("Database traverse failed during conversion\n"));
211 0 : return false;
212 : }
213 :
214 0 : if (s.failed) {
215 0 : DEBUG(0, ("Problem during conversion\n"));
216 0 : return False;
217 : }
218 :
219 0 : status = dbwrap_store_int32_bystring(db, "IDMAP_VERSION",
220 : IDMAP_VERSION);
221 0 : if (!NT_STATUS_IS_OK(status)) {
222 0 : DEBUG(0, ("Unable to store idmap version in database: %s\n",
223 : nt_errstr(status)));
224 0 : return False;
225 : }
226 :
227 0 : return True;
228 : }
229 :
230 0 : static NTSTATUS idmap_tdb_init_hwm(struct idmap_domain *dom)
231 : {
232 0 : uint32_t low_uid;
233 0 : uint32_t low_gid;
234 0 : bool update_uid = false;
235 0 : bool update_gid = false;
236 0 : struct idmap_tdb_common_context *ctx;
237 0 : NTSTATUS status;
238 :
239 0 : ctx = talloc_get_type(dom->private_data,
240 : struct idmap_tdb_common_context);
241 :
242 0 : status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_USER, &low_uid);
243 0 : if (!NT_STATUS_IS_OK(status) || low_uid < dom->low_id) {
244 0 : update_uid = true;
245 : }
246 :
247 0 : status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_GROUP, &low_gid);
248 0 : if (!NT_STATUS_IS_OK(status) || low_gid < dom->low_id) {
249 0 : update_gid = true;
250 : }
251 :
252 0 : if (!update_uid && !update_gid) {
253 0 : return NT_STATUS_OK;
254 : }
255 :
256 0 : if (dbwrap_transaction_start(ctx->db) != 0) {
257 0 : DEBUG(0, ("Unable to start upgrade transaction!\n"));
258 0 : return NT_STATUS_INTERNAL_DB_ERROR;
259 : }
260 :
261 0 : if (update_uid) {
262 0 : status = dbwrap_store_uint32_bystring(ctx->db, HWM_USER,
263 : dom->low_id);
264 0 : if (!NT_STATUS_IS_OK(status)) {
265 0 : dbwrap_transaction_cancel(ctx->db);
266 0 : DEBUG(0, ("Unable to initialise user hwm in idmap "
267 : "database: %s\n", nt_errstr(status)));
268 0 : return NT_STATUS_INTERNAL_DB_ERROR;
269 : }
270 : }
271 :
272 0 : if (update_gid) {
273 0 : status = dbwrap_store_uint32_bystring(ctx->db, HWM_GROUP,
274 : dom->low_id);
275 0 : if (!NT_STATUS_IS_OK(status)) {
276 0 : dbwrap_transaction_cancel(ctx->db);
277 0 : DEBUG(0, ("Unable to initialise group hwm in idmap "
278 : "database: %s\n", nt_errstr(status)));
279 0 : return NT_STATUS_INTERNAL_DB_ERROR;
280 : }
281 : }
282 :
283 0 : if (dbwrap_transaction_commit(ctx->db) != 0) {
284 0 : DEBUG(0, ("Unable to commit upgrade transaction!\n"));
285 0 : return NT_STATUS_INTERNAL_DB_ERROR;
286 : }
287 :
288 0 : return NT_STATUS_OK;
289 : }
290 :
291 0 : static NTSTATUS idmap_tdb_open_db(struct idmap_domain *dom)
292 : {
293 0 : NTSTATUS ret;
294 0 : TALLOC_CTX *mem_ctx;
295 0 : char *tdbfile = NULL;
296 0 : struct db_context *db = NULL;
297 0 : int32_t version;
298 0 : struct idmap_tdb_common_context *ctx;
299 :
300 0 : ctx = talloc_get_type(dom->private_data,
301 : struct idmap_tdb_common_context);
302 :
303 0 : if (ctx->db) {
304 : /* it is already open */
305 0 : return NT_STATUS_OK;
306 : }
307 :
308 : /* use our own context here */
309 0 : mem_ctx = talloc_stackframe();
310 :
311 : /* use the old database if present */
312 0 : tdbfile = state_path(talloc_tos(), "winbindd_idmap.tdb");
313 0 : if (!tdbfile) {
314 0 : DEBUG(0, ("Out of memory!\n"));
315 0 : ret = NT_STATUS_NO_MEMORY;
316 0 : goto done;
317 : }
318 :
319 0 : DEBUG(10,("Opening tdbfile %s\n", tdbfile ));
320 :
321 : /* Open idmap repository */
322 0 : db = db_open(mem_ctx, tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
323 : DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
324 0 : if (!db) {
325 0 : DEBUG(0, ("Unable to open idmap database\n"));
326 0 : ret = NT_STATUS_UNSUCCESSFUL;
327 0 : goto done;
328 : }
329 :
330 : /* check against earlier versions */
331 0 : ret = dbwrap_fetch_int32_bystring(db, "IDMAP_VERSION", &version);
332 0 : if (!NT_STATUS_IS_OK(ret)) {
333 0 : version = -1;
334 : }
335 :
336 0 : if (version != IDMAP_VERSION) {
337 0 : if (dbwrap_transaction_start(db) != 0) {
338 0 : DEBUG(0, ("Unable to start upgrade transaction!\n"));
339 0 : ret = NT_STATUS_INTERNAL_DB_ERROR;
340 0 : goto done;
341 : }
342 :
343 0 : if (!idmap_tdb_upgrade(dom, db)) {
344 0 : dbwrap_transaction_cancel(db);
345 0 : DEBUG(0, ("Unable to open idmap database, it's in an old format, and upgrade failed!\n"));
346 0 : ret = NT_STATUS_INTERNAL_DB_ERROR;
347 0 : goto done;
348 : }
349 :
350 0 : if (dbwrap_transaction_commit(db) != 0) {
351 0 : DEBUG(0, ("Unable to commit upgrade transaction!\n"));
352 0 : ret = NT_STATUS_INTERNAL_DB_ERROR;
353 0 : goto done;
354 : }
355 : }
356 :
357 0 : ctx->db = talloc_move(ctx, &db);
358 :
359 0 : ret = idmap_tdb_init_hwm(dom);
360 :
361 0 : done:
362 0 : talloc_free(mem_ctx);
363 0 : return ret;
364 : }
365 :
366 : /**********************************************************************
367 : IDMAP MAPPING TDB BACKEND
368 : **********************************************************************/
369 :
370 : /*****************************
371 : Initialise idmap database.
372 : *****************************/
373 :
374 0 : static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom)
375 : {
376 0 : NTSTATUS ret;
377 0 : struct idmap_tdb_common_context *ctx;
378 :
379 0 : DEBUG(10, ("idmap_tdb_db_init called for domain '%s'\n", dom->name));
380 :
381 0 : ctx = talloc_zero(dom, struct idmap_tdb_common_context);
382 0 : if ( ! ctx) {
383 0 : DEBUG(0, ("Out of memory!\n"));
384 0 : return NT_STATUS_NO_MEMORY;
385 : }
386 :
387 : /* load backend specific configuration here: */
388 : #if 0
389 : if (strequal(dom->name, "*")) {
390 : } else {
391 : }
392 : #endif
393 :
394 0 : ctx->rw_ops = talloc_zero(ctx, struct idmap_rw_ops);
395 0 : if (ctx->rw_ops == NULL) {
396 0 : DEBUG(0, ("Out of memory!\n"));
397 0 : ret = NT_STATUS_NO_MEMORY;
398 0 : goto failed;
399 : }
400 :
401 0 : ctx->max_id = dom->high_id;
402 0 : ctx->hwmkey_uid = HWM_USER;
403 0 : ctx->hwmkey_gid = HWM_GROUP;
404 :
405 0 : ctx->rw_ops->get_new_id = idmap_tdb_common_get_new_id;
406 0 : ctx->rw_ops->set_mapping = idmap_tdb_common_set_mapping;
407 :
408 0 : dom->private_data = ctx;
409 :
410 0 : ret = idmap_tdb_open_db(dom);
411 0 : if ( ! NT_STATUS_IS_OK(ret)) {
412 0 : goto failed;
413 : }
414 :
415 0 : return NT_STATUS_OK;
416 :
417 0 : failed:
418 0 : talloc_free(ctx);
419 0 : return ret;
420 : }
421 :
422 : static const struct idmap_methods db_methods = {
423 : .init = idmap_tdb_db_init,
424 : .unixids_to_sids = idmap_tdb_common_unixids_to_sids,
425 : .sids_to_unixids = idmap_tdb_common_sids_to_unixids,
426 : .allocate_id = idmap_tdb_common_get_new_id,
427 : };
428 :
429 0 : NTSTATUS idmap_tdb_init(TALLOC_CTX *mem_ctx)
430 : {
431 0 : DEBUG(10, ("calling idmap_tdb_init\n"));
432 :
433 0 : return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
434 : }
|