/src/samba/source3/smbd/smbXsrv_open.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | Copyright (C) Stefan Metzmacher 2012 |
5 | | Copyright (C) Michael Adam 2012 |
6 | | |
7 | | This program is free software; you can redistribute it and/or modify |
8 | | it under the terms of the GNU General Public License as published by |
9 | | the Free Software Foundation; either version 3 of the License, or |
10 | | (at your option) any later version. |
11 | | |
12 | | This program is distributed in the hope that it will be useful, |
13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | GNU General Public License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "includes.h" |
22 | | #include "system/filesys.h" |
23 | | #include "lib/util/server_id.h" |
24 | | #include "smbd/smbd.h" |
25 | | #include "smbd/globals.h" |
26 | | #include "smbXsrv_open.h" |
27 | | #include "dbwrap/dbwrap.h" |
28 | | #include "dbwrap/dbwrap_rbt.h" |
29 | | #include "dbwrap/dbwrap_open.h" |
30 | | #include "../libcli/security/security.h" |
31 | | #include "messages.h" |
32 | | #include "lib/util/util_tdb.h" |
33 | | #include "librpc/gen_ndr/ndr_smbXsrv.h" |
34 | | #include "serverid.h" |
35 | | #include "source3/include/util_tdb.h" |
36 | | #include "lib/util/idtree_random.h" |
37 | | #include "lib/util/time_basic.h" |
38 | | #include "../librpc/gen_ndr/ndr_smb2_lease_struct.h" |
39 | | |
40 | | struct smbXsrv_open_table { |
41 | | struct { |
42 | | struct idr_context *idr; |
43 | | uint32_t lowest_id; |
44 | | uint32_t highest_id; |
45 | | uint32_t max_opens; |
46 | | uint32_t num_opens; |
47 | | } local; |
48 | | struct { |
49 | | struct db_context *db_ctx; |
50 | | } global; |
51 | | }; |
52 | | |
53 | | static struct db_context *smbXsrv_open_global_db_ctx = NULL; |
54 | | |
55 | | NTSTATUS smbXsrv_open_global_init(void) |
56 | 0 | { |
57 | 0 | char *global_path = NULL; |
58 | 0 | struct db_context *db_ctx = NULL; |
59 | |
|
60 | 0 | if (smbXsrv_open_global_db_ctx != NULL) { |
61 | 0 | return NT_STATUS_OK; |
62 | 0 | } |
63 | | |
64 | 0 | global_path = lock_path(talloc_tos(), "smbXsrv_open_global.tdb"); |
65 | 0 | if (global_path == NULL) { |
66 | 0 | return NT_STATUS_NO_MEMORY; |
67 | 0 | } |
68 | | |
69 | 0 | db_ctx = db_open(NULL, global_path, |
70 | 0 | SMBD_VOLATILE_TDB_HASH_SIZE, |
71 | 0 | SMBD_VOLATILE_TDB_FLAGS, |
72 | 0 | O_RDWR | O_CREAT, 0600, |
73 | 0 | DBWRAP_LOCK_ORDER_1, |
74 | 0 | DBWRAP_FLAG_NONE); |
75 | 0 | TALLOC_FREE(global_path); |
76 | 0 | if (db_ctx == NULL) { |
77 | 0 | NTSTATUS status = map_nt_error_from_unix_common(errno); |
78 | 0 | return status; |
79 | 0 | } |
80 | | |
81 | 0 | smbXsrv_open_global_db_ctx = db_ctx; |
82 | |
|
83 | 0 | return NT_STATUS_OK; |
84 | 0 | } |
85 | | |
86 | | /* |
87 | | * NOTE: |
88 | | * We need to store the keys in big endian so that dbwrap_rbt's memcmp |
89 | | * has the same result as integer comparison between the uint32_t |
90 | | * values. |
91 | | * |
92 | | * TODO: implement string based key |
93 | | */ |
94 | | |
95 | | struct smbXsrv_open_global_key_buf { uint8_t buf[sizeof(uint32_t)]; }; |
96 | | |
97 | | static TDB_DATA smbXsrv_open_global_id_to_key( |
98 | | uint32_t id, struct smbXsrv_open_global_key_buf *key_buf) |
99 | 0 | { |
100 | 0 | RSIVAL(key_buf->buf, 0, id); |
101 | |
|
102 | 0 | return (TDB_DATA) { |
103 | 0 | .dptr = key_buf->buf, |
104 | 0 | .dsize = sizeof(key_buf->buf), |
105 | 0 | }; |
106 | 0 | } |
107 | | |
108 | | static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn, |
109 | | uint32_t lowest_id, |
110 | | uint32_t highest_id, |
111 | | uint32_t max_opens) |
112 | 0 | { |
113 | 0 | struct smbXsrv_client *client = conn->client; |
114 | 0 | struct smbXsrv_open_table *table; |
115 | 0 | NTSTATUS status; |
116 | 0 | uint64_t max_range; |
117 | |
|
118 | 0 | if (lowest_id > highest_id) { |
119 | 0 | return NT_STATUS_INTERNAL_ERROR; |
120 | 0 | } |
121 | | |
122 | 0 | max_range = highest_id; |
123 | 0 | max_range -= lowest_id; |
124 | 0 | max_range += 1; |
125 | |
|
126 | 0 | if (max_opens > max_range) { |
127 | 0 | return NT_STATUS_INTERNAL_ERROR; |
128 | 0 | } |
129 | | |
130 | 0 | table = talloc_zero(client, struct smbXsrv_open_table); |
131 | 0 | if (table == NULL) { |
132 | 0 | return NT_STATUS_NO_MEMORY; |
133 | 0 | } |
134 | | |
135 | 0 | table->local.idr = idr_init(table); |
136 | 0 | if (table->local.idr == NULL) { |
137 | 0 | TALLOC_FREE(table); |
138 | 0 | return NT_STATUS_NO_MEMORY; |
139 | 0 | } |
140 | 0 | table->local.lowest_id = lowest_id; |
141 | 0 | table->local.highest_id = highest_id; |
142 | 0 | table->local.max_opens = max_opens; |
143 | |
|
144 | 0 | status = smbXsrv_open_global_init(); |
145 | 0 | if (!NT_STATUS_IS_OK(status)) { |
146 | 0 | TALLOC_FREE(table); |
147 | 0 | return status; |
148 | 0 | } |
149 | | |
150 | 0 | table->global.db_ctx = smbXsrv_open_global_db_ctx; |
151 | |
|
152 | 0 | client->open_table = table; |
153 | 0 | return NT_STATUS_OK; |
154 | 0 | } |
155 | | |
156 | | static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table, |
157 | | uint32_t open_local_id, |
158 | | uint32_t open_global_id, |
159 | | NTTIME now, |
160 | | struct smbXsrv_open **_open) |
161 | 0 | { |
162 | 0 | struct smbXsrv_open *op = NULL; |
163 | |
|
164 | 0 | *_open = NULL; |
165 | |
|
166 | 0 | if (open_local_id == 0) { |
167 | 0 | return NT_STATUS_FILE_CLOSED; |
168 | 0 | } |
169 | | |
170 | 0 | if (table == NULL) { |
171 | | /* this might happen before the end of negprot */ |
172 | 0 | return NT_STATUS_FILE_CLOSED; |
173 | 0 | } |
174 | | |
175 | 0 | if (table->local.idr == NULL) { |
176 | 0 | return NT_STATUS_INTERNAL_ERROR; |
177 | 0 | } |
178 | | |
179 | 0 | op = idr_find(table->local.idr, open_local_id); |
180 | 0 | if (op == NULL) { |
181 | 0 | return NT_STATUS_FILE_CLOSED; |
182 | 0 | } |
183 | | |
184 | 0 | if (open_global_id == 0) { |
185 | | /* make the global check a no-op for SMB1 */ |
186 | 0 | open_global_id = op->global->open_global_id; |
187 | 0 | } |
188 | |
|
189 | 0 | if (op->global->open_global_id != open_global_id) { |
190 | 0 | return NT_STATUS_FILE_CLOSED; |
191 | 0 | } |
192 | | |
193 | 0 | if (now != 0) { |
194 | 0 | op->idle_time = now; |
195 | 0 | } |
196 | |
|
197 | 0 | *_open = op; |
198 | 0 | return NT_STATUS_OK; |
199 | 0 | } |
200 | | |
201 | | static NTSTATUS smbXsrv_open_global_parse_record( |
202 | | TALLOC_CTX *mem_ctx, |
203 | | TDB_DATA key, |
204 | | TDB_DATA val, |
205 | | struct smbXsrv_open_global0 **global) |
206 | 0 | { |
207 | 0 | DATA_BLOB blob = data_blob_const(val.dptr, val.dsize); |
208 | 0 | struct smbXsrv_open_globalB global_blob; |
209 | 0 | enum ndr_err_code ndr_err; |
210 | 0 | NTSTATUS status; |
211 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
212 | |
|
213 | 0 | ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob, |
214 | 0 | (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB); |
215 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
216 | 0 | DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:" |
217 | 0 | "key '%s' ndr_pull_struct_blob - %s\n", |
218 | 0 | tdb_data_dbg(key), |
219 | 0 | ndr_errstr(ndr_err))); |
220 | 0 | status = ndr_map_error2ntstatus(ndr_err); |
221 | 0 | goto done; |
222 | 0 | } |
223 | | |
224 | 0 | DBG_DEBUG("\n"); |
225 | 0 | if (CHECK_DEBUGLVL(10)) { |
226 | 0 | NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob); |
227 | 0 | } |
228 | |
|
229 | 0 | if (global_blob.version != SMBXSRV_VERSION_0) { |
230 | 0 | status = NT_STATUS_INTERNAL_DB_CORRUPTION; |
231 | 0 | DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:" |
232 | 0 | "key '%s' unsupported version - %d - %s\n", |
233 | 0 | tdb_data_dbg(key), |
234 | 0 | (int)global_blob.version, |
235 | 0 | nt_errstr(status))); |
236 | 0 | goto done; |
237 | 0 | } |
238 | | |
239 | 0 | if (global_blob.info.info0 == NULL) { |
240 | 0 | status = NT_STATUS_INTERNAL_DB_CORRUPTION; |
241 | 0 | DEBUG(1,("Invalid record in smbXsrv_open_global.tdb:" |
242 | 0 | "key '%s' info0 NULL pointer - %s\n", |
243 | 0 | tdb_data_dbg(key), |
244 | 0 | nt_errstr(status))); |
245 | 0 | goto done; |
246 | 0 | } |
247 | | |
248 | 0 | *global = talloc_move(mem_ctx, &global_blob.info.info0); |
249 | 0 | status = NT_STATUS_OK; |
250 | 0 | done: |
251 | 0 | talloc_free(frame); |
252 | 0 | return status; |
253 | 0 | } |
254 | | |
255 | | static NTSTATUS smbXsrv_open_global_verify_record( |
256 | | TDB_DATA key, |
257 | | TDB_DATA val, |
258 | | TALLOC_CTX *mem_ctx, |
259 | | struct smbXsrv_open_global0 **_global0) |
260 | 0 | { |
261 | 0 | struct smbXsrv_open_global0 *global0 = NULL; |
262 | 0 | struct server_id_buf buf; |
263 | 0 | NTSTATUS status; |
264 | |
|
265 | 0 | if (val.dsize == 0) { |
266 | 0 | return NT_STATUS_NOT_FOUND; |
267 | 0 | } |
268 | | |
269 | 0 | status = smbXsrv_open_global_parse_record(mem_ctx, key, val, &global0); |
270 | 0 | if (!NT_STATUS_IS_OK(status)) { |
271 | 0 | DBG_WARNING("smbXsrv_open_global_parse_record for %s failed: " |
272 | 0 | "%s\n", |
273 | 0 | tdb_data_dbg(key), |
274 | 0 | nt_errstr(status)); |
275 | 0 | return status; |
276 | 0 | } |
277 | 0 | *_global0 = global0; |
278 | |
|
279 | 0 | if (server_id_is_disconnected(&global0->server_id)) { |
280 | 0 | return NT_STATUS_REMOTE_DISCONNECT; |
281 | 0 | } |
282 | 0 | if (serverid_exists(&global0->server_id)) { |
283 | 0 | return NT_STATUS_OBJECTID_EXISTS; |
284 | 0 | } |
285 | | |
286 | 0 | DBG_WARNING("smbd %s did not clean up record %s\n", |
287 | 0 | server_id_str_buf(global0->server_id, &buf), |
288 | 0 | tdb_data_dbg(key)); |
289 | |
|
290 | 0 | return NT_STATUS_FATAL_APP_EXIT; |
291 | 0 | } |
292 | | |
293 | | struct smbXsrv_open_global_lookup_state { |
294 | | TALLOC_CTX *mem_ctx; |
295 | | struct smbXsrv_open_global0 *global; |
296 | | NTSTATUS status; |
297 | | }; |
298 | | |
299 | | static void smbXsrv_open_global_lookup_fn(struct db_record *rec, |
300 | | TDB_DATA val, |
301 | | void *private_data) |
302 | 0 | { |
303 | 0 | struct smbXsrv_open_global_lookup_state *state = private_data; |
304 | 0 | TDB_DATA key = dbwrap_record_get_key(rec); |
305 | |
|
306 | 0 | if (val.dsize == 0) { |
307 | | /* Likely a ctdb tombstone record */ |
308 | 0 | state->status = NT_STATUS_NOT_FOUND; |
309 | 0 | return; |
310 | 0 | } |
311 | | |
312 | 0 | state->status = smbXsrv_open_global_parse_record(state->mem_ctx, |
313 | 0 | key, |
314 | 0 | val, |
315 | 0 | &state->global); |
316 | 0 | } |
317 | | |
318 | | static NTSTATUS smbXsrv_open_global_lookup( |
319 | | struct smbXsrv_open_table *table, |
320 | | TALLOC_CTX *mem_ctx, |
321 | | uint32_t open_global_id, |
322 | | const struct smbXsrv_open_global0 **_global) |
323 | 0 | { |
324 | 0 | struct smbXsrv_open_global_key_buf key_buf; |
325 | 0 | TDB_DATA key = smbXsrv_open_global_id_to_key(open_global_id, &key_buf); |
326 | 0 | struct smbXsrv_open_global_lookup_state state = { |
327 | 0 | .mem_ctx = mem_ctx, |
328 | 0 | }; |
329 | 0 | NTSTATUS status; |
330 | |
|
331 | 0 | status = dbwrap_do_locked(table->global.db_ctx, |
332 | 0 | key, |
333 | 0 | smbXsrv_open_global_lookup_fn, |
334 | 0 | &state); |
335 | 0 | if (!NT_STATUS_IS_OK(status)) { |
336 | 0 | DBG_ERR("dbwrap_do_locked failed\n"); |
337 | 0 | return status; |
338 | 0 | } |
339 | 0 | if (NT_STATUS_EQUAL(state.status, NT_STATUS_NOT_FOUND)) { |
340 | 0 | DBG_DEBUG("smbXsrv_open record not found\n"); |
341 | 0 | return state.status; |
342 | 0 | } |
343 | 0 | if (!NT_STATUS_IS_OK(state.status)) { |
344 | 0 | DBG_ERR("smbXsrv_open_global_lookup_fn failed\n"); |
345 | 0 | return state.status; |
346 | 0 | } |
347 | 0 | *_global = state.global; |
348 | 0 | return NT_STATUS_OK; |
349 | 0 | } |
350 | | |
351 | | static NTSTATUS smbXsrv_open_global_store( |
352 | | struct db_record *rec, |
353 | | TDB_DATA key, |
354 | | TDB_DATA oldval, |
355 | | struct smbXsrv_open_global0 *global) |
356 | 0 | { |
357 | 0 | struct smbXsrv_open_globalB global_blob; |
358 | 0 | DATA_BLOB blob = data_blob_null; |
359 | 0 | TDB_DATA val = { .dptr = NULL, }; |
360 | 0 | NTSTATUS status; |
361 | 0 | enum ndr_err_code ndr_err; |
362 | | |
363 | | /* |
364 | | * TODO: if we use other versions than '0' |
365 | | * we would add glue code here, that would be able to |
366 | | * store the information in the old format. |
367 | | */ |
368 | |
|
369 | 0 | global_blob = (struct smbXsrv_open_globalB) { |
370 | 0 | .version = smbXsrv_version_global_current(), |
371 | 0 | }; |
372 | |
|
373 | 0 | if (oldval.dsize >= 8) { |
374 | 0 | global_blob.seqnum = IVAL(oldval.dptr, 4); |
375 | 0 | } |
376 | 0 | global_blob.seqnum += 1; |
377 | 0 | global_blob.info.info0 = global; |
378 | |
|
379 | 0 | ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &global_blob, |
380 | 0 | (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_globalB); |
381 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
382 | 0 | DBG_WARNING("key '%s' ndr_push - %s\n", |
383 | 0 | tdb_data_dbg(key), |
384 | 0 | ndr_map_error2string(ndr_err)); |
385 | 0 | return ndr_map_error2ntstatus(ndr_err); |
386 | 0 | } |
387 | | |
388 | 0 | val = make_tdb_data(blob.data, blob.length); |
389 | 0 | status = dbwrap_record_store(rec, val, TDB_REPLACE); |
390 | 0 | TALLOC_FREE(blob.data); |
391 | 0 | if (!NT_STATUS_IS_OK(status)) { |
392 | 0 | DBG_WARNING("key '%s' store - %s\n", |
393 | 0 | tdb_data_dbg(key), |
394 | 0 | nt_errstr(status)); |
395 | 0 | return status; |
396 | 0 | } |
397 | | |
398 | 0 | if (CHECK_DEBUGLVL(10)) { |
399 | 0 | DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key)); |
400 | 0 | NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob); |
401 | 0 | } |
402 | |
|
403 | 0 | return NT_STATUS_OK; |
404 | 0 | } |
405 | | |
406 | | struct smbXsrv_open_global_allocate_state { |
407 | | uint32_t id; |
408 | | struct smbXsrv_open_global0 *global; |
409 | | NTSTATUS status; |
410 | | }; |
411 | | |
412 | | static void smbXsrv_open_global_allocate_fn( |
413 | | struct db_record *rec, TDB_DATA oldval, void *private_data) |
414 | 0 | { |
415 | 0 | struct smbXsrv_open_global_allocate_state *state = private_data; |
416 | 0 | struct smbXsrv_open_global0 *global = state->global; |
417 | 0 | struct smbXsrv_open_global0 *tmp_global0 = NULL; |
418 | 0 | TDB_DATA key = dbwrap_record_get_key(rec); |
419 | |
|
420 | 0 | state->status = smbXsrv_open_global_verify_record( |
421 | 0 | key, oldval, talloc_tos(), &tmp_global0); |
422 | |
|
423 | 0 | if (!NT_STATUS_EQUAL(state->status, NT_STATUS_NOT_FOUND)) { |
424 | | /* |
425 | | * Found an existing record |
426 | | */ |
427 | 0 | TALLOC_FREE(tmp_global0); |
428 | 0 | state->status = NT_STATUS_RETRY; |
429 | 0 | return; |
430 | 0 | } |
431 | | |
432 | 0 | if (NT_STATUS_EQUAL(state->status, NT_STATUS_NOT_FOUND)) { |
433 | | /* |
434 | | * Found an empty slot |
435 | | */ |
436 | 0 | global->open_global_id = state->id; |
437 | 0 | global->open_persistent_id = state->id; |
438 | |
|
439 | 0 | state->status = smbXsrv_open_global_store( |
440 | 0 | rec, key, (TDB_DATA) { .dsize = 0, }, state->global); |
441 | 0 | if (!NT_STATUS_IS_OK(state->status)) { |
442 | 0 | DBG_WARNING("smbXsrv_open_global_store() for " |
443 | 0 | "id %"PRIu32" failed: %s\n", |
444 | 0 | state->id, |
445 | 0 | nt_errstr(state->status)); |
446 | 0 | } |
447 | 0 | return; |
448 | 0 | } |
449 | | |
450 | 0 | if (NT_STATUS_EQUAL(state->status, NT_STATUS_FATAL_APP_EXIT)) { |
451 | 0 | NTSTATUS status; |
452 | |
|
453 | 0 | TALLOC_FREE(tmp_global0); |
454 | | |
455 | | /* |
456 | | * smbd crashed |
457 | | */ |
458 | 0 | status = dbwrap_record_delete(rec); |
459 | 0 | if (!NT_STATUS_IS_OK(status)) { |
460 | 0 | DBG_WARNING("dbwrap_record_delete() failed " |
461 | 0 | "for record %"PRIu32": %s\n", |
462 | 0 | state->id, |
463 | 0 | nt_errstr(status)); |
464 | 0 | state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; |
465 | 0 | return; |
466 | 0 | } |
467 | 0 | return; |
468 | 0 | } |
469 | 0 | } |
470 | | |
471 | | static NTSTATUS smbXsrv_open_global_allocate( |
472 | | struct db_context *db, struct smbXsrv_open_global0 *global) |
473 | 0 | { |
474 | 0 | struct smbXsrv_open_global_allocate_state state = { |
475 | 0 | .global = global, |
476 | 0 | }; |
477 | 0 | uint32_t i; |
478 | 0 | uint32_t last_free = 0; |
479 | 0 | const uint32_t min_tries = 3; |
480 | | |
481 | | /* |
482 | | * Here we just randomly try the whole 32-bit space |
483 | | * |
484 | | * We use just 32-bit, because we want to reuse the |
485 | | * ID for SRVSVC. |
486 | | */ |
487 | 0 | for (i = 0; i < UINT32_MAX; i++) { |
488 | 0 | struct smbXsrv_open_global_key_buf key_buf; |
489 | 0 | TDB_DATA key; |
490 | 0 | NTSTATUS status; |
491 | |
|
492 | 0 | if (i >= min_tries && last_free != 0) { |
493 | 0 | state.id = last_free; |
494 | 0 | } else { |
495 | 0 | generate_nonce_buffer( |
496 | 0 | (uint8_t *)&state.id, sizeof(state.id)); |
497 | 0 | state.id = MAX(state.id, 1); |
498 | 0 | state.id = MIN(state.id, UINT32_MAX-1); |
499 | 0 | } |
500 | |
|
501 | 0 | key = smbXsrv_open_global_id_to_key(state.id, &key_buf); |
502 | |
|
503 | 0 | status = dbwrap_do_locked( |
504 | 0 | db, key, smbXsrv_open_global_allocate_fn, &state); |
505 | |
|
506 | 0 | if (!NT_STATUS_IS_OK(status)) { |
507 | 0 | DBG_WARNING("dbwrap_do_locked() failed: %s\n", |
508 | 0 | nt_errstr(status)); |
509 | 0 | return NT_STATUS_INTERNAL_DB_ERROR; |
510 | 0 | } |
511 | | |
512 | 0 | if (NT_STATUS_IS_OK(state.status)) { |
513 | | /* |
514 | | * Found an empty slot, done. |
515 | | */ |
516 | 0 | DBG_DEBUG("Found slot %"PRIu32"\n", state.id); |
517 | 0 | return NT_STATUS_OK; |
518 | 0 | } |
519 | | |
520 | 0 | if (NT_STATUS_EQUAL(state.status, NT_STATUS_FATAL_APP_EXIT)) { |
521 | |
|
522 | 0 | if ((i < min_tries) && (last_free == 0)) { |
523 | | /* |
524 | | * Remember "id" as free but also try |
525 | | * others to not recycle ids too |
526 | | * quickly. |
527 | | */ |
528 | 0 | last_free = state.id; |
529 | 0 | } |
530 | 0 | continue; |
531 | 0 | } |
532 | | |
533 | 0 | if (NT_STATUS_EQUAL(state.status, NT_STATUS_RETRY)) { |
534 | | /* |
535 | | * Normal collision, try next |
536 | | */ |
537 | 0 | DBG_DEBUG("Found record for id %"PRIu32"\n", |
538 | 0 | state.id); |
539 | 0 | continue; |
540 | 0 | } |
541 | | |
542 | 0 | DBG_WARNING("smbXsrv_open_global_allocate_fn() failed: %s\n", |
543 | 0 | nt_errstr(state.status)); |
544 | 0 | return state.status; |
545 | 0 | } |
546 | | |
547 | | /* should not be reached */ |
548 | 0 | return NT_STATUS_INTERNAL_ERROR; |
549 | 0 | } |
550 | | |
551 | | static int smbXsrv_open_destructor(struct smbXsrv_open *op) |
552 | 0 | { |
553 | 0 | NTSTATUS status; |
554 | |
|
555 | 0 | status = smbXsrv_open_close(op, 0); |
556 | 0 | if (!NT_STATUS_IS_OK(status)) { |
557 | 0 | DEBUG(0, ("smbXsrv_open_destructor: " |
558 | 0 | "smbXsrv_open_close() failed - %s\n", |
559 | 0 | nt_errstr(status))); |
560 | 0 | } |
561 | |
|
562 | 0 | TALLOC_FREE(op->global); |
563 | |
|
564 | 0 | return 0; |
565 | 0 | } |
566 | | |
567 | | NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn, |
568 | | struct smbXsrv_session *session, |
569 | | struct smbXsrv_tcon *tcon, |
570 | | NTTIME now, |
571 | | struct smbXsrv_open **_open) |
572 | 0 | { |
573 | 0 | struct smbXsrv_open_table *table = conn->client->open_table; |
574 | 0 | struct auth_session_info *session_info = NULL; |
575 | 0 | struct smbXsrv_open *op = NULL; |
576 | 0 | struct smbXsrv_open_global0 *global = NULL; |
577 | 0 | NTSTATUS status; |
578 | 0 | struct dom_sid *current_sid = NULL; |
579 | 0 | struct security_token *current_token = NULL; |
580 | 0 | int local_id; |
581 | |
|
582 | 0 | session_info = session->global->auth_session_info; |
583 | 0 | if (session_info == NULL) { |
584 | 0 | return NT_STATUS_INVALID_HANDLE; |
585 | 0 | } |
586 | 0 | current_token = session_info->security_token; |
587 | |
|
588 | 0 | if ((current_token == NULL) || |
589 | 0 | (current_token->num_sids <= PRIMARY_USER_SID_INDEX)) { |
590 | 0 | return NT_STATUS_INVALID_HANDLE; |
591 | 0 | } |
592 | 0 | current_sid = ¤t_token->sids[PRIMARY_USER_SID_INDEX]; |
593 | |
|
594 | 0 | if (table->local.num_opens >= table->local.max_opens) { |
595 | 0 | return NT_STATUS_INSUFFICIENT_RESOURCES; |
596 | 0 | } |
597 | | |
598 | 0 | op = talloc_zero(table, struct smbXsrv_open); |
599 | 0 | if (op == NULL) { |
600 | 0 | return NT_STATUS_NO_MEMORY; |
601 | 0 | } |
602 | 0 | op->table = table; |
603 | 0 | op->status = NT_STATUS_OK; /* TODO: start with INTERNAL_ERROR */ |
604 | 0 | op->idle_time = now; |
605 | 0 | op->session = session; |
606 | 0 | op->tcon = tcon; |
607 | |
|
608 | 0 | global = talloc_zero(op, struct smbXsrv_open_global0); |
609 | 0 | if (global == NULL) { |
610 | 0 | TALLOC_FREE(op); |
611 | 0 | return NT_STATUS_NO_MEMORY; |
612 | 0 | } |
613 | 0 | op->global = global; |
614 | | |
615 | | /* |
616 | | * We mark every slot as invalid using 0xFF. |
617 | | * Valid values are masked with 0xF. |
618 | | */ |
619 | 0 | memset(global->lock_sequence_array, 0xFF, |
620 | 0 | sizeof(global->lock_sequence_array)); |
621 | |
|
622 | 0 | local_id = idr_get_new_random( |
623 | 0 | table->local.idr, |
624 | 0 | op, |
625 | 0 | table->local.lowest_id, |
626 | 0 | table->local.highest_id); |
627 | 0 | if (local_id == -1) { |
628 | 0 | TALLOC_FREE(op); |
629 | 0 | return NT_STATUS_INSUFFICIENT_RESOURCES; |
630 | 0 | } |
631 | 0 | op->local_id = local_id; |
632 | |
|
633 | 0 | global->open_volatile_id = op->local_id; |
634 | |
|
635 | 0 | global->server_id = messaging_server_id(conn->client->msg_ctx); |
636 | 0 | global->open_time = now; |
637 | 0 | global->open_owner = *current_sid; |
638 | 0 | global->session_global_id = session->global->session_global_id; |
639 | 0 | global->tcon_global_id = tcon->global->tcon_global_id; |
640 | 0 | if (conn->protocol >= PROTOCOL_SMB2_10) { |
641 | 0 | global->client_guid = conn->smb2.client.guid; |
642 | 0 | } |
643 | |
|
644 | 0 | status = smbXsrv_open_global_allocate(table->global.db_ctx, |
645 | 0 | global); |
646 | 0 | if (!NT_STATUS_IS_OK(status)) { |
647 | 0 | int ret = idr_remove(table->local.idr, local_id); |
648 | 0 | SMB_ASSERT(ret == 0); |
649 | | |
650 | 0 | DBG_WARNING("smbXsrv_open_global_allocate() failed: %s\n", |
651 | 0 | nt_errstr(status)); |
652 | 0 | TALLOC_FREE(op); |
653 | 0 | return status; |
654 | 0 | } |
655 | | |
656 | 0 | table->local.num_opens += 1; |
657 | 0 | talloc_set_destructor(op, smbXsrv_open_destructor); |
658 | |
|
659 | 0 | if (CHECK_DEBUGLVL(10)) { |
660 | 0 | struct smbXsrv_openB open_blob = { |
661 | 0 | .version = SMBXSRV_VERSION_0, |
662 | 0 | .info.info0 = op, |
663 | 0 | }; |
664 | |
|
665 | 0 | DEBUG(10,("smbXsrv_open_create: global_id (0x%08x) stored\n", |
666 | 0 | op->global->open_global_id)); |
667 | 0 | NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob); |
668 | 0 | } |
669 | |
|
670 | 0 | *_open = op; |
671 | 0 | return NT_STATUS_OK; |
672 | 0 | } |
673 | | |
674 | | struct smbXsrv_open_replay_cache_key_buf { |
675 | | uint8_t buf[SMBXSRV_OPEN_REPLAY_CACHE_KEY_FIXED_SIZE]; |
676 | | }; |
677 | | |
678 | | static TDB_DATA smbXsrv_open_replay_cache_key( |
679 | | const struct GUID *client_guid, |
680 | | const struct GUID *create_guid, |
681 | | struct smbXsrv_open_replay_cache_key_buf *key_buf) |
682 | 0 | { |
683 | 0 | struct smbXsrv_open_replay_cache_key key = { |
684 | 0 | .client_guid = *client_guid, |
685 | 0 | .create_guid = *create_guid, |
686 | 0 | }; |
687 | 0 | DATA_BLOB blob = { |
688 | 0 | .data = key_buf->buf, |
689 | 0 | .length = sizeof(key_buf->buf) |
690 | 0 | }; |
691 | 0 | enum ndr_err_code ndr_err; |
692 | 0 | NTSTATUS status; |
693 | |
|
694 | 0 | ndr_err = ndr_push_struct_into_fixed_blob(&blob, &key, |
695 | 0 | (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache_key); |
696 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
697 | 0 | status = ndr_map_error2ntstatus(ndr_err); |
698 | 0 | DBG_ERR("ndr_push_struct_into_fixed_blob failed: %s\n", |
699 | 0 | nt_errstr(status)); |
700 | 0 | smb_panic("ndr_push_struct_into_fixed_blob failed\n"); |
701 | 0 | return tdb_null; |
702 | 0 | } |
703 | | |
704 | 0 | if (CHECK_DEBUGLVL(10)) { |
705 | 0 | NDR_PRINT_DEBUG(smbXsrv_open_replay_cache_key, &key); |
706 | 0 | } |
707 | |
|
708 | 0 | return make_tdb_data(blob.data, blob.length); |
709 | 0 | } |
710 | | |
711 | | static NTSTATUS smbXsrv_open_set_replay_cache(struct smbXsrv_open *op) |
712 | 0 | { |
713 | 0 | struct GUID_txt_buf buf; |
714 | 0 | struct db_context *db = op->table->global.db_ctx; |
715 | 0 | struct smbXsrv_open_global_key_buf open_key_buf; |
716 | 0 | NTSTATUS status; |
717 | 0 | struct smbXsrv_open_replay_cache_key_buf key_buf; |
718 | 0 | TDB_DATA key; |
719 | 0 | TDB_DATA val; |
720 | |
|
721 | 0 | if (!(op->flags & SMBXSRV_OPEN_NEED_REPLAY_CACHE)) { |
722 | 0 | return NT_STATUS_OK; |
723 | 0 | } |
724 | | |
725 | 0 | if (op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE) { |
726 | 0 | return NT_STATUS_OK; |
727 | 0 | } |
728 | | |
729 | 0 | key = smbXsrv_open_replay_cache_key(&op->global->client_guid, |
730 | 0 | &op->global->create_guid, |
731 | 0 | &key_buf); |
732 | |
|
733 | 0 | val = smbXsrv_open_global_id_to_key(op->global->open_global_id, |
734 | 0 | &open_key_buf); |
735 | |
|
736 | 0 | DBG_DEBUG("Replay Cache: store create_guid [%s]\n", |
737 | 0 | GUID_buf_string(&op->global->create_guid, &buf)); |
738 | |
|
739 | 0 | status = dbwrap_store(db, key, val, TDB_REPLACE); |
740 | |
|
741 | 0 | if (NT_STATUS_IS_OK(status)) { |
742 | 0 | op->flags |= SMBXSRV_OPEN_HAVE_REPLAY_CACHE; |
743 | 0 | op->flags &= ~SMBXSRV_OPEN_NEED_REPLAY_CACHE; |
744 | 0 | } |
745 | |
|
746 | 0 | return status; |
747 | 0 | } |
748 | | |
749 | | NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client, |
750 | | const struct GUID *create_guid) |
751 | 0 | { |
752 | 0 | struct GUID_txt_buf buf; |
753 | 0 | struct smbXsrv_open_replay_cache_key_buf key_buf; |
754 | 0 | TDB_DATA key; |
755 | 0 | NTSTATUS status; |
756 | |
|
757 | 0 | DBG_DEBUG("Replay Cache: purge create_guid [%s]\n", |
758 | 0 | GUID_buf_string(create_guid, &buf)); |
759 | |
|
760 | 0 | if (client->open_table == NULL) { |
761 | 0 | return NT_STATUS_OK; |
762 | 0 | } |
763 | | |
764 | 0 | key = smbXsrv_open_replay_cache_key(&client->global->client_guid, |
765 | 0 | create_guid, |
766 | 0 | &key_buf); |
767 | |
|
768 | 0 | status = dbwrap_purge(client->open_table->global.db_ctx, key); |
769 | 0 | return status; |
770 | 0 | } |
771 | | |
772 | | static NTSTATUS smbXsrv_open_clear_replay_cache(struct smbXsrv_open *op) |
773 | 0 | { |
774 | 0 | struct GUID *create_guid; |
775 | 0 | struct GUID_txt_buf buf; |
776 | 0 | struct smbXsrv_open_replay_cache_key_buf key_buf; |
777 | 0 | TDB_DATA key; |
778 | 0 | struct db_context *db; |
779 | 0 | NTSTATUS status; |
780 | |
|
781 | 0 | if (op->table == NULL) { |
782 | 0 | return NT_STATUS_OK; |
783 | 0 | } |
784 | | |
785 | 0 | db = op->table->global.db_ctx; |
786 | |
|
787 | 0 | if (!(op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE)) { |
788 | 0 | return NT_STATUS_OK; |
789 | 0 | } |
790 | | |
791 | 0 | create_guid = &op->global->create_guid; |
792 | 0 | if (GUID_all_zero(create_guid)) { |
793 | 0 | return NT_STATUS_OK; |
794 | 0 | } |
795 | | |
796 | 0 | DBG_DEBUG("Replay Cache: clear create_guid [%s]\n", |
797 | 0 | GUID_buf_string(create_guid, &buf)); |
798 | |
|
799 | 0 | key = smbXsrv_open_replay_cache_key(&op->global->client_guid, |
800 | 0 | create_guid, |
801 | 0 | &key_buf); |
802 | |
|
803 | 0 | status = dbwrap_purge(db, key); |
804 | |
|
805 | 0 | if (NT_STATUS_IS_OK(status)) { |
806 | 0 | op->flags &= ~SMBXSRV_OPEN_HAVE_REPLAY_CACHE; |
807 | 0 | } |
808 | |
|
809 | 0 | return status; |
810 | 0 | } |
811 | | |
812 | | struct smbXsrv_open_update_state { |
813 | | struct smbXsrv_open_global0 *global; |
814 | | NTSTATUS status; |
815 | | }; |
816 | | |
817 | | static void smbXsrv_open_update_fn( |
818 | | struct db_record *rec, TDB_DATA oldval, void *private_data) |
819 | 0 | { |
820 | 0 | struct smbXsrv_open_update_state *state = private_data; |
821 | 0 | TDB_DATA key = dbwrap_record_get_key(rec); |
822 | |
|
823 | 0 | state->status = smbXsrv_open_global_store( |
824 | 0 | rec, key, oldval, state->global); |
825 | 0 | } |
826 | | |
827 | | NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op) |
828 | 0 | { |
829 | 0 | struct smbXsrv_open_update_state state = { .global = op->global, }; |
830 | 0 | struct smbXsrv_open_table *table = op->table; |
831 | 0 | struct smbXsrv_open_global_key_buf key_buf; |
832 | 0 | TDB_DATA key = smbXsrv_open_global_id_to_key( |
833 | 0 | op->global->open_global_id, &key_buf); |
834 | 0 | NTSTATUS status; |
835 | |
|
836 | 0 | status = dbwrap_do_locked( |
837 | 0 | table->global.db_ctx, key, smbXsrv_open_update_fn, &state); |
838 | 0 | if (!NT_STATUS_IS_OK(status)) { |
839 | 0 | DBG_WARNING("global_id (0x%08x) dbwrap_do_locked failed: %s\n", |
840 | 0 | op->global->open_global_id, |
841 | 0 | nt_errstr(status)); |
842 | 0 | return NT_STATUS_INTERNAL_DB_ERROR; |
843 | 0 | } |
844 | | |
845 | 0 | if (!NT_STATUS_IS_OK(state.status)) { |
846 | 0 | DBG_WARNING("global_id (0x%08x) smbXsrv_open_global_store " |
847 | 0 | "failed: %s\n", |
848 | 0 | op->global->open_global_id, |
849 | 0 | nt_errstr(state.status)); |
850 | 0 | return state.status; |
851 | 0 | } |
852 | | |
853 | 0 | status = smbXsrv_open_set_replay_cache(op); |
854 | 0 | if (!NT_STATUS_IS_OK(status)) { |
855 | 0 | DBG_ERR("smbXsrv_open_set_replay_cache failed: %s\n", |
856 | 0 | nt_errstr(status)); |
857 | 0 | return status; |
858 | 0 | } |
859 | | |
860 | 0 | if (CHECK_DEBUGLVL(10)) { |
861 | 0 | struct smbXsrv_openB open_blob = { |
862 | 0 | .version = SMBXSRV_VERSION_0, |
863 | 0 | .info.info0 = op, |
864 | 0 | }; |
865 | |
|
866 | 0 | DEBUG(10,("smbXsrv_open_update: global_id (0x%08x) stored\n", |
867 | 0 | op->global->open_global_id)); |
868 | 0 | NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob); |
869 | 0 | } |
870 | |
|
871 | 0 | return NT_STATUS_OK; |
872 | 0 | } |
873 | | |
874 | | struct smbXsrv_open_close_state { |
875 | | struct smbXsrv_open *op; |
876 | | NTSTATUS status; |
877 | | }; |
878 | | |
879 | | static void smbXsrv_open_close_fn( |
880 | | struct db_record *rec, TDB_DATA oldval, void *private_data) |
881 | 0 | { |
882 | 0 | struct smbXsrv_open_close_state *state = private_data; |
883 | 0 | struct smbXsrv_open_global0 *global = state->op->global; |
884 | 0 | TDB_DATA key = dbwrap_record_get_key(rec); |
885 | |
|
886 | 0 | if (global->durable) { |
887 | | /* |
888 | | * Durable open -- we need to update the global part |
889 | | * instead of deleting it |
890 | | */ |
891 | 0 | state->status = smbXsrv_open_global_store( |
892 | 0 | rec, key, oldval, global); |
893 | 0 | if (!NT_STATUS_IS_OK(state->status)) { |
894 | 0 | DBG_WARNING("failed to store global key '%s': %s\n", |
895 | 0 | tdb_data_dbg(key), |
896 | 0 | nt_errstr(state->status)); |
897 | 0 | return; |
898 | 0 | } |
899 | | |
900 | 0 | if (CHECK_DEBUGLVL(10)) { |
901 | 0 | struct smbXsrv_openB open_blob = { |
902 | 0 | .version = SMBXSRV_VERSION_0, |
903 | 0 | .info.info0 = state->op, |
904 | 0 | }; |
905 | |
|
906 | 0 | DBG_DEBUG("(0x%08x) stored disconnect\n", |
907 | 0 | global->open_global_id); |
908 | 0 | NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob); |
909 | 0 | } |
910 | 0 | return; |
911 | 0 | } |
912 | | |
913 | 0 | state->status = dbwrap_record_delete(rec); |
914 | 0 | if (!NT_STATUS_IS_OK(state->status)) { |
915 | 0 | DBG_WARNING("failed to delete global key '%s': %s\n", |
916 | 0 | tdb_data_dbg(key), |
917 | 0 | nt_errstr(state->status)); |
918 | 0 | } |
919 | 0 | } |
920 | | |
921 | | NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now) |
922 | 0 | { |
923 | 0 | struct smbXsrv_open_close_state state = { .op = op, }; |
924 | 0 | struct smbXsrv_open_global0 *global = op->global; |
925 | 0 | struct smbXsrv_open_table *table; |
926 | 0 | NTSTATUS status; |
927 | 0 | NTSTATUS error = NT_STATUS_OK; |
928 | 0 | struct smbXsrv_open_global_key_buf key_buf; |
929 | 0 | TDB_DATA key = smbXsrv_open_global_id_to_key( |
930 | 0 | global->open_global_id, &key_buf); |
931 | 0 | int ret; |
932 | |
|
933 | 0 | if (op->table == NULL) { |
934 | 0 | return error; |
935 | 0 | } |
936 | | |
937 | 0 | table = op->table; |
938 | 0 | op->table = NULL; |
939 | |
|
940 | 0 | op->status = NT_STATUS_FILE_CLOSED; |
941 | 0 | global->disconnect_time = now; |
942 | 0 | global->session_global_id = 0; |
943 | 0 | global->tcon_global_id = 0; |
944 | 0 | server_id_set_disconnected(&global->server_id); |
945 | |
|
946 | 0 | status = dbwrap_do_locked( |
947 | 0 | table->global.db_ctx, key, smbXsrv_open_close_fn, &state); |
948 | 0 | if (!NT_STATUS_IS_OK(status)) { |
949 | 0 | DBG_WARNING("dbwrap_do_locked() for %s failed: %s\n", |
950 | 0 | tdb_data_dbg(key), |
951 | 0 | nt_errstr(status)); |
952 | 0 | error = status; |
953 | 0 | } else if (!NT_STATUS_IS_OK(state.status)) { |
954 | 0 | DBG_WARNING("smbXsrv_open_close_fn() for %s failed: %s\n", |
955 | 0 | tdb_data_dbg(key), |
956 | 0 | nt_errstr(state.status)); |
957 | 0 | error = state.status; |
958 | 0 | } |
959 | |
|
960 | 0 | if (!op->global->durable) { |
961 | | /* |
962 | | * If this is not a Durable Handle, remove the Replay-Cache entry. |
963 | | */ |
964 | 0 | error = smbXsrv_open_clear_replay_cache(op); |
965 | 0 | if (!NT_STATUS_IS_OK(error)) { |
966 | 0 | DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n", |
967 | 0 | nt_errstr(error)); |
968 | 0 | } |
969 | 0 | } |
970 | |
|
971 | 0 | ret = idr_remove(table->local.idr, op->local_id); |
972 | 0 | SMB_ASSERT(ret == 0); |
973 | | |
974 | 0 | table->local.num_opens -= 1; |
975 | |
|
976 | 0 | if (op->compat) { |
977 | 0 | op->compat->op = NULL; |
978 | 0 | file_free(NULL, op->compat); |
979 | 0 | op->compat = NULL; |
980 | 0 | } |
981 | |
|
982 | 0 | return error; |
983 | 0 | } |
984 | | |
985 | | NTSTATUS smb1srv_open_table_init(struct smbXsrv_connection *conn) |
986 | 0 | { |
987 | 0 | uint32_t max_opens; |
988 | | |
989 | | /* |
990 | | * Allow a range from 1..65534. |
991 | | * |
992 | | * With real_max_open_files possible ids, |
993 | | * truncated to the SMB1 limit of 16-bit. |
994 | | * |
995 | | * 0 and 0xFFFF are no valid ids. |
996 | | */ |
997 | 0 | max_opens = conn->client->sconn->real_max_open_files; |
998 | 0 | max_opens = MIN(max_opens, UINT16_MAX - 1); |
999 | |
|
1000 | 0 | return smbXsrv_open_table_init(conn, 1, UINT16_MAX - 1, max_opens); |
1001 | 0 | } |
1002 | | |
1003 | | NTSTATUS smb1srv_open_lookup(struct smbXsrv_connection *conn, |
1004 | | uint16_t fnum, NTTIME now, |
1005 | | struct smbXsrv_open **_open) |
1006 | 0 | { |
1007 | 0 | struct smbXsrv_open_table *table = conn->client->open_table; |
1008 | 0 | uint32_t local_id = fnum; |
1009 | 0 | uint32_t global_id = 0; |
1010 | |
|
1011 | 0 | return smbXsrv_open_local_lookup(table, local_id, global_id, now, _open); |
1012 | 0 | } |
1013 | | |
1014 | | NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn) |
1015 | 0 | { |
1016 | 0 | uint32_t max_opens; |
1017 | 0 | uint32_t highest_id; |
1018 | | |
1019 | | /* |
1020 | | * Allow a range from 1..4294967294. |
1021 | | * |
1022 | | * With real_max_open_files possible ids, |
1023 | | * truncated to 16-bit (the same as SMB1 for now). |
1024 | | * |
1025 | | * 0 and 0xFFFFFFFF are no valid ids. |
1026 | | * |
1027 | | * The usage of conn->sconn->real_max_open_files |
1028 | | * is the reason that we use one open table per |
1029 | | * transport connection (as we still have a 1:1 mapping |
1030 | | * between process and transport connection). |
1031 | | */ |
1032 | 0 | max_opens = conn->client->sconn->real_max_open_files; |
1033 | 0 | max_opens = MIN(max_opens, UINT16_MAX - 1); |
1034 | | |
1035 | | /* |
1036 | | * idtree uses "int" for local IDs. Limit the maximum ID to |
1037 | | * what "int" can hold. |
1038 | | */ |
1039 | 0 | highest_id = UINT32_MAX-1; |
1040 | 0 | highest_id = MIN(highest_id, INT_MAX); |
1041 | |
|
1042 | 0 | return smbXsrv_open_table_init(conn, 1, highest_id, max_opens); |
1043 | 0 | } |
1044 | | |
1045 | | NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn, |
1046 | | uint64_t persistent_id, |
1047 | | uint64_t volatile_id, |
1048 | | NTTIME now, |
1049 | | struct smbXsrv_open **_open) |
1050 | 0 | { |
1051 | 0 | struct smbXsrv_open_table *table = conn->client->open_table; |
1052 | 0 | uint32_t local_id = volatile_id & UINT32_MAX; |
1053 | 0 | uint64_t local_zeros = volatile_id & 0xFFFFFFFF00000000LLU; |
1054 | 0 | uint32_t global_id = persistent_id & UINT32_MAX; |
1055 | 0 | uint64_t global_zeros = persistent_id & 0xFFFFFFFF00000000LLU; |
1056 | 0 | NTSTATUS status; |
1057 | |
|
1058 | 0 | if (local_zeros != 0) { |
1059 | 0 | return NT_STATUS_FILE_CLOSED; |
1060 | 0 | } |
1061 | | |
1062 | 0 | if (global_zeros != 0) { |
1063 | 0 | return NT_STATUS_FILE_CLOSED; |
1064 | 0 | } |
1065 | | |
1066 | 0 | if (global_id == 0) { |
1067 | 0 | return NT_STATUS_FILE_CLOSED; |
1068 | 0 | } |
1069 | | |
1070 | 0 | status = smbXsrv_open_local_lookup(table, local_id, global_id, now, |
1071 | 0 | _open); |
1072 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1073 | 0 | return status; |
1074 | 0 | } |
1075 | | |
1076 | | /* |
1077 | | * Clear the replay cache for this create_guid if it exists: |
1078 | | * This is based on the assumption that this lookup will be |
1079 | | * triggered by a client request using the file-id for lookup. |
1080 | | * Hence the client has proven that it has in fact seen the |
1081 | | * reply to its initial create call. So subsequent create replays |
1082 | | * should be treated as invalid. Hence the index for create_guid |
1083 | | * lookup needs to be removed. |
1084 | | */ |
1085 | 0 | status = smbXsrv_open_clear_replay_cache(*_open); |
1086 | |
|
1087 | 0 | return status; |
1088 | 0 | } |
1089 | | |
1090 | | /* |
1091 | | * This checks or marks the replay cache, we have the following |
1092 | | * cases: |
1093 | | * |
1094 | | * 1. There is no record in the cache |
1095 | | * => We return STATUS_FWP_RESERVED in order to indicate |
1096 | | * that the caller holds the current reservation |
1097 | | * This is the "normal" CREATE processing |
1098 | | * |
1099 | | * 2. There is a record in the cache and open_persistent_id is 0 |
1100 | | * => We return NT_STATUS_FILE_NOT_AVAILABLE to indicate |
1101 | | * the original request is still pending |
1102 | | * This is the CREATE pending (still being processed) case |
1103 | | * |
1104 | | * 3. There is a record in the cache with a valid open_persistent_id: |
1105 | | * => We lookup the existing global open by open_persistent_id |
1106 | | * => We lookup a matching local open and return NT_STATUS_OK together |
1107 | | * with the smbXsrv_open if one is found |
1108 | | * This is the successful CREATE-replay case in the same process |
1109 | | * => Otherwise we return NT_STATUS_HANDLE_NO_LONGER_VALID together with |
1110 | | * open_persistent_id to trigger a handle reconnect |
1111 | | * This is the successful CREATE-replay case in a different process |
1112 | | * |
1113 | | * With NT_STATUS_OK the caller can continue the replay processing. |
1114 | | * |
1115 | | * With STATUS_FWP_RESERVED the caller should continue the normal |
1116 | | * open processing: |
1117 | | * - On success: |
1118 | | * - smbXsrv_open_update()/smbXsrv_open_set_replay_cache() |
1119 | | * will update the record with a valid open_persistent_id. |
1120 | | * - On failure: |
1121 | | * - smbXsrv_open_purge_replay_cache() should cleanup |
1122 | | * the reservation. |
1123 | | * |
1124 | | * All other values should be returned to the client, |
1125 | | * while NT_STATUS_FILE_NOT_AVAILABLE will trigger the |
1126 | | * retry loop on the client. |
1127 | | */ |
1128 | | NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn, |
1129 | | struct smbXsrv_session *session, |
1130 | | struct GUID create_guid, |
1131 | | const char *name, |
1132 | | NTTIME now, |
1133 | | uint64_t *persistent_id, |
1134 | | struct smbXsrv_open **_open) |
1135 | 0 | { |
1136 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1137 | 0 | NTSTATUS status; |
1138 | 0 | struct smbXsrv_open_table *table = conn->client->open_table; |
1139 | 0 | struct db_context *db = table->global.db_ctx; |
1140 | 0 | struct GUID_txt_buf _create_guid_buf; |
1141 | 0 | const char *create_guid_str = GUID_buf_string(&create_guid, &_create_guid_buf); |
1142 | 0 | struct db_record *db_rec = NULL; |
1143 | 0 | const struct smbXsrv_open_global0 *global = NULL; |
1144 | 0 | struct smbXsrv_open *op = NULL; |
1145 | 0 | uint32_t open_global_id; |
1146 | 0 | struct smbXsrv_open_replay_cache_key_buf key_buf; |
1147 | 0 | TDB_DATA key; |
1148 | 0 | TDB_DATA val; |
1149 | |
|
1150 | 0 | *_open = NULL; |
1151 | |
|
1152 | 0 | key = smbXsrv_open_replay_cache_key(&conn->client->global->client_guid, |
1153 | 0 | &create_guid, |
1154 | 0 | &key_buf); |
1155 | |
|
1156 | 0 | db_rec = dbwrap_fetch_locked(db, frame, key); |
1157 | 0 | if (db_rec == NULL) { |
1158 | 0 | TALLOC_FREE(frame); |
1159 | 0 | return NT_STATUS_INTERNAL_DB_ERROR; |
1160 | 0 | } |
1161 | | |
1162 | 0 | val = dbwrap_record_get_value(db_rec); |
1163 | 0 | if (val.dsize == 0) { |
1164 | 0 | struct smbXsrv_open_global_key_buf open_key_buf; |
1165 | |
|
1166 | 0 | DBG_DEBUG("Fresh replay-cache record\n"); |
1167 | |
|
1168 | 0 | val = smbXsrv_open_global_id_to_key(0, &open_key_buf); |
1169 | 0 | status = dbwrap_record_store(db_rec, val, TDB_REPLACE); |
1170 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1171 | 0 | TALLOC_FREE(frame); |
1172 | 0 | return status; |
1173 | 0 | } |
1174 | | |
1175 | | /* |
1176 | | * We're the new holder |
1177 | | */ |
1178 | 0 | *_open = NULL; |
1179 | 0 | TALLOC_FREE(frame); |
1180 | 0 | return NT_STATUS_FWP_RESERVED; |
1181 | 0 | } |
1182 | | |
1183 | | /* |
1184 | | * For now we expect the size of the record containing the global |
1185 | | * key to be the size of an uint32_t. If we ever change this, this |
1186 | | * will needs revisiting. |
1187 | | */ |
1188 | 0 | if (val.dsize != sizeof(uint32_t)) { |
1189 | 0 | TALLOC_FREE(frame); |
1190 | 0 | return NT_STATUS_INTERNAL_DB_CORRUPTION; |
1191 | 0 | } |
1192 | 0 | open_global_id = PULL_BE_U32(val.dptr, 0); |
1193 | |
|
1194 | 0 | DBG_DEBUG("Found replay cache record open_global_id [%"PRIx32"]\n", |
1195 | 0 | open_global_id); |
1196 | |
|
1197 | 0 | if (open_global_id == 0) { |
1198 | | /* |
1199 | | * The original request (or a former replay) is still |
1200 | | * pending, ask the client to retry by sending |
1201 | | * STATUS_FILE_NOT_AVAILABLE. |
1202 | | */ |
1203 | 0 | DBG_DEBUG("Pending create [%s] [%s]\n", create_guid_str, name); |
1204 | 0 | TALLOC_FREE(frame); |
1205 | 0 | return NT_STATUS_FILE_NOT_AVAILABLE; |
1206 | 0 | } |
1207 | | |
1208 | 0 | TALLOC_FREE(db_rec); |
1209 | 0 | status = smbXsrv_open_global_lookup(table, |
1210 | 0 | frame, |
1211 | 0 | open_global_id, |
1212 | 0 | &global); |
1213 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1214 | 0 | DBG_DEBUG("Global open not found for create " |
1215 | 0 | "[0x%"PRIx32"] [%s] [%s]\n", |
1216 | 0 | open_global_id, create_guid_str, name); |
1217 | 0 | TALLOC_FREE(frame); |
1218 | 0 | return status; |
1219 | 0 | } |
1220 | | |
1221 | 0 | if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) { |
1222 | 0 | NDR_PRINT_DEBUG(smbXsrv_open_global0, global); |
1223 | 0 | } |
1224 | |
|
1225 | 0 | status = smbXsrv_open_local_lookup(table, |
1226 | 0 | global->open_volatile_id, |
1227 | 0 | global->open_persistent_id, |
1228 | 0 | now, |
1229 | 0 | &op); |
1230 | 0 | if (NT_STATUS_IS_OK(status)) { |
1231 | 0 | if (op->session->global->session_global_id != |
1232 | 0 | session->global->session_global_id) |
1233 | 0 | { |
1234 | 0 | TALLOC_FREE(frame); |
1235 | 0 | return NT_STATUS_DUPLICATE_OBJECTID; |
1236 | 0 | } |
1237 | 0 | DBG_DEBUG("Found local open\n"); |
1238 | | /* |
1239 | | * We found an open the caller can reuse. |
1240 | | */ |
1241 | 0 | SMB_ASSERT(op != NULL); |
1242 | 0 | *_open = op; |
1243 | 0 | TALLOC_FREE(frame); |
1244 | 0 | return NT_STATUS_OK; |
1245 | 0 | } |
1246 | | |
1247 | 0 | DBG_DEBUG("No local open, triggering reconnect\n"); |
1248 | 0 | *persistent_id = open_global_id; |
1249 | 0 | TALLOC_FREE(frame); |
1250 | 0 | return NT_STATUS_HANDLE_NO_LONGER_VALID; |
1251 | 0 | } |
1252 | | |
1253 | | struct smb2srv_open_recreate_state { |
1254 | | struct smbXsrv_session *session; |
1255 | | struct smbXsrv_tcon *tcon; |
1256 | | struct smbXsrv_open *op; |
1257 | | const struct GUID *client_guid; |
1258 | | const struct GUID *create_guid; |
1259 | | const struct smb2_lease_key *lease_key; |
1260 | | struct security_token *current_token; |
1261 | | struct server_id me; |
1262 | | |
1263 | | NTSTATUS status; |
1264 | | }; |
1265 | | |
1266 | | static void smb2srv_open_recreate_fn( |
1267 | | struct db_record *rec, TDB_DATA oldval, void *private_data) |
1268 | 0 | { |
1269 | 0 | struct smb2srv_open_recreate_state *state = private_data; |
1270 | 0 | TDB_DATA key = dbwrap_record_get_key(rec); |
1271 | 0 | struct smbXsrv_open_global0 *global = NULL; |
1272 | 0 | struct GUID_txt_buf buf1, buf2; |
1273 | |
|
1274 | 0 | state->status = smbXsrv_open_global_verify_record( |
1275 | 0 | key, oldval, state->op, &state->op->global); |
1276 | 0 | if (!NT_STATUS_EQUAL(state->status, NT_STATUS_REMOTE_DISCONNECT)) { |
1277 | 0 | DBG_WARNING("smbXsrv_open_global_verify_record for %s " |
1278 | 0 | "failed: %s\n", |
1279 | 0 | tdb_data_dbg(key), |
1280 | 0 | nt_errstr(state->status)); |
1281 | 0 | goto not_found; |
1282 | 0 | } |
1283 | 0 | global = state->op->global; |
1284 | |
|
1285 | 0 | if (state->lease_key != NULL && |
1286 | 0 | !GUID_equal(&global->client_guid, state->client_guid)) |
1287 | 0 | { |
1288 | 0 | DBG_NOTICE("client guid: %s != %s in %s\n", |
1289 | 0 | GUID_buf_string(&global->client_guid, &buf1), |
1290 | 0 | GUID_buf_string(state->client_guid, &buf2), |
1291 | 0 | tdb_data_dbg(key)); |
1292 | 0 | goto not_found; |
1293 | 0 | } |
1294 | | |
1295 | | /* |
1296 | | * If the provided create_guid is NULL, this means that |
1297 | | * the reconnect request was a v1 request. In that case |
1298 | | * we should skip the create GUID verification, since |
1299 | | * it is valid to v1-reconnect a v2-opened handle. |
1300 | | */ |
1301 | 0 | if ((state->create_guid != NULL) && |
1302 | 0 | !GUID_equal(&global->create_guid, state->create_guid)) { |
1303 | 0 | DBG_NOTICE("%s != %s in %s\n", |
1304 | 0 | GUID_buf_string(&global->create_guid, &buf1), |
1305 | 0 | GUID_buf_string(state->create_guid, &buf2), |
1306 | 0 | tdb_data_dbg(key)); |
1307 | 0 | goto not_found; |
1308 | 0 | } |
1309 | | |
1310 | 0 | if (!security_token_is_sid( |
1311 | 0 | state->current_token, &global->open_owner)) { |
1312 | 0 | struct dom_sid_buf buf; |
1313 | 0 | DBG_NOTICE("global owner %s not in our token in %s\n", |
1314 | 0 | dom_sid_str_buf(&global->open_owner, &buf), |
1315 | 0 | tdb_data_dbg(key)); |
1316 | 0 | state->status = NT_STATUS_ACCESS_DENIED; |
1317 | 0 | return; |
1318 | 0 | } |
1319 | | |
1320 | 0 | if (!global->durable) { |
1321 | 0 | DBG_NOTICE("%"PRIu64"/%"PRIu64" not durable in %s\n", |
1322 | 0 | global->open_persistent_id, |
1323 | 0 | global->open_volatile_id, |
1324 | 0 | tdb_data_dbg(key)); |
1325 | 0 | goto not_found; |
1326 | 0 | } |
1327 | | |
1328 | 0 | global->open_volatile_id = state->op->local_id; |
1329 | 0 | global->server_id = state->me; |
1330 | 0 | global->session_global_id = state->session->global->session_global_id; |
1331 | 0 | global->tcon_global_id = state->tcon->global->tcon_global_id; |
1332 | |
|
1333 | 0 | state->status = smbXsrv_open_global_store(rec, key, oldval, global); |
1334 | 0 | if (!NT_STATUS_IS_OK(state->status)) { |
1335 | 0 | DBG_WARNING("smbXsrv_open_global_store for %s failed: %s\n", |
1336 | 0 | tdb_data_dbg(key), |
1337 | 0 | nt_errstr(state->status)); |
1338 | 0 | return; |
1339 | 0 | } |
1340 | 0 | return; |
1341 | | |
1342 | 0 | not_found: |
1343 | 0 | state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; |
1344 | 0 | } |
1345 | | |
1346 | | NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn, |
1347 | | struct smbXsrv_session *session, |
1348 | | struct smbXsrv_tcon *tcon, |
1349 | | uint64_t persistent_id, |
1350 | | const struct GUID *create_guid, |
1351 | | const struct smb2_lease_key *lease_key, |
1352 | | NTTIME now, |
1353 | | struct smbXsrv_open **_open) |
1354 | 0 | { |
1355 | 0 | struct smbXsrv_open_table *table = conn->client->open_table; |
1356 | 0 | struct smb2srv_open_recreate_state state = { |
1357 | 0 | .session = session, |
1358 | 0 | .tcon = tcon, |
1359 | 0 | .client_guid = &conn->client->global->client_guid, |
1360 | 0 | .create_guid = create_guid, |
1361 | 0 | .lease_key = lease_key, |
1362 | 0 | .me = messaging_server_id(conn->client->msg_ctx), |
1363 | 0 | }; |
1364 | 0 | struct auth_session_info *session_info = |
1365 | 0 | session->global->auth_session_info; |
1366 | 0 | struct smbXsrv_open_global_key_buf key_buf; |
1367 | 0 | TDB_DATA key = smbXsrv_open_global_id_to_key( |
1368 | 0 | persistent_id & UINT32_MAX, &key_buf); |
1369 | 0 | int ret, local_id; |
1370 | 0 | NTSTATUS status; |
1371 | |
|
1372 | 0 | if (session_info == NULL) { |
1373 | 0 | DEBUG(10, ("session_info=NULL\n")); |
1374 | 0 | return NT_STATUS_INVALID_HANDLE; |
1375 | 0 | } |
1376 | 0 | state.current_token = session_info->security_token; |
1377 | |
|
1378 | 0 | if (state.current_token == NULL) { |
1379 | 0 | DEBUG(10, ("current_token=NULL\n")); |
1380 | 0 | return NT_STATUS_INVALID_HANDLE; |
1381 | 0 | } |
1382 | | |
1383 | 0 | if ((persistent_id & 0xFFFFFFFF00000000LLU) != 0) { |
1384 | | /* |
1385 | | * We only use 32 bit for the persistent ID |
1386 | | */ |
1387 | 0 | DBG_DEBUG("persistent_id=%"PRIx64"\n", persistent_id); |
1388 | 0 | return NT_STATUS_OBJECT_NAME_NOT_FOUND; |
1389 | 0 | } |
1390 | | |
1391 | 0 | if (table->local.num_opens >= table->local.max_opens) { |
1392 | 0 | return NT_STATUS_INSUFFICIENT_RESOURCES; |
1393 | 0 | } |
1394 | | |
1395 | 0 | state.op = talloc_zero(table, struct smbXsrv_open); |
1396 | 0 | if (state.op == NULL) { |
1397 | 0 | return NT_STATUS_NO_MEMORY; |
1398 | 0 | } |
1399 | 0 | state.op->table = table; |
1400 | |
|
1401 | 0 | local_id = idr_get_new_random( |
1402 | 0 | table->local.idr, |
1403 | 0 | state.op, |
1404 | 0 | table->local.lowest_id, |
1405 | 0 | table->local.highest_id); |
1406 | 0 | if (local_id == -1) { |
1407 | 0 | TALLOC_FREE(state.op); |
1408 | 0 | return NT_STATUS_INSUFFICIENT_RESOURCES; |
1409 | 0 | } |
1410 | 0 | state.op->local_id = local_id; |
1411 | 0 | SMB_ASSERT(state.op->local_id == local_id); /* No coercion loss */ |
1412 | | |
1413 | 0 | table->local.num_opens += 1; |
1414 | |
|
1415 | 0 | state.op->idle_time = now; |
1416 | 0 | state.op->status = NT_STATUS_FILE_CLOSED; |
1417 | |
|
1418 | 0 | status = dbwrap_do_locked( |
1419 | 0 | table->global.db_ctx, key, smb2srv_open_recreate_fn, &state); |
1420 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1421 | 0 | DBG_DEBUG("dbwrap_do_locked() for %s failed: %s\n", |
1422 | 0 | tdb_data_dbg(key), |
1423 | 0 | nt_errstr(status)); |
1424 | 0 | goto fail; |
1425 | 0 | } |
1426 | | |
1427 | 0 | if (!NT_STATUS_IS_OK(state.status)) { |
1428 | 0 | status = state.status; |
1429 | 0 | DBG_DEBUG("smb2srv_open_recreate_fn for %s failed: %s\n", |
1430 | 0 | tdb_data_dbg(key), |
1431 | 0 | nt_errstr(status)); |
1432 | 0 | goto fail; |
1433 | 0 | } |
1434 | | |
1435 | 0 | state.op->session = session; |
1436 | 0 | state.op->tcon = tcon; |
1437 | |
|
1438 | 0 | talloc_set_destructor(state.op, smbXsrv_open_destructor); |
1439 | |
|
1440 | 0 | if (CHECK_DEBUGLVL(10)) { |
1441 | 0 | struct smbXsrv_openB open_blob = { |
1442 | 0 | .info.info0 = state.op, |
1443 | 0 | }; |
1444 | 0 | DBG_DEBUG("global_id (0x%08x) stored\n", |
1445 | 0 | state.op->global->open_global_id); |
1446 | 0 | NDR_PRINT_DEBUG(smbXsrv_openB, &open_blob); |
1447 | 0 | } |
1448 | |
|
1449 | 0 | *_open = state.op; |
1450 | |
|
1451 | 0 | return NT_STATUS_OK; |
1452 | 0 | fail: |
1453 | 0 | table->local.num_opens -= 1; |
1454 | |
|
1455 | 0 | ret = idr_remove(table->local.idr, state.op->local_id); |
1456 | 0 | SMB_ASSERT(ret == 0); |
1457 | 0 | TALLOC_FREE(state.op); |
1458 | 0 | return status; |
1459 | 0 | } |
1460 | | |
1461 | | struct smbXsrv_open_global_traverse_state { |
1462 | | int (*fn)(struct db_record *rec, |
1463 | | struct smbXsrv_open_global0 *global, |
1464 | | TDB_DATA *rc_open_global_key, |
1465 | | void *private_data); |
1466 | | void *private_data; |
1467 | | }; |
1468 | | |
1469 | | static int smbXsrv_open_global_traverse_fn(struct db_record *rec, void *data) |
1470 | 0 | { |
1471 | 0 | struct smbXsrv_open_global_traverse_state *state = |
1472 | 0 | (struct smbXsrv_open_global_traverse_state*)data; |
1473 | 0 | struct smbXsrv_open_global0 *global = NULL; |
1474 | 0 | TDB_DATA key = dbwrap_record_get_key(rec); |
1475 | 0 | TDB_DATA val = dbwrap_record_get_value(rec); |
1476 | 0 | NTSTATUS status; |
1477 | 0 | int ret = -1; |
1478 | | |
1479 | | /* |
1480 | | * Currently expected fixed size of the replay cache record is the |
1481 | | * size of the open_global record's key and the expected size of |
1482 | | * the key is 32 bytes (client GUID + create GUID). |
1483 | | */ |
1484 | 0 | if ((key.dsize == 32) && (val.dsize == sizeof(uint32_t))) { |
1485 | 0 | return state->fn(rec, NULL, &val, state->private_data); |
1486 | 0 | } |
1487 | | |
1488 | 0 | status = smbXsrv_open_global_parse_record( |
1489 | 0 | talloc_tos(), key, val, &global); |
1490 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1491 | 0 | return -1; |
1492 | 0 | } |
1493 | | |
1494 | 0 | ret = state->fn(rec, global, NULL, state->private_data); |
1495 | 0 | talloc_free(global); |
1496 | 0 | return ret; |
1497 | 0 | } |
1498 | | |
1499 | | NTSTATUS smbXsrv_open_global_traverse( |
1500 | | int (*fn)(struct db_record *rec, |
1501 | | struct smbXsrv_open_global0 *global, |
1502 | | TDB_DATA *rc_open_global_key, |
1503 | | void *private_data), |
1504 | | void *private_data) |
1505 | 0 | { |
1506 | |
|
1507 | 0 | NTSTATUS status; |
1508 | 0 | int count = 0; |
1509 | 0 | struct smbXsrv_open_global_traverse_state state = { |
1510 | 0 | .fn = fn, |
1511 | 0 | .private_data = private_data, |
1512 | 0 | }; |
1513 | |
|
1514 | 0 | become_root(); |
1515 | 0 | status = smbXsrv_open_global_init(); |
1516 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1517 | 0 | unbecome_root(); |
1518 | 0 | DEBUG(0, ("Failed to initialize open_global: %s\n", |
1519 | 0 | nt_errstr(status))); |
1520 | 0 | return status; |
1521 | 0 | } |
1522 | | |
1523 | 0 | status = dbwrap_traverse_read(smbXsrv_open_global_db_ctx, |
1524 | 0 | smbXsrv_open_global_traverse_fn, |
1525 | 0 | &state, |
1526 | 0 | &count); |
1527 | 0 | unbecome_root(); |
1528 | |
|
1529 | 0 | return status; |
1530 | 0 | } |
1531 | | |
1532 | | struct smbXsrv_open_cleanup_state { |
1533 | | uint32_t global_id; |
1534 | | struct GUID client_guid; |
1535 | | struct GUID create_guid; |
1536 | | NTSTATUS status; |
1537 | | }; |
1538 | | |
1539 | | static void smbXsrv_open_cleanup_fn( |
1540 | | struct db_record *rec, TDB_DATA oldval, void *private_data) |
1541 | 0 | { |
1542 | 0 | struct smbXsrv_open_cleanup_state *state = private_data; |
1543 | 0 | struct smbXsrv_open_global0 *global = NULL; |
1544 | 0 | TDB_DATA key = dbwrap_record_get_key(rec); |
1545 | 0 | bool delete_open = false; |
1546 | |
|
1547 | 0 | if (oldval.dsize == 0) { |
1548 | 0 | DBG_DEBUG("[global: 0x%08x] " |
1549 | 0 | "empty record in %s, skipping...\n", |
1550 | 0 | state->global_id, |
1551 | 0 | dbwrap_name(dbwrap_record_get_db(rec))); |
1552 | 0 | state->status = NT_STATUS_OK; |
1553 | 0 | return; |
1554 | 0 | } |
1555 | | |
1556 | 0 | state->status = smbXsrv_open_global_parse_record( |
1557 | 0 | talloc_tos(), key, oldval, &global); |
1558 | 0 | if (!NT_STATUS_IS_OK(state->status)) { |
1559 | 0 | DBG_WARNING("[global: %x08x] " |
1560 | 0 | "smbXsrv_open_global_parse_record() in %s " |
1561 | 0 | "failed: %s, deleting record\n", |
1562 | 0 | state->global_id, |
1563 | 0 | dbwrap_name(dbwrap_record_get_db(rec)), |
1564 | 0 | nt_errstr(state->status)); |
1565 | 0 | delete_open = true; |
1566 | 0 | goto do_delete; |
1567 | 0 | } |
1568 | | |
1569 | 0 | if (server_id_is_disconnected(&global->server_id)) { |
1570 | 0 | struct timeval now = timeval_current(); |
1571 | 0 | struct timeval disconnect_time; |
1572 | 0 | struct timeval_buf buf; |
1573 | 0 | int64_t tdiff; |
1574 | |
|
1575 | 0 | nttime_to_timeval(&disconnect_time, global->disconnect_time); |
1576 | 0 | tdiff = usec_time_diff(&now, &disconnect_time); |
1577 | 0 | delete_open = (tdiff >= 1000*global->durable_timeout_msec); |
1578 | |
|
1579 | 0 | DBG_DEBUG("[global: 0x%08x] " |
1580 | 0 | "disconnected at [%s] %"PRIi64"s ago with " |
1581 | 0 | "timeout of %"PRIu32"s -%s reached\n", |
1582 | 0 | state->global_id, |
1583 | 0 | timeval_str_buf(&disconnect_time, |
1584 | 0 | false, |
1585 | 0 | false, |
1586 | 0 | &buf), |
1587 | 0 | tdiff/1000000, |
1588 | 0 | global->durable_timeout_msec / 1000, |
1589 | 0 | delete_open ? "" : " not"); |
1590 | 0 | } else if (!serverid_exists(&global->server_id)) { |
1591 | 0 | struct server_id_buf idbuf; |
1592 | 0 | DBG_DEBUG("[global: 0x%08x] " |
1593 | 0 | "server[%s] does not exist\n", |
1594 | 0 | state->global_id, |
1595 | 0 | server_id_str_buf(global->server_id, &idbuf)); |
1596 | 0 | delete_open = true; |
1597 | 0 | } |
1598 | |
|
1599 | 0 | if (!delete_open) { |
1600 | 0 | state->status = NT_STATUS_CANNOT_DELETE; |
1601 | 0 | return; |
1602 | 0 | } |
1603 | 0 | do_delete: |
1604 | 0 | state->status = dbwrap_record_delete(rec); |
1605 | 0 | if (!NT_STATUS_IS_OK(state->status)) { |
1606 | 0 | DBG_WARNING("[global: 0x%08x] " |
1607 | 0 | "failed to delete record " |
1608 | 0 | "from %s: %s\n", |
1609 | 0 | state->global_id, |
1610 | 0 | dbwrap_name(dbwrap_record_get_db(rec)), |
1611 | 0 | nt_errstr(state->status)); |
1612 | 0 | return; |
1613 | 0 | } |
1614 | | |
1615 | 0 | DBG_DEBUG("[global: 0x%08x] " |
1616 | 0 | "deleted record from %s\n", |
1617 | 0 | state->global_id, |
1618 | 0 | dbwrap_name(dbwrap_record_get_db(rec))); |
1619 | 0 | state->status = NT_STATUS_OK; |
1620 | 0 | if (global != NULL) { |
1621 | 0 | state->client_guid = global->client_guid; |
1622 | 0 | state->create_guid = global->create_guid; |
1623 | 0 | } |
1624 | 0 | } |
1625 | | |
1626 | | NTSTATUS smbXsrv_open_cleanup(uint64_t persistent_id) |
1627 | 0 | { |
1628 | 0 | struct smbXsrv_open_cleanup_state state = { |
1629 | 0 | .global_id = persistent_id & UINT32_MAX, |
1630 | 0 | }; |
1631 | 0 | struct smbXsrv_open_global_key_buf key_buf; |
1632 | 0 | TDB_DATA key = smbXsrv_open_global_id_to_key( |
1633 | 0 | state.global_id, &key_buf); |
1634 | 0 | NTSTATUS status; |
1635 | |
|
1636 | 0 | status = dbwrap_do_locked( |
1637 | 0 | smbXsrv_open_global_db_ctx, |
1638 | 0 | key, |
1639 | 0 | smbXsrv_open_cleanup_fn, |
1640 | 0 | &state); |
1641 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1642 | 0 | DBG_DEBUG("[global: 0x%08x] dbwrap_do_locked failed: %s\n", |
1643 | 0 | state.global_id, |
1644 | 0 | nt_errstr(status)); |
1645 | 0 | return status; |
1646 | 0 | } |
1647 | | |
1648 | 0 | if (!NT_STATUS_IS_OK(state.status)) { |
1649 | 0 | return state.status; |
1650 | 0 | } |
1651 | | |
1652 | 0 | if (GUID_all_zero(&state.create_guid)) { |
1653 | 0 | return NT_STATUS_OK; |
1654 | 0 | } |
1655 | 0 | status = smbXsrv_replay_cleanup(&state.client_guid, &state.create_guid); |
1656 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1657 | 0 | DBG_DEBUG("Failed to remove replay record\n"); |
1658 | 0 | return status; |
1659 | 0 | } |
1660 | | |
1661 | 0 | return NT_STATUS_OK; |
1662 | 0 | } |
1663 | | |
1664 | | NTSTATUS smbXsrv_replay_cleanup(const struct GUID *client_guid, |
1665 | | const struct GUID *create_guid) |
1666 | 0 | { |
1667 | 0 | struct smbXsrv_open_replay_cache_key_buf rc_key_buf; |
1668 | 0 | TDB_DATA key; |
1669 | 0 | NTSTATUS status; |
1670 | |
|
1671 | 0 | key = smbXsrv_open_replay_cache_key(client_guid, |
1672 | 0 | create_guid, |
1673 | 0 | &rc_key_buf); |
1674 | |
|
1675 | 0 | status = dbwrap_purge(smbXsrv_open_global_db_ctx, key); |
1676 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1677 | 0 | struct GUID_txt_buf create_guid_buf; |
1678 | | DBG_ERR("create_guid [%s] purge replay-cache " |
1679 | 0 | "record failed: %s\n", |
1680 | 0 | GUID_buf_string(create_guid, &create_guid_buf), |
1681 | 0 | nt_errstr(status)); |
1682 | 0 | } |
1683 | 0 | return status; |
1684 | 0 | } |