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.
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.
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.
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/>.
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"
36 #define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
38 static TDB_CONTEXT
*netsamlogon_tdb
= NULL
;
40 /***********************************************************************
42 ***********************************************************************/
44 bool netsamlogon_cache_init(void)
46 bool first_try
= true;
49 struct tdb_context
*tdb
;
51 if (netsamlogon_tdb
) {
55 path
= cache_path(talloc_tos(), NETSAMLOGON_TDB
);
60 tdb
= tdb_open_log(path
, 0, TDB_DEFAULT
|TDB_INCOMPATIBLE_HASH
,
61 O_RDWR
| O_CREAT
, 0600);
63 DEBUG(0,("tdb_open_log('%s') - failed\n", path
));
67 ret
= tdb_check(tdb
, NULL
, NULL
);
70 DEBUG(0,("tdb_check('%s') - failed\n", path
));
74 netsamlogon_tdb
= tdb
;
85 DEBUG(0,("retry after truncate for '%s'\n", path
));
86 ret
= truncate(path
, 0);
88 DBG_ERR("truncate failed: %s\n", strerror(errno
));
96 /***********************************************************************
97 Clear cache getpwnam and getgroups entries from the winbindd cache
98 ***********************************************************************/
100 void netsamlogon_clear_cached_user(const struct dom_sid
*user_sid
)
102 struct dom_sid_buf keystr
;
104 if (!netsamlogon_cache_init()) {
105 DEBUG(0,("netsamlogon_clear_cached_user: cannot open "
111 /* Prepare key as DOMAIN-SID/USER-RID string */
112 dom_sid_str_buf(user_sid
, &keystr
);
114 DBG_DEBUG("SID [%s]\n", keystr
.buf
);
116 tdb_delete_bystring(netsamlogon_tdb
, keystr
.buf
);
119 /***********************************************************************
120 Store a netr_SamInfo3 structure in a tdb for later user
121 username should be in UTF-8 format
122 ***********************************************************************/
124 bool netsamlogon_cache_store(const char *username
, struct netr_SamInfo3
*info3
)
127 TDB_DATA data
= { .dptr
= &dummy
, .dsize
= sizeof(dummy
) };
128 struct dom_sid_buf keystr
;
130 struct dom_sid user_sid
;
131 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
133 enum ndr_err_code ndr_err
;
134 struct netsamlogoncache_entry r
;
141 if (!netsamlogon_cache_init()) {
142 D_WARNING("netsamlogon_cache_store: cannot open %s for write!\n",
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.
153 dom_sid_str_buf(info3
->base
.domain_sid
, &keystr
);
155 ret
= tdb_store_bystring(netsamlogon_tdb
, keystr
.buf
, data
, TDB_INSERT
);
157 if ((ret
== -1) && (tdb_error(netsamlogon_tdb
) != TDB_ERR_EXISTS
)) {
158 D_WARNING("Could not store domain marker for %s: %s\n",
159 keystr
.buf
, tdb_errorstr(netsamlogon_tdb
));
163 sid_compose(&user_sid
, info3
->base
.domain_sid
, info3
->base
.rid
);
165 /* Prepare key as DOMAIN-SID/USER-RID string */
166 dom_sid_str_buf(&user_sid
, &keystr
);
168 DBG_DEBUG("SID [%s]\n", keystr
.buf
);
172 if (info3
->base
.full_name
.string
== NULL
) {
173 struct netr_SamInfo3
*cached_info3
;
174 const char *full_name
= NULL
;
176 cached_info3
= netsamlogon_cache_get(tmp_ctx
, &user_sid
);
177 if (cached_info3
!= NULL
) {
178 full_name
= cached_info3
->base
.full_name
.string
;
181 if (full_name
!= NULL
) {
182 info3
->base
.full_name
.string
= talloc_strdup(info3
, full_name
);
183 if (info3
->base
.full_name
.string
== NULL
) {
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 */
192 if (!info3
->base
.account_name
.string
) {
193 info3
->base
.account_name
.string
= talloc_strdup(info3
, username
);
194 if (info3
->base
.account_name
.string
== NULL
) {
199 r
.timestamp
= time(NULL
);
202 /* avoid storing secret information */
203 ZERO_STRUCT(r
.info3
.base
.key
);
204 ZERO_STRUCT(r
.info3
.base
.LMSessKey
);
206 if (DEBUGLEVEL
>= 10) {
207 NDR_PRINT_DEBUG(netsamlogoncache_entry
, &r
);
210 ndr_err
= ndr_push_struct_blob(&blob
, tmp_ctx
, &r
,
211 (ndr_push_flags_fn_t
)ndr_push_netsamlogoncache_entry
);
212 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
213 DBG_WARNING("failed to push entry to cache: %s\n",
214 ndr_errstr(ndr_err
));
218 data
.dsize
= blob
.length
;
219 data
.dptr
= blob
.data
;
221 if (tdb_store_bystring(netsamlogon_tdb
, keystr
.buf
, data
, TDB_REPLACE
) == 0) {
226 TALLOC_FREE(tmp_ctx
);
230 /***********************************************************************
231 Retrieves a netr_SamInfo3 structure from a tdb. Caller must
232 free the user_info struct (talloced memory)
233 ***********************************************************************/
235 struct netr_SamInfo3
*netsamlogon_cache_get(TALLOC_CTX
*mem_ctx
, const struct dom_sid
*user_sid
)
237 struct netr_SamInfo3
*info3
= NULL
;
239 struct dom_sid_buf keystr
;
240 enum ndr_err_code ndr_err
;
242 struct netsamlogoncache_entry r
;
244 if (!netsamlogon_cache_init()) {
245 DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n",
250 /* Prepare key as DOMAIN-SID/USER-RID string */
251 dom_sid_str_buf(user_sid
, &keystr
);
252 DBG_DEBUG("SID [%s]\n", keystr
.buf
);
253 data
= tdb_fetch_bystring( netsamlogon_tdb
, keystr
.buf
);
256 D_DEBUG("tdb fetch for %s is empty\n", keystr
.buf
);
260 info3
= talloc_zero(mem_ctx
, struct netr_SamInfo3
);
265 blob
= data_blob_const(data
.dptr
, data
.dsize
);
267 ndr_err
= ndr_pull_struct_blob_all(
269 (ndr_pull_flags_fn_t
)ndr_pull_netsamlogoncache_entry
);
271 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
272 D_WARNING("netsamlogon_cache_get: failed to pull entry from cache\n");
273 tdb_delete_bystring(netsamlogon_tdb
, keystr
.buf
);
278 NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG
, netsamlogoncache_entry
, &r
);
280 info3
= (struct netr_SamInfo3
*)talloc_memdup(mem_ctx
, &r
.info3
,
284 SAFE_FREE(data
.dptr
);
289 bool netsamlogon_cache_have(const struct dom_sid
*sid
)
291 struct dom_sid_buf keystr
;
294 if (!netsamlogon_cache_init()) {
295 DBG_WARNING("Cannot open %s\n", NETSAMLOGON_TDB
);
299 dom_sid_str_buf(sid
, &keystr
);
301 ok
= tdb_exists(netsamlogon_tdb
, string_term_tdb_data(keystr
.buf
));
305 struct netsamlog_cache_forall_state
{
307 int (*cb
)(const char *sid_str
,
309 struct netr_SamInfo3
*,
314 static int netsamlog_cache_traverse_cb(struct tdb_context
*tdb
,
319 struct netsamlog_cache_forall_state
*state
=
320 (struct netsamlog_cache_forall_state
*)private_data
;
321 TALLOC_CTX
*mem_ctx
= NULL
;
323 const char *sid_str
= NULL
;
325 struct netsamlogoncache_entry r
;
326 enum ndr_err_code ndr_err
;
330 if (key
.dsize
== 0) {
333 if (key
.dptr
[key
.dsize
- 1] != '\0') {
336 if (data
.dptr
== NULL
) {
339 sid_str
= (char *)key
.dptr
;
341 ok
= string_to_sid(&sid
, sid_str
);
343 DBG_ERR("String to SID failed for %s\n", sid_str
);
347 if (sid
.num_auths
!= 5) {
351 mem_ctx
= talloc_new(state
->mem_ctx
);
352 if (mem_ctx
== NULL
) {
356 blob
= data_blob_const(data
.dptr
, data
.dsize
);
358 ndr_err
= ndr_pull_struct_blob(
359 &blob
, state
->mem_ctx
, &r
,
360 (ndr_pull_flags_fn_t
)ndr_pull_netsamlogoncache_entry
);
362 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
363 DBG_ERR("failed to pull entry from cache\n");
367 ret
= state
->cb(sid_str
, r
.timestamp
, &r
.info3
, state
->private_data
);
369 TALLOC_FREE(mem_ctx
);
373 int netsamlog_cache_for_all(int (*cb
)(const char *sid_str
,
375 struct netr_SamInfo3
*,
380 TALLOC_CTX
*mem_ctx
= NULL
;
381 struct netsamlog_cache_forall_state state
;
383 if (!netsamlogon_cache_init()) {
384 DBG_ERR("Cannot open %s\n", NETSAMLOGON_TDB
);
388 mem_ctx
= talloc_init("netsamlog_cache_for_all");
389 if (mem_ctx
== NULL
) {
393 state
= (struct netsamlog_cache_forall_state
) {
396 .private_data
= private_data
,
399 ret
= tdb_traverse_read(netsamlogon_tdb
,
400 netsamlog_cache_traverse_cb
,
403 TALLOC_FREE(state
.mem_ctx
);