Line data Source code
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 :
36 : #define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
37 :
38 : static TDB_CONTEXT *netsamlogon_tdb = NULL;
39 :
40 : /***********************************************************************
41 : open the tdb
42 : ***********************************************************************/
43 :
44 107865 : bool netsamlogon_cache_init(void)
45 : {
46 107865 : bool first_try = true;
47 107865 : char *path = NULL;
48 0 : int ret;
49 0 : struct tdb_context *tdb;
50 :
51 107865 : if (netsamlogon_tdb) {
52 107802 : return true;
53 : }
54 :
55 63 : path = cache_path(talloc_tos(), NETSAMLOGON_TDB);
56 63 : if (path == NULL) {
57 0 : return false;
58 : }
59 63 : again:
60 63 : tdb = tdb_open_log(path, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH,
61 : O_RDWR | O_CREAT, 0600);
62 63 : if (tdb == NULL) {
63 0 : DEBUG(0,("tdb_open_log('%s') - failed\n", path));
64 0 : goto clear;
65 : }
66 :
67 63 : ret = tdb_check(tdb, NULL, NULL);
68 63 : if (ret != 0) {
69 0 : tdb_close(tdb);
70 0 : DEBUG(0,("tdb_check('%s') - failed\n", path));
71 0 : goto clear;
72 : }
73 :
74 63 : netsamlogon_tdb = tdb;
75 63 : talloc_free(path);
76 63 : return true;
77 :
78 0 : clear:
79 0 : if (!first_try) {
80 0 : talloc_free(path);
81 0 : return false;
82 : }
83 0 : first_try = false;
84 :
85 0 : DEBUG(0,("retry after truncate for '%s'\n", path));
86 0 : ret = truncate(path, 0);
87 0 : if (ret == -1) {
88 0 : DBG_ERR("truncate failed: %s\n", strerror(errno));
89 0 : talloc_free(path);
90 0 : return false;
91 : }
92 :
93 0 : goto again;
94 : }
95 :
96 : /***********************************************************************
97 : Clear cache getpwnam and getgroups entries from the winbindd cache
98 : ***********************************************************************/
99 :
100 4 : void netsamlogon_clear_cached_user(const struct dom_sid *user_sid)
101 : {
102 0 : struct dom_sid_buf keystr;
103 :
104 4 : if (!netsamlogon_cache_init()) {
105 0 : DEBUG(0,("netsamlogon_clear_cached_user: cannot open "
106 : "%s for write!\n",
107 : NETSAMLOGON_TDB));
108 0 : return;
109 : }
110 :
111 : /* Prepare key as DOMAIN-SID/USER-RID string */
112 4 : dom_sid_str_buf(user_sid, &keystr);
113 :
114 4 : DBG_DEBUG("SID [%s]\n", keystr.buf);
115 :
116 4 : tdb_delete_bystring(netsamlogon_tdb, keystr.buf);
117 : }
118 :
119 : /***********************************************************************
120 : Store a netr_SamInfo3 structure in a tdb for later user
121 : username should be in UTF-8 format
122 : ***********************************************************************/
123 :
124 868 : bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3)
125 : {
126 868 : uint8_t dummy = 0;
127 868 : TDB_DATA data = { .dptr = &dummy, .dsize = sizeof(dummy) };
128 0 : struct dom_sid_buf keystr;
129 868 : bool result = false;
130 0 : struct dom_sid user_sid;
131 868 : TALLOC_CTX *tmp_ctx = talloc_stackframe();
132 0 : DATA_BLOB blob;
133 0 : enum ndr_err_code ndr_err;
134 0 : struct netsamlogoncache_entry r;
135 0 : int ret;
136 :
137 868 : if (!info3) {
138 0 : goto fail;
139 : }
140 :
141 868 : if (!netsamlogon_cache_init()) {
142 0 : D_WARNING("netsamlogon_cache_store: cannot open %s for write!\n",
143 : NETSAMLOGON_TDB);
144 0 : goto fail;
145 : }
146 :
147 : /*
148 : * First write a record with just the domain sid for
149 : * netsamlogon_cache_domain_known. Use TDB_INSERT to avoid
150 : * overwriting potentially other data. We're just interested
151 : * in the existence of that record.
152 : */
153 868 : dom_sid_str_buf(info3->base.domain_sid, &keystr);
154 :
155 868 : ret = tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_INSERT);
156 :
157 868 : if ((ret == -1) && (tdb_error(netsamlogon_tdb) != TDB_ERR_EXISTS)) {
158 0 : D_WARNING("Could not store domain marker for %s: %s\n",
159 : keystr.buf, tdb_errorstr(netsamlogon_tdb));
160 0 : goto fail;
161 : }
162 :
163 868 : sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid);
164 :
165 : /* Prepare key as DOMAIN-SID/USER-RID string */
166 868 : dom_sid_str_buf(&user_sid, &keystr);
167 :
168 868 : DBG_DEBUG("SID [%s]\n", keystr.buf);
169 :
170 : /* Prepare data */
171 :
172 868 : if (info3->base.full_name.string == NULL) {
173 0 : struct netr_SamInfo3 *cached_info3;
174 0 : const char *full_name = NULL;
175 :
176 0 : cached_info3 = netsamlogon_cache_get(tmp_ctx, &user_sid);
177 0 : if (cached_info3 != NULL) {
178 0 : full_name = cached_info3->base.full_name.string;
179 : }
180 :
181 0 : if (full_name != NULL) {
182 0 : info3->base.full_name.string = talloc_strdup(info3, full_name);
183 0 : if (info3->base.full_name.string == NULL) {
184 0 : goto fail;
185 : }
186 : }
187 : }
188 :
189 : /* only Samba fills in the username, not sure why NT doesn't */
190 : /* so we fill it in since winbindd_getpwnam() makes use of it */
191 :
192 868 : if (!info3->base.account_name.string) {
193 0 : info3->base.account_name.string = talloc_strdup(info3, username);
194 0 : if (info3->base.account_name.string == NULL) {
195 0 : goto fail;
196 : }
197 : }
198 :
199 868 : r.timestamp = time(NULL);
200 868 : r.info3 = *info3;
201 :
202 : /* avoid storing secret information */
203 868 : ZERO_STRUCT(r.info3.base.key);
204 868 : ZERO_STRUCT(r.info3.base.LMSessKey);
205 :
206 868 : if (DEBUGLEVEL >= 10) {
207 0 : NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
208 : }
209 :
210 868 : ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r,
211 : (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry);
212 868 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
213 0 : DBG_WARNING("failed to push entry to cache: %s\n",
214 : ndr_errstr(ndr_err));
215 0 : goto fail;
216 : }
217 :
218 868 : data.dsize = blob.length;
219 868 : data.dptr = blob.data;
220 :
221 868 : if (tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_REPLACE) == 0) {
222 868 : result = true;
223 : }
224 :
225 0 : fail:
226 868 : TALLOC_FREE(tmp_ctx);
227 868 : return result;
228 : }
229 :
230 : /***********************************************************************
231 : Retrieves a netr_SamInfo3 structure from a tdb. Caller must
232 : free the user_info struct (talloced memory)
233 : ***********************************************************************/
234 :
235 106942 : struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid)
236 : {
237 106942 : struct netr_SamInfo3 *info3 = NULL;
238 0 : TDB_DATA data;
239 0 : struct dom_sid_buf keystr;
240 0 : enum ndr_err_code ndr_err;
241 0 : DATA_BLOB blob;
242 0 : struct netsamlogoncache_entry r;
243 :
244 106942 : if (!netsamlogon_cache_init()) {
245 0 : DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n",
246 : NETSAMLOGON_TDB));
247 0 : return NULL;
248 : }
249 :
250 : /* Prepare key as DOMAIN-SID/USER-RID string */
251 106942 : dom_sid_str_buf(user_sid, &keystr);
252 106942 : DBG_DEBUG("SID [%s]\n", keystr.buf);
253 106942 : data = tdb_fetch_bystring( netsamlogon_tdb, keystr.buf );
254 :
255 106942 : if (!data.dptr) {
256 9983 : D_DEBUG("tdb fetch for %s is empty\n", keystr.buf);
257 9983 : return NULL;
258 : }
259 :
260 96959 : info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
261 96959 : if (!info3) {
262 0 : goto done;
263 : }
264 :
265 96959 : blob = data_blob_const(data.dptr, data.dsize);
266 :
267 96959 : ndr_err = ndr_pull_struct_blob_all(
268 : &blob, mem_ctx, &r,
269 : (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
270 :
271 96959 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
272 0 : D_WARNING("netsamlogon_cache_get: failed to pull entry from cache\n");
273 0 : tdb_delete_bystring(netsamlogon_tdb, keystr.buf);
274 0 : TALLOC_FREE(info3);
275 0 : goto done;
276 : }
277 :
278 96959 : NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, netsamlogoncache_entry, &r);
279 :
280 96959 : info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3,
281 : sizeof(r.info3));
282 :
283 96959 : done:
284 96959 : SAFE_FREE(data.dptr);
285 :
286 96959 : return info3;
287 : }
288 :
289 0 : bool netsamlogon_cache_have(const struct dom_sid *sid)
290 : {
291 0 : struct dom_sid_buf keystr;
292 0 : bool ok;
293 :
294 0 : if (!netsamlogon_cache_init()) {
295 0 : DBG_WARNING("Cannot open %s\n", NETSAMLOGON_TDB);
296 0 : return false;
297 : }
298 :
299 0 : dom_sid_str_buf(sid, &keystr);
300 :
301 0 : ok = tdb_exists(netsamlogon_tdb, string_term_tdb_data(keystr.buf));
302 0 : return ok;
303 : }
304 :
305 : struct netsamlog_cache_forall_state {
306 : TALLOC_CTX *mem_ctx;
307 : int (*cb)(const char *sid_str,
308 : time_t when_cached,
309 : struct netr_SamInfo3 *,
310 : void *private_data);
311 : void *private_data;
312 : };
313 :
314 96 : static int netsamlog_cache_traverse_cb(struct tdb_context *tdb,
315 : TDB_DATA key,
316 : TDB_DATA data,
317 : void *private_data)
318 : {
319 96 : struct netsamlog_cache_forall_state *state =
320 : (struct netsamlog_cache_forall_state *)private_data;
321 96 : TALLOC_CTX *mem_ctx = NULL;
322 0 : DATA_BLOB blob;
323 96 : const char *sid_str = NULL;
324 0 : struct dom_sid sid;
325 0 : struct netsamlogoncache_entry r;
326 0 : enum ndr_err_code ndr_err;
327 0 : int ret;
328 0 : bool ok;
329 :
330 96 : if (key.dsize == 0) {
331 0 : return 0;
332 : }
333 96 : if (key.dptr[key.dsize - 1] != '\0') {
334 0 : return 0;
335 : }
336 96 : if (data.dptr == NULL) {
337 0 : return 0;
338 : }
339 96 : sid_str = (char *)key.dptr;
340 :
341 96 : ok = string_to_sid(&sid, sid_str);
342 96 : if (!ok) {
343 0 : DBG_ERR("String to SID failed for %s\n", sid_str);
344 0 : return -1;
345 : }
346 :
347 96 : if (sid.num_auths != 5) {
348 24 : return 0;
349 : }
350 :
351 72 : mem_ctx = talloc_new(state->mem_ctx);
352 72 : if (mem_ctx == NULL) {
353 0 : return -1;
354 : }
355 :
356 72 : blob = data_blob_const(data.dptr, data.dsize);
357 :
358 72 : ndr_err = ndr_pull_struct_blob(
359 : &blob, state->mem_ctx, &r,
360 : (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
361 :
362 72 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
363 0 : DBG_ERR("failed to pull entry from cache\n");
364 0 : return -1;
365 : }
366 :
367 72 : ret = state->cb(sid_str, r.timestamp, &r.info3, state->private_data);
368 :
369 72 : TALLOC_FREE(mem_ctx);
370 72 : return ret;
371 : }
372 :
373 6 : int netsamlog_cache_for_all(int (*cb)(const char *sid_str,
374 : time_t when_cached,
375 : struct netr_SamInfo3 *,
376 : void *private_data),
377 : void *private_data)
378 : {
379 0 : int ret;
380 6 : TALLOC_CTX *mem_ctx = NULL;
381 0 : struct netsamlog_cache_forall_state state;
382 :
383 6 : if (!netsamlogon_cache_init()) {
384 0 : DBG_ERR("Cannot open %s\n", NETSAMLOGON_TDB);
385 0 : return -1;
386 : }
387 :
388 6 : mem_ctx = talloc_init("netsamlog_cache_for_all");
389 6 : if (mem_ctx == NULL) {
390 0 : return -1;
391 : }
392 :
393 6 : state = (struct netsamlog_cache_forall_state) {
394 : .mem_ctx = mem_ctx,
395 : .cb = cb,
396 : .private_data = private_data,
397 : };
398 :
399 6 : ret = tdb_traverse_read(netsamlogon_tdb,
400 : netsamlog_cache_traverse_cb,
401 : &state);
402 :
403 6 : TALLOC_FREE(state.mem_ctx);
404 6 : return ret;
405 : }
|