/src/samba/source3/libsmb/samlogon_cache.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Net_sam_logon info3 helpers |
4 | | Copyright (C) Alexander Bokovoy 2002. |
5 | | Copyright (C) Andrew Bartlett 2002. |
6 | | Copyright (C) Gerald Carter 2003. |
7 | | Copyright (C) Tim Potter 2003. |
8 | | Copyright (C) Guenther Deschner 2008. |
9 | | |
10 | | This program is free software; you can redistribute it and/or modify |
11 | | it under the terms of the GNU General Public License as published by |
12 | | the Free Software Foundation; either version 3 of the License, or |
13 | | (at your option) any later version. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | GNU General Public License for more details. |
19 | | |
20 | | You should have received a copy of the GNU General Public License |
21 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | #include "replace.h" |
25 | | #include "samlogon_cache.h" |
26 | | #include "system/filesys.h" |
27 | | #include "system/time.h" |
28 | | #include "lib/util/debug.h" |
29 | | #include "lib/util/talloc_stack.h" |
30 | | #include "lib/util/memory.h" /* for SAFE_FREE() */ |
31 | | #include "source3/lib/util_path.h" |
32 | | #include "librpc/gen_ndr/ndr_krb5pac.h" |
33 | | #include "../libcli/security/security.h" |
34 | | #include "util_tdb.h" |
35 | | #include "param/loadparm.h" |
36 | | |
37 | 0 | #define NETSAMLOGON_TDB "netsamlogon_cache.tdb" |
38 | | |
39 | | static TDB_CONTEXT *netsamlogon_tdb = NULL; |
40 | | |
41 | | /*********************************************************************** |
42 | | open the tdb |
43 | | ***********************************************************************/ |
44 | | |
45 | | bool netsamlogon_cache_init(void) |
46 | 0 | { |
47 | 0 | bool first_try = true; |
48 | 0 | char *path = NULL; |
49 | 0 | int ret; |
50 | 0 | struct tdb_context *tdb; |
51 | |
|
52 | 0 | if (netsamlogon_tdb) { |
53 | 0 | return true; |
54 | 0 | } |
55 | | |
56 | 0 | path = cache_path(talloc_tos(), NETSAMLOGON_TDB); |
57 | 0 | if (path == NULL) { |
58 | 0 | return false; |
59 | 0 | } |
60 | 0 | again: |
61 | 0 | tdb = tdb_open_log(path, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH, |
62 | 0 | O_RDWR | O_CREAT, 0600); |
63 | 0 | if (tdb == NULL) { |
64 | 0 | DEBUG(0,("tdb_open_log('%s') - failed\n", path)); |
65 | 0 | goto clear; |
66 | 0 | } |
67 | | |
68 | 0 | ret = tdb_check(tdb, NULL, NULL); |
69 | 0 | if (ret != 0) { |
70 | 0 | tdb_close(tdb); |
71 | 0 | DEBUG(0,("tdb_check('%s') - failed\n", path)); |
72 | 0 | goto clear; |
73 | 0 | } |
74 | | |
75 | 0 | netsamlogon_tdb = tdb; |
76 | 0 | talloc_free(path); |
77 | 0 | return true; |
78 | | |
79 | 0 | clear: |
80 | 0 | if (!first_try) { |
81 | 0 | talloc_free(path); |
82 | 0 | return false; |
83 | 0 | } |
84 | 0 | first_try = false; |
85 | |
|
86 | 0 | DEBUG(0,("retry after truncate for '%s'\n", path)); |
87 | 0 | ret = truncate(path, 0); |
88 | 0 | if (ret == -1) { |
89 | 0 | DBG_ERR("truncate failed: %s\n", strerror(errno)); |
90 | 0 | talloc_free(path); |
91 | 0 | return false; |
92 | 0 | } |
93 | | |
94 | 0 | goto again; |
95 | 0 | } |
96 | | |
97 | | /*********************************************************************** |
98 | | Clear the whole cache |
99 | | ***********************************************************************/ |
100 | | |
101 | | void netsamlogon_cache_flush(void) |
102 | 0 | { |
103 | 0 | if (!netsamlogon_cache_init()) { |
104 | 0 | DBG_ERR("cannot open %s for write!\n", |
105 | 0 | NETSAMLOGON_TDB); |
106 | 0 | return; |
107 | 0 | } |
108 | | |
109 | 0 | DBG_DEBUG("Wipe all!\n"); |
110 | |
|
111 | 0 | tdb_wipe_all(netsamlogon_tdb); |
112 | 0 | } |
113 | | |
114 | | /*********************************************************************** |
115 | | Clear cache getpwnam and getgroups entries from the winbindd cache |
116 | | ***********************************************************************/ |
117 | | |
118 | | void netsamlogon_clear_cached_user(const struct dom_sid *user_sid) |
119 | 0 | { |
120 | 0 | struct dom_sid_buf keystr; |
121 | |
|
122 | 0 | if (!netsamlogon_cache_init()) { |
123 | 0 | DEBUG(0,("netsamlogon_clear_cached_user: cannot open " |
124 | 0 | "%s for write!\n", |
125 | 0 | NETSAMLOGON_TDB)); |
126 | 0 | return; |
127 | 0 | } |
128 | | |
129 | | /* Prepare key as DOMAIN-SID/USER-RID string */ |
130 | 0 | dom_sid_str_buf(user_sid, &keystr); |
131 | |
|
132 | 0 | DBG_DEBUG("SID [%s]\n", keystr.buf); |
133 | |
|
134 | 0 | tdb_delete_bystring(netsamlogon_tdb, keystr.buf); |
135 | 0 | } |
136 | | |
137 | | /*********************************************************************** |
138 | | Store a netr_SamInfo3 structure in a tdb for later user |
139 | | username should be in UTF-8 format |
140 | | ***********************************************************************/ |
141 | | |
142 | | bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3) |
143 | 0 | { |
144 | 0 | uint8_t dummy = 0; |
145 | 0 | TDB_DATA data = { .dptr = &dummy, .dsize = sizeof(dummy) }; |
146 | 0 | struct dom_sid_buf keystr; |
147 | 0 | bool result = false; |
148 | 0 | struct dom_sid user_sid; |
149 | 0 | TALLOC_CTX *tmp_ctx = talloc_stackframe(); |
150 | 0 | DATA_BLOB blob; |
151 | 0 | enum ndr_err_code ndr_err; |
152 | 0 | struct netsamlogoncache_entry r; |
153 | 0 | int ret; |
154 | |
|
155 | 0 | if (!info3) { |
156 | 0 | goto fail; |
157 | 0 | } |
158 | | |
159 | 0 | if (!netsamlogon_cache_init()) { |
160 | 0 | D_WARNING("netsamlogon_cache_store: cannot open %s for write!\n", |
161 | 0 | NETSAMLOGON_TDB); |
162 | 0 | goto fail; |
163 | 0 | } |
164 | | |
165 | | /* |
166 | | * First write a record with just the domain sid for |
167 | | * netsamlogon_cache_domain_known. Use TDB_INSERT to avoid |
168 | | * overwriting potentially other data. We're just interested |
169 | | * in the existence of that record. |
170 | | */ |
171 | 0 | dom_sid_str_buf(info3->base.domain_sid, &keystr); |
172 | |
|
173 | 0 | ret = tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_INSERT); |
174 | |
|
175 | 0 | if ((ret == -1) && (tdb_error(netsamlogon_tdb) != TDB_ERR_EXISTS)) { |
176 | 0 | D_WARNING("Could not store domain marker for %s: %s\n", |
177 | 0 | keystr.buf, tdb_errorstr(netsamlogon_tdb)); |
178 | 0 | goto fail; |
179 | 0 | } |
180 | | |
181 | 0 | sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid); |
182 | | |
183 | | /* Prepare key as DOMAIN-SID/USER-RID string */ |
184 | 0 | dom_sid_str_buf(&user_sid, &keystr); |
185 | |
|
186 | 0 | DBG_DEBUG("SID [%s]\n", keystr.buf); |
187 | | |
188 | | /* Prepare data */ |
189 | |
|
190 | 0 | if (info3->base.full_name.string == NULL) { |
191 | 0 | struct netr_SamInfo3 *cached_info3; |
192 | 0 | const char *full_name = NULL; |
193 | |
|
194 | 0 | cached_info3 = netsamlogon_cache_get(tmp_ctx, &user_sid); |
195 | 0 | if (cached_info3 != NULL) { |
196 | 0 | full_name = cached_info3->base.full_name.string; |
197 | 0 | } |
198 | |
|
199 | 0 | if (full_name != NULL) { |
200 | 0 | info3->base.full_name.string = talloc_strdup(info3, full_name); |
201 | 0 | if (info3->base.full_name.string == NULL) { |
202 | 0 | goto fail; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | /* only Samba fills in the username, not sure why NT doesn't */ |
208 | | /* so we fill it in since winbindd_getpwnam() makes use of it */ |
209 | | |
210 | 0 | if (!info3->base.account_name.string) { |
211 | 0 | info3->base.account_name.string = talloc_strdup(info3, username); |
212 | 0 | if (info3->base.account_name.string == NULL) { |
213 | 0 | goto fail; |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | 0 | r.timestamp = time(NULL); |
218 | 0 | r.info3 = *info3; |
219 | | |
220 | | /* avoid storing secret information */ |
221 | 0 | ZERO_STRUCT(r.info3.base.key); |
222 | 0 | ZERO_STRUCT(r.info3.base.LMSessKey); |
223 | |
|
224 | 0 | if (DEBUGLEVEL >= 10) { |
225 | 0 | NDR_PRINT_DEBUG(netsamlogoncache_entry, &r); |
226 | 0 | } |
227 | |
|
228 | 0 | ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r, |
229 | 0 | (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry); |
230 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
231 | 0 | DBG_WARNING("failed to push entry to cache: %s\n", |
232 | 0 | ndr_errstr(ndr_err)); |
233 | 0 | goto fail; |
234 | 0 | } |
235 | | |
236 | 0 | data.dsize = blob.length; |
237 | 0 | data.dptr = blob.data; |
238 | |
|
239 | 0 | if (tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_REPLACE) == 0) { |
240 | 0 | result = true; |
241 | 0 | } |
242 | |
|
243 | 0 | fail: |
244 | 0 | TALLOC_FREE(tmp_ctx); |
245 | 0 | return result; |
246 | 0 | } |
247 | | |
248 | | /*********************************************************************** |
249 | | Retrieves a netr_SamInfo3 structure from a tdb. Caller must |
250 | | free the user_info struct (talloced memory) |
251 | | ***********************************************************************/ |
252 | | |
253 | | struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid) |
254 | 0 | { |
255 | 0 | struct netr_SamInfo3 *info3 = NULL; |
256 | 0 | TDB_DATA data; |
257 | 0 | struct dom_sid_buf keystr; |
258 | 0 | enum ndr_err_code ndr_err; |
259 | 0 | DATA_BLOB blob; |
260 | 0 | struct netsamlogoncache_entry r; |
261 | 0 | int ttl; |
262 | |
|
263 | 0 | if (!netsamlogon_cache_init()) { |
264 | 0 | DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n", |
265 | 0 | NETSAMLOGON_TDB)); |
266 | 0 | return NULL; |
267 | 0 | } |
268 | | |
269 | | /* Prepare key as DOMAIN-SID/USER-RID string */ |
270 | 0 | dom_sid_str_buf(user_sid, &keystr); |
271 | 0 | DBG_DEBUG("SID [%s]\n", keystr.buf); |
272 | 0 | data = tdb_fetch_bystring( netsamlogon_tdb, keystr.buf ); |
273 | |
|
274 | 0 | if (!data.dptr) { |
275 | 0 | D_DEBUG("tdb fetch for %s is empty\n", keystr.buf); |
276 | 0 | return NULL; |
277 | 0 | } |
278 | | |
279 | 0 | info3 = talloc_zero(mem_ctx, struct netr_SamInfo3); |
280 | 0 | if (!info3) { |
281 | 0 | goto done; |
282 | 0 | } |
283 | | |
284 | 0 | blob = data_blob_const(data.dptr, data.dsize); |
285 | |
|
286 | 0 | ndr_err = ndr_pull_struct_blob_all( |
287 | 0 | &blob, mem_ctx, &r, |
288 | 0 | (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry); |
289 | |
|
290 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
291 | 0 | D_WARNING("netsamlogon_cache_get: failed to pull entry from cache\n"); |
292 | 0 | tdb_delete_bystring(netsamlogon_tdb, keystr.buf); |
293 | 0 | TALLOC_FREE(info3); |
294 | 0 | goto done; |
295 | 0 | } |
296 | | |
297 | 0 | NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, netsamlogoncache_entry, &r); |
298 | |
|
299 | 0 | ttl = lp_parm_int(-1, "netsamlogoncache", "timeout", 0); |
300 | |
|
301 | 0 | if (ttl > 0 && r.timestamp + ttl < time(NULL)) { |
302 | 0 | DBG_DEBUG("netsamlogon cache entry expired\n"); |
303 | 0 | tdb_delete_bystring(netsamlogon_tdb, keystr.buf); |
304 | 0 | TALLOC_FREE(info3); |
305 | 0 | goto done; |
306 | 0 | } |
307 | | |
308 | 0 | info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3, |
309 | 0 | sizeof(r.info3)); |
310 | |
|
311 | 0 | done: |
312 | 0 | SAFE_FREE(data.dptr); |
313 | |
|
314 | 0 | return info3; |
315 | 0 | } |
316 | | |
317 | | bool netsamlogon_cache_have(const struct dom_sid *sid) |
318 | 0 | { |
319 | 0 | struct dom_sid_buf keystr; |
320 | 0 | bool ok; |
321 | |
|
322 | 0 | if (!netsamlogon_cache_init()) { |
323 | 0 | DBG_WARNING("Cannot open %s\n", NETSAMLOGON_TDB); |
324 | 0 | return false; |
325 | 0 | } |
326 | | |
327 | 0 | dom_sid_str_buf(sid, &keystr); |
328 | |
|
329 | 0 | ok = tdb_exists(netsamlogon_tdb, string_term_tdb_data(keystr.buf)); |
330 | 0 | return ok; |
331 | 0 | } |
332 | | |
333 | | struct netsamlog_cache_forall_state { |
334 | | TALLOC_CTX *mem_ctx; |
335 | | int (*cb)(const char *sid_str, |
336 | | time_t when_cached, |
337 | | struct netr_SamInfo3 *, |
338 | | void *private_data); |
339 | | void *private_data; |
340 | | }; |
341 | | |
342 | | static int netsamlog_cache_traverse_cb(struct tdb_context *tdb, |
343 | | TDB_DATA key, |
344 | | TDB_DATA data, |
345 | | void *private_data) |
346 | 0 | { |
347 | 0 | struct netsamlog_cache_forall_state *state = |
348 | 0 | (struct netsamlog_cache_forall_state *)private_data; |
349 | 0 | TALLOC_CTX *mem_ctx = NULL; |
350 | 0 | DATA_BLOB blob; |
351 | 0 | const char *sid_str = NULL; |
352 | 0 | struct dom_sid sid; |
353 | 0 | struct netsamlogoncache_entry r; |
354 | 0 | enum ndr_err_code ndr_err; |
355 | 0 | int ret; |
356 | 0 | bool ok; |
357 | |
|
358 | 0 | if (key.dsize == 0) { |
359 | 0 | return 0; |
360 | 0 | } |
361 | 0 | if (key.dptr[key.dsize - 1] != '\0') { |
362 | 0 | return 0; |
363 | 0 | } |
364 | 0 | if (data.dptr == NULL) { |
365 | 0 | return 0; |
366 | 0 | } |
367 | 0 | sid_str = (char *)key.dptr; |
368 | |
|
369 | 0 | ok = string_to_sid(&sid, sid_str); |
370 | 0 | if (!ok) { |
371 | 0 | DBG_ERR("String to SID failed for %s\n", sid_str); |
372 | 0 | return -1; |
373 | 0 | } |
374 | | |
375 | 0 | if (sid.num_auths != 5) { |
376 | 0 | return 0; |
377 | 0 | } |
378 | | |
379 | 0 | mem_ctx = talloc_new(state->mem_ctx); |
380 | 0 | if (mem_ctx == NULL) { |
381 | 0 | return -1; |
382 | 0 | } |
383 | | |
384 | 0 | blob = data_blob_const(data.dptr, data.dsize); |
385 | |
|
386 | 0 | ndr_err = ndr_pull_struct_blob( |
387 | 0 | &blob, state->mem_ctx, &r, |
388 | 0 | (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry); |
389 | |
|
390 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
391 | 0 | DBG_ERR("failed to pull entry from cache\n"); |
392 | 0 | return -1; |
393 | 0 | } |
394 | | |
395 | 0 | ret = state->cb(sid_str, r.timestamp, &r.info3, state->private_data); |
396 | |
|
397 | 0 | TALLOC_FREE(mem_ctx); |
398 | 0 | return ret; |
399 | 0 | } |
400 | | |
401 | | int netsamlog_cache_for_all(int (*cb)(const char *sid_str, |
402 | | time_t when_cached, |
403 | | struct netr_SamInfo3 *, |
404 | | void *private_data), |
405 | | void *private_data) |
406 | 0 | { |
407 | 0 | int ret; |
408 | 0 | TALLOC_CTX *mem_ctx = NULL; |
409 | 0 | struct netsamlog_cache_forall_state state; |
410 | |
|
411 | 0 | if (!netsamlogon_cache_init()) { |
412 | 0 | DBG_ERR("Cannot open %s\n", NETSAMLOGON_TDB); |
413 | 0 | return -1; |
414 | 0 | } |
415 | | |
416 | 0 | mem_ctx = talloc_init("netsamlog_cache_for_all"); |
417 | 0 | if (mem_ctx == NULL) { |
418 | 0 | return -1; |
419 | 0 | } |
420 | | |
421 | 0 | state = (struct netsamlog_cache_forall_state) { |
422 | 0 | .mem_ctx = mem_ctx, |
423 | 0 | .cb = cb, |
424 | 0 | .private_data = private_data, |
425 | 0 | }; |
426 | |
|
427 | 0 | ret = tdb_traverse_read(netsamlogon_tdb, |
428 | 0 | netsamlog_cache_traverse_cb, |
429 | 0 | &state); |
430 | |
|
431 | | TALLOC_FREE(state.mem_ctx); |
432 | 0 | return ret; |
433 | 0 | } |