2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "system/filesys.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_winbind.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
37 #include "libsmb/samlogon_cache.h"
38 #include "lib/namemap_cache.h"
39 #include "lib/util/string_wrappers.h"
41 #include "lib/crypto/gnutls_helpers.h"
42 #include <gnutls/crypto.h>
45 #define DBGC_CLASS DBGC_WINBIND
47 #define WINBINDD_CACHE_VER1 1 /* initial db version */
48 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
50 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
51 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
53 extern struct winbindd_methods reconnect_methods
;
55 extern struct winbindd_methods reconnect_ads_methods
;
57 extern struct winbindd_methods builtin_passdb_methods
;
58 extern struct winbindd_methods sam_passdb_methods
;
60 static void wcache_flush_cache(void);
62 static bool opt_nocache
= False
;
65 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
66 * Here are the list of entry types that are *not* stored
67 * as form struct cache_entry in the cache.
70 static const char *non_centry_keys
[] = {
75 WINBINDD_CACHE_VERSION_KEYSTR
,
79 bool winbindd_use_idmap_cache(void)
84 bool winbindd_use_cache(void)
89 void winbindd_set_use_cache(bool use_cache
)
91 opt_nocache
= !use_cache
;
94 void winbindd_flush_caches(void)
96 /* We need to invalidate cached user list entries on a SIGHUP
97 otherwise cached access denied errors due to restrict anonymous
98 hang around until the sequence number changes. */
100 if (!wcache_invalidate_cache()) {
101 DBG_ERR("invalidating the cache failed; revalidate the cache\n");
102 if (!winbindd_cache_validate_and_initialize()) {
108 /************************************************************************
109 Is this key a non-centry type ?
110 ************************************************************************/
112 static bool is_non_centry_key(TDB_DATA kbuf
)
116 if (kbuf
.dptr
== NULL
|| kbuf
.dsize
== 0) {
119 for (i
= 0; non_centry_keys
[i
] != NULL
; i
++) {
120 size_t namelen
= strlen(non_centry_keys
[i
]);
121 if (kbuf
.dsize
< namelen
) {
124 if (strncmp(non_centry_keys
[i
], (const char *)kbuf
.dptr
, namelen
) == 0) {
131 struct winbind_cache
{
137 uint32_t sequence_number
;
143 void (*smb_panic_fn
)(const char *const why
) = smb_panic
;
145 static struct winbind_cache
*wcache
;
147 static char *wcache_path(void)
150 * Data needs to be kept persistent in state directory for
151 * running with "winbindd offline logon".
153 return state_path(talloc_tos(), "winbindd_cache.tdb");
156 static void winbindd_domain_init_backend(struct winbindd_domain
*domain
)
158 if (domain
->backend
!= NULL
) {
162 if (domain
->internal
) {
163 domain
->backend
= &builtin_passdb_methods
;
166 if (dom_sid_equal(&domain
->sid
, &global_sid_Builtin
)) {
167 domain
->initialized
= true;
170 if (strequal(domain
->name
, get_global_sam_name()) &&
171 sid_check_is_our_sam(&domain
->sid
))
173 domain
->backend
= &sam_passdb_methods
;
176 if (!domain
->initialized
) {
177 /* We do not need a connection to an RW DC for cache operation */
178 init_dc_connection(domain
, false);
182 if (domain
->backend
== NULL
) {
183 struct winbindd_domain
*our_domain
= domain
;
185 /* find our domain first so we can figure out if we
186 are joined to a kerberized domain */
188 if (!domain
->primary
) {
189 our_domain
= find_our_domain();
192 if ((our_domain
->active_directory
|| IS_DC
)
193 && domain
->active_directory
194 && !lp_winbind_rpc_only())
196 DBG_INFO("Setting ADS methods for domain %s\n",
198 domain
->backend
= &reconnect_ads_methods
;
201 #endif /* HAVE_ADS */
203 if (domain
->backend
== NULL
) {
204 DBG_INFO("Setting MS-RPC methods for domain %s\n", domain
->name
);
205 domain
->backend
= &reconnect_methods
;
209 /* get the winbind_cache structure */
210 static struct winbind_cache
*get_cache(struct winbindd_domain
*domain
)
212 struct winbind_cache
*ret
= wcache
;
214 winbindd_domain_init_backend(domain
);
220 ret
= SMB_XMALLOC_P(struct winbind_cache
);
224 wcache_flush_cache();
230 free a centry structure
232 static void centry_free(struct cache_entry
*centry
)
236 SAFE_FREE(centry
->data
);
240 static bool centry_check_bytes(struct cache_entry
*centry
, size_t nbytes
)
242 if (centry
->len
- centry
->ofs
< nbytes
) {
243 DBG_ERR("centry corruption? needed %u bytes, have %d\n",
244 (unsigned int)nbytes
,
245 centry
->len
- centry
->ofs
);
252 pull a uint64_t from a cache entry
254 static uint64_t centry_uint64_t(struct cache_entry
*centry
)
258 if (!centry_check_bytes(centry
, 8)) {
259 smb_panic_fn("centry_uint64_t");
261 ret
= BVAL(centry
->data
, centry
->ofs
);
267 pull a uint32_t from a cache entry
269 static uint32_t centry_uint32(struct cache_entry
*centry
)
273 if (!centry_check_bytes(centry
, 4)) {
274 smb_panic_fn("centry_uint32");
276 ret
= IVAL(centry
->data
, centry
->ofs
);
282 pull a uint16_t from a cache entry
284 static uint16_t centry_uint16(struct cache_entry
*centry
)
287 if (!centry_check_bytes(centry
, 2)) {
288 smb_panic_fn("centry_uint16");
290 ret
= SVAL(centry
->data
, centry
->ofs
);
296 pull a uint8_t from a cache entry
298 static uint8_t centry_uint8(struct cache_entry
*centry
)
301 if (!centry_check_bytes(centry
, 1)) {
302 smb_panic_fn("centry_uint8");
304 ret
= CVAL(centry
->data
, centry
->ofs
);
310 pull a NTTIME from a cache entry
312 static NTTIME
centry_nttime(struct cache_entry
*centry
)
315 if (!centry_check_bytes(centry
, 8)) {
316 smb_panic_fn("centry_nttime");
318 ret
= IVAL(centry
->data
, centry
->ofs
);
320 ret
+= (uint64_t)IVAL(centry
->data
, centry
->ofs
) << 32;
326 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
328 static time_t centry_time(struct cache_entry
*centry
)
330 return (time_t)centry_nttime(centry
);
333 /* pull a string from a cache entry, using the supplied
336 static char *centry_string(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
341 len
= centry_uint8(centry
);
344 /* a deliberate NULL string */
348 if (!centry_check_bytes(centry
, (size_t)len
)) {
349 smb_panic_fn("centry_string");
352 ret
= talloc_array(mem_ctx
, char, len
+1);
354 smb_panic_fn("centry_string out of memory\n");
356 memcpy(ret
,centry
->data
+ centry
->ofs
, len
);
362 /* pull a hash16 from a cache entry, using the supplied
365 static char *centry_hash16(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
370 len
= centry_uint8(centry
);
373 DBG_ERR("centry corruption? hash len (%u) != 16\n",
378 if (!centry_check_bytes(centry
, 16)) {
382 ret
= talloc_array(mem_ctx
, char, 16);
384 smb_panic_fn("centry_hash out of memory\n");
386 memcpy(ret
,centry
->data
+ centry
->ofs
, 16);
391 /* pull a sid from a cache entry, using the supplied
394 static bool centry_sid(struct cache_entry
*centry
, struct dom_sid
*sid
)
399 sid_string
= centry_string(centry
, talloc_tos());
400 if (sid_string
== NULL
) {
403 ret
= string_to_sid(sid
, sid_string
);
404 TALLOC_FREE(sid_string
);
410 pull a NTSTATUS from a cache entry
412 static NTSTATUS
centry_ntstatus(struct cache_entry
*centry
)
416 status
= NT_STATUS(centry_uint32(centry
));
421 /* the server is considered down if it can't give us a sequence number */
422 static bool wcache_server_down(struct winbindd_domain
*domain
)
429 ret
= (domain
->sequence_number
== DOM_SEQUENCE_NONE
);
432 DBG_DEBUG("wcache_server_down: server for Domain %s down\n",
437 struct wcache_seqnum_state
{
439 uint32_t *last_seq_check
;
442 static int wcache_seqnum_parser(TDB_DATA key
, TDB_DATA data
,
445 struct wcache_seqnum_state
*state
= private_data
;
447 if (data
.dsize
!= 8) {
448 DBG_DEBUG("wcache_fetch_seqnum: invalid data size %d\n",
453 *state
->seqnum
= IVAL(data
.dptr
, 0);
454 *state
->last_seq_check
= IVAL(data
.dptr
, 4);
458 static bool wcache_fetch_seqnum(const char *domain_name
, uint32_t *seqnum
,
459 uint32_t *last_seq_check
)
461 struct wcache_seqnum_state state
= {
462 .seqnum
= seqnum
, .last_seq_check
= last_seq_check
464 size_t len
= strlen(domain_name
);
466 TDB_DATA key
= { .dptr
= (uint8_t *)keystr
, .dsize
= sizeof(keystr
) };
469 if (wcache
->tdb
== NULL
) {
470 DBG_DEBUG("wcache_fetch_seqnum: tdb == NULL\n");
474 snprintf(keystr
, sizeof(keystr
), "SEQNUM/%s", domain_name
);
476 ret
= tdb_parse_record(wcache
->tdb
, key
, wcache_seqnum_parser
,
481 static NTSTATUS
fetch_cache_seqnum( struct winbindd_domain
*domain
, time_t now
)
483 uint32_t last_check
, time_diff
;
485 if (!wcache_fetch_seqnum(domain
->name
, &domain
->sequence_number
,
487 return NT_STATUS_UNSUCCESSFUL
;
489 domain
->last_seq_check
= last_check
;
491 /* have we expired? */
493 time_diff
= now
- domain
->last_seq_check
;
494 if ((int)time_diff
> lp_winbind_cache_time()) {
495 DBG_DEBUG("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
496 domain
->name
, domain
->sequence_number
,
497 (uint32_t)domain
->last_seq_check
);
498 return NT_STATUS_UNSUCCESSFUL
;
501 DBG_DEBUG("fetch_cache_seqnum: success [%s][%u @ %u]\n",
502 domain
->name
, domain
->sequence_number
,
503 (uint32_t)domain
->last_seq_check
);
508 bool wcache_store_seqnum(const char *domain_name
, uint32_t seqnum
,
509 time_t last_seq_check
)
511 size_t len
= strlen(domain_name
);
513 TDB_DATA key
= { .dptr
= (uint8_t *)keystr
, .dsize
= sizeof(keystr
) };
517 if (wcache
->tdb
== NULL
) {
518 DBG_DEBUG("wcache_store_seqnum: wcache->tdb == NULL\n");
522 snprintf(keystr
, sizeof(keystr
), "SEQNUM/%s", domain_name
);
524 SIVAL(buf
, 0, seqnum
);
525 SIVAL(buf
, 4, last_seq_check
);
527 ret
= tdb_store(wcache
->tdb
, key
, make_tdb_data(buf
, sizeof(buf
)),
530 DBG_DEBUG("tdb_store_bystring failed: %s\n",
531 tdb_errorstr(wcache
->tdb
));
535 DBG_DEBUG("wcache_store_seqnum: success [%s][%u @ %u]\n",
536 domain_name
, seqnum
, (unsigned)last_seq_check
);
541 static bool store_cache_seqnum( struct winbindd_domain
*domain
)
543 return wcache_store_seqnum(domain
->name
, domain
->sequence_number
,
544 domain
->last_seq_check
);
548 refresh the domain sequence number on timeout.
551 static void refresh_sequence_number(struct winbindd_domain
*domain
)
555 time_t t
= time(NULL
);
556 unsigned cache_time
= lp_winbind_cache_time();
558 if (is_domain_offline(domain
)) {
564 time_diff
= t
- domain
->last_seq_check
;
566 /* see if we have to refetch the domain sequence number */
567 if ((time_diff
< cache_time
) &&
568 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
569 NT_STATUS_IS_OK(domain
->last_status
)) {
570 DBG_DEBUG("refresh_sequence_number: %s time ok\n", domain
->name
);
574 /* try to get the sequence number from the tdb cache first */
575 /* this will update the timestamp as well */
577 status
= fetch_cache_seqnum( domain
, t
);
578 if (NT_STATUS_IS_OK(status
) &&
579 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
580 NT_STATUS_IS_OK(domain
->last_status
)) {
584 /* just use the current time */
585 domain
->last_status
= NT_STATUS_OK
;
586 domain
->sequence_number
= time(NULL
);
587 domain
->last_seq_check
= time(NULL
);
589 /* save the new sequence number in the cache */
590 store_cache_seqnum( domain
);
593 DBG_DEBUG("refresh_sequence_number: %s seq number is now %d\n",
594 domain
->name
, domain
->sequence_number
);
600 decide if a cache entry has expired
602 static bool centry_expired(struct winbindd_domain
*domain
, const char *keystr
, struct cache_entry
*centry
)
604 /* If we've been told to be offline - stay in that state... */
605 if (lp_winbind_offline_logon() && get_global_winbindd_state_offline()) {
606 DBG_DEBUG("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
607 keystr
, domain
->name
);
611 /* when the domain is offline return the cached entry.
612 * This deals with transient offline states... */
614 if (!domain
->online
) {
615 DBG_DEBUG("centry_expired: Key %s for domain %s valid as domain is offline.\n",
616 keystr
, domain
->name
);
620 /* if the server is OK and our cache entry came from when it was down then
621 the entry is invalid */
622 if ((domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
623 (centry
->sequence_number
== DOM_SEQUENCE_NONE
)) {
624 DBG_DEBUG("centry_expired: Key %s for domain %s invalid sequence.\n",
625 keystr
, domain
->name
);
629 /* if the server is down or the cache entry is not older than the
630 current sequence number or it did not timeout then it is OK */
631 if (wcache_server_down(domain
)
632 || ((centry
->sequence_number
== domain
->sequence_number
)
633 && ((time_t)centry
->timeout
> time(NULL
)))) {
634 DBG_DEBUG("centry_expired: Key %s for domain %s is good.\n",
635 keystr
, domain
->name
);
639 DBG_DEBUG("centry_expired: Key %s for domain %s expired\n",
640 keystr
, domain
->name
);
646 static struct cache_entry
*wcache_fetch_raw(char *kstr
)
649 struct cache_entry
*centry
;
652 key
= string_tdb_data(kstr
);
653 data
= tdb_fetch(wcache
->tdb
, key
);
659 centry
= SMB_XMALLOC_P(struct cache_entry
);
660 centry
->data
= (unsigned char *)data
.dptr
;
661 centry
->len
= data
.dsize
;
664 if (centry
->len
< 16) {
665 /* huh? corrupt cache? */
666 DBG_DEBUG("wcache_fetch_raw: Corrupt cache for key %s "
667 "(len < 16)?\n", kstr
);
672 centry
->status
= centry_ntstatus(centry
);
673 centry
->sequence_number
= centry_uint32(centry
);
674 centry
->timeout
= centry_uint64_t(centry
);
679 static bool is_my_own_sam_domain(struct winbindd_domain
*domain
)
681 if (strequal(domain
->name
, get_global_sam_name()) &&
682 sid_check_is_our_sam(&domain
->sid
)) {
689 static bool is_builtin_domain(struct winbindd_domain
*domain
)
691 if (strequal(domain
->name
, "BUILTIN") &&
692 sid_check_is_builtin(&domain
->sid
)) {
700 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
701 number and return status
703 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
704 struct winbindd_domain
*domain
,
705 const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
706 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
707 struct winbindd_domain
*domain
,
708 const char *format
, ...)
712 struct cache_entry
*centry
;
715 if (!winbindd_use_cache() ||
716 is_my_own_sam_domain(domain
) ||
717 is_builtin_domain(domain
)) {
721 refresh_sequence_number(domain
);
723 va_start(ap
, format
);
724 ret
= vasprintf(&kstr
, format
, ap
);
731 centry
= wcache_fetch_raw(kstr
);
732 if (centry
== NULL
) {
737 if (centry_expired(domain
, kstr
, centry
)) {
739 DBG_DEBUG("wcache_fetch: entry %s expired for domain %s\n",
740 kstr
, domain
->name
);
747 DBG_DEBUG("wcache_fetch: returning entry %s for domain %s\n",
748 kstr
, domain
->name
);
754 static void wcache_delete(const char *format
, ...) PRINTF_ATTRIBUTE(1,2);
755 static void wcache_delete(const char *format
, ...)
762 va_start(ap
, format
);
763 ret
= vasprintf(&kstr
, format
, ap
);
770 key
= string_tdb_data(kstr
);
772 tdb_delete(wcache
->tdb
, key
);
777 make sure we have at least len bytes available in a centry
779 static void centry_expand(struct cache_entry
*centry
, uint32_t len
)
781 if (centry
->len
- centry
->ofs
>= len
)
784 centry
->data
= SMB_REALLOC_ARRAY(centry
->data
, unsigned char,
787 DBG_ERR("out of memory: needed %d bytes in centry_expand\n", centry
->len
);
788 smb_panic_fn("out of memory in centry_expand");
793 push a uint64_t into a centry
795 static void centry_put_uint64_t(struct cache_entry
*centry
, uint64_t v
)
797 centry_expand(centry
, 8);
798 SBVAL(centry
->data
, centry
->ofs
, v
);
803 push a uint32_t into a centry
805 static void centry_put_uint32(struct cache_entry
*centry
, uint32_t v
)
807 centry_expand(centry
, 4);
808 SIVAL(centry
->data
, centry
->ofs
, v
);
813 push a uint16_t into a centry
815 static void centry_put_uint16(struct cache_entry
*centry
, uint16_t v
)
817 centry_expand(centry
, 2);
818 SSVAL(centry
->data
, centry
->ofs
, v
);
823 push a uint8_t into a centry
825 static void centry_put_uint8(struct cache_entry
*centry
, uint8_t v
)
827 centry_expand(centry
, 1);
828 SCVAL(centry
->data
, centry
->ofs
, v
);
833 push a string into a centry
835 static void centry_put_string(struct cache_entry
*centry
, const char *s
)
840 /* null strings are marked as len 0xFFFF */
841 centry_put_uint8(centry
, 0xFF);
846 /* can't handle more than 254 char strings. Truncating is probably best */
848 DBG_DEBUG("centry_put_string: truncating len (%zu) to: 254\n",
852 centry_put_uint8(centry
, len
);
853 centry_expand(centry
, len
);
854 memcpy(centry
->data
+ centry
->ofs
, s
, len
);
859 push a 16 byte hash into a centry - treat as 16 byte string.
861 static void centry_put_hash16(struct cache_entry
*centry
, const uint8_t val
[16])
863 centry_put_uint8(centry
, 16);
864 centry_expand(centry
, 16);
865 memcpy(centry
->data
+ centry
->ofs
, val
, 16);
869 static void centry_put_sid(struct cache_entry
*centry
, const struct dom_sid
*sid
)
871 struct dom_sid_buf sid_string
;
872 centry_put_string(centry
, dom_sid_str_buf(sid
, &sid_string
));
877 put NTSTATUS into a centry
879 static void centry_put_ntstatus(struct cache_entry
*centry
, NTSTATUS status
)
881 uint32_t status_value
= NT_STATUS_V(status
);
882 centry_put_uint32(centry
, status_value
);
887 push a NTTIME into a centry
889 static void centry_put_nttime(struct cache_entry
*centry
, NTTIME nt
)
891 centry_expand(centry
, 8);
892 SIVAL(centry
->data
, centry
->ofs
, nt
& 0xFFFFFFFF);
894 SIVAL(centry
->data
, centry
->ofs
, nt
>> 32);
899 push a time_t into a centry - use a 64 bit size.
900 NTTIME here is being used as a convenient 64-bit size.
902 static void centry_put_time(struct cache_entry
*centry
, time_t t
)
904 NTTIME nt
= (NTTIME
)t
;
905 centry_put_nttime(centry
, nt
);
909 start a centry for output. When finished, call centry_end()
911 static struct cache_entry
*centry_start(struct winbindd_domain
*domain
,
914 struct cache_entry
*centry
;
919 centry
= SMB_XMALLOC_P(struct cache_entry
);
921 centry
->len
= 8192; /* reasonable default */
922 centry
->data
= SMB_XMALLOC_ARRAY(uint8_t, centry
->len
);
924 centry
->sequence_number
= domain
->sequence_number
;
925 centry
->timeout
= lp_winbind_cache_time() + time(NULL
);
926 centry_put_ntstatus(centry
, status
);
927 centry_put_uint32(centry
, centry
->sequence_number
);
928 centry_put_uint64_t(centry
, centry
->timeout
);
933 finish a centry and write it to the tdb
935 static void centry_end(struct cache_entry
*centry
, const char *format
, ...) PRINTF_ATTRIBUTE(2,3);
936 static void centry_end(struct cache_entry
*centry
, const char *format
, ...)
943 if (!winbindd_use_cache()) {
947 va_start(ap
, format
);
948 ret
= vasprintf(&kstr
, format
, ap
);
955 key
= string_tdb_data(kstr
);
956 data
.dptr
= centry
->data
;
957 data
.dsize
= centry
->ofs
;
959 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
963 static void wcache_save_name_to_sid(struct winbindd_domain
*domain
,
964 NTSTATUS status
, const char *domain_name
,
965 const char *name
, const struct dom_sid
*sid
,
966 enum lsa_SidType type
)
970 ok
= namemap_cache_set_name2sid(domain_name
, name
, sid
, type
,
971 time(NULL
) + lp_winbind_cache_time());
973 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
977 * Don't store the reverse mapping. The name came from user
978 * input, and we might not have the correct capitalization,
979 * which is important for nsswitch.
983 static void wcache_save_sid_to_name(struct winbindd_domain
*domain
, NTSTATUS status
,
984 const struct dom_sid
*sid
, const char *domain_name
, const char *name
, enum lsa_SidType type
)
988 ok
= namemap_cache_set_sid2name(sid
, domain_name
, name
, type
,
989 time(NULL
) + lp_winbind_cache_time());
991 DBG_DEBUG("namemap_cache_set_sid2name failed\n");
994 if (type
!= SID_NAME_UNKNOWN
) {
995 ok
= namemap_cache_set_name2sid(
996 domain_name
, name
, sid
, type
,
997 time(NULL
) + lp_winbind_cache_time());
999 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
1004 static void wcache_save_lockout_policy(struct winbindd_domain
*domain
,
1006 struct samr_DomInfo12
*lockout_policy
)
1008 struct cache_entry
*centry
;
1010 centry
= centry_start(domain
, status
);
1014 centry_put_nttime(centry
, lockout_policy
->lockout_duration
);
1015 centry_put_nttime(centry
, lockout_policy
->lockout_window
);
1016 centry_put_uint16(centry
, lockout_policy
->lockout_threshold
);
1018 centry_end(centry
, "LOC_POL/%s", domain
->name
);
1020 DBG_DEBUG("wcache_save_lockout_policy: %s\n", domain
->name
);
1022 centry_free(centry
);
1027 static void wcache_save_password_policy(struct winbindd_domain
*domain
,
1029 struct samr_DomInfo1
*policy
)
1031 struct cache_entry
*centry
;
1033 centry
= centry_start(domain
, status
);
1037 centry_put_uint16(centry
, policy
->min_password_length
);
1038 centry_put_uint16(centry
, policy
->password_history_length
);
1039 centry_put_uint32(centry
, policy
->password_properties
);
1040 centry_put_nttime(centry
, policy
->max_password_age
);
1041 centry_put_nttime(centry
, policy
->min_password_age
);
1043 centry_end(centry
, "PWD_POL/%s", domain
->name
);
1045 DBG_DEBUG("wcache_save_password_policy: %s\n", domain
->name
);
1047 centry_free(centry
);
1050 /***************************************************************************
1051 ***************************************************************************/
1053 static void wcache_save_username_alias(struct winbindd_domain
*domain
,
1055 const char *name
, const char *alias
)
1057 struct cache_entry
*centry
;
1060 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1063 centry_put_string( centry
, alias
);
1065 fstrcpy(uname
, name
);
1066 (void)strupper_m(uname
);
1067 centry_end(centry
, "NSS/NA/%s", uname
);
1069 DBG_DEBUG("wcache_save_username_alias: %s -> %s\n", name
, alias
);
1071 centry_free(centry
);
1074 static void wcache_save_alias_username(struct winbindd_domain
*domain
,
1076 const char *alias
, const char *name
)
1078 struct cache_entry
*centry
;
1081 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1084 centry_put_string( centry
, name
);
1086 fstrcpy(uname
, alias
);
1087 (void)strupper_m(uname
);
1088 centry_end(centry
, "NSS/AN/%s", uname
);
1090 DBG_DEBUG("wcache_save_alias_username: %s -> %s\n", alias
, name
);
1092 centry_free(centry
);
1095 /***************************************************************************
1096 ***************************************************************************/
1098 NTSTATUS
resolve_username_to_alias( TALLOC_CTX
*mem_ctx
,
1099 struct winbindd_domain
*domain
,
1100 const char *name
, char **alias
)
1102 struct winbind_cache
*cache
= get_cache(domain
);
1103 struct cache_entry
*centry
= NULL
;
1107 if ( domain
->internal
)
1108 return NT_STATUS_NOT_SUPPORTED
;
1113 upper_name
= talloc_strdup_upper(mem_ctx
, name
);
1114 if (upper_name
== NULL
) {
1115 return NT_STATUS_NO_MEMORY
;
1118 centry
= wcache_fetch(cache
, domain
, "NSS/NA/%s", upper_name
);
1120 talloc_free(upper_name
);
1125 status
= centry
->status
;
1127 if (!NT_STATUS_IS_OK(status
)) {
1128 centry_free(centry
);
1132 *alias
= centry_string( centry
, mem_ctx
);
1134 centry_free(centry
);
1136 DBG_DEBUG("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1137 name
, *alias
? *alias
: "(none)");
1139 return (*alias
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1143 /* If its not in cache and we are offline, then fail */
1145 if (is_domain_offline(domain
)) {
1146 DBG_DEBUG("resolve_username_to_alias: rejecting query "
1147 "in offline mode\n");
1148 return NT_STATUS_NOT_FOUND
;
1151 status
= nss_map_to_alias( mem_ctx
, domain
->name
, name
, alias
);
1153 if ( NT_STATUS_IS_OK( status
) ) {
1154 wcache_save_username_alias(domain
, status
, name
, *alias
);
1157 if ( NT_STATUS_EQUAL( status
, NT_STATUS_NONE_MAPPED
) ) {
1158 wcache_save_username_alias(domain
, status
, name
, "(NULL)");
1161 DBG_INFO("resolve_username_to_alias: backend query returned %s\n",
1164 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1165 set_domain_offline( domain
);
1171 /***************************************************************************
1172 ***************************************************************************/
1174 NTSTATUS
resolve_alias_to_username( TALLOC_CTX
*mem_ctx
,
1175 struct winbindd_domain
*domain
,
1176 const char *alias
, char **name
)
1178 struct winbind_cache
*cache
= get_cache(domain
);
1179 struct cache_entry
*centry
= NULL
;
1183 if ( domain
->internal
)
1184 return NT_STATUS_NOT_SUPPORTED
;
1189 upper_name
= talloc_strdup(mem_ctx
, alias
);
1190 if (upper_name
== NULL
) {
1191 return NT_STATUS_NO_MEMORY
;
1193 if (!strupper_m(upper_name
)) {
1194 talloc_free(upper_name
);
1195 return NT_STATUS_INVALID_PARAMETER
;
1198 centry
= wcache_fetch(cache
, domain
, "NSS/AN/%s", upper_name
);
1200 talloc_free(upper_name
);
1205 status
= centry
->status
;
1207 if (!NT_STATUS_IS_OK(status
)) {
1208 centry_free(centry
);
1212 *name
= centry_string( centry
, mem_ctx
);
1214 centry_free(centry
);
1216 DBG_DEBUG("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1217 alias
, *name
? *name
: "(none)");
1219 return (*name
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1223 /* If its not in cache and we are offline, then fail */
1225 if (is_domain_offline(domain
)) {
1226 DBG_DEBUG("resolve_alias_to_username: rejecting query "
1227 "in offline mode\n");
1228 return NT_STATUS_NOT_FOUND
;
1231 /* an alias cannot contain a domain prefix or '@' */
1233 if (strchr(alias
, '\\') || strchr(alias
, '@')) {
1234 DBG_DEBUG("resolve_alias_to_username: skipping fully "
1235 "qualified name %s\n", alias
);
1236 return NT_STATUS_OBJECT_NAME_INVALID
;
1239 status
= nss_map_from_alias( mem_ctx
, domain
->name
, alias
, name
);
1241 if ( NT_STATUS_IS_OK( status
) ) {
1242 wcache_save_alias_username( domain
, status
, alias
, *name
);
1245 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
1246 wcache_save_alias_username(domain
, status
, alias
, "(NULL)");
1249 DBG_INFO("resolve_alias_to_username: backend query returned %s\n",
1252 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1253 set_domain_offline( domain
);
1259 NTSTATUS
wcache_cached_creds_exist(struct winbindd_domain
*domain
, const struct dom_sid
*sid
)
1261 struct winbind_cache
*cache
= get_cache(domain
);
1263 struct dom_sid_buf tmp
;
1268 return NT_STATUS_INTERNAL_DB_ERROR
;
1271 if (is_null_sid(sid
)) {
1272 return NT_STATUS_INVALID_SID
;
1275 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1276 return NT_STATUS_INVALID_SID
;
1279 fstr_sprintf(key_str
, "CRED/%s", dom_sid_str_buf(sid
, &tmp
));
1281 ret
= tdb_exists(cache
->tdb
, string_tdb_data(key_str
));
1283 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1286 return NT_STATUS_OK
;
1289 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1290 as new salted ones. */
1292 NTSTATUS
wcache_get_creds(struct winbindd_domain
*domain
,
1293 TALLOC_CTX
*mem_ctx
,
1294 const struct dom_sid
*sid
,
1295 const uint8_t **cached_nt_pass
,
1296 const uint8_t **cached_salt
)
1298 struct winbind_cache
*cache
= get_cache(domain
);
1299 struct cache_entry
*centry
= NULL
;
1302 struct dom_sid_buf sidstr
;
1305 return NT_STATUS_INTERNAL_DB_ERROR
;
1308 if (is_null_sid(sid
)) {
1309 return NT_STATUS_INVALID_SID
;
1312 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1313 return NT_STATUS_INVALID_SID
;
1316 /* Try and get a salted cred first. If we can't
1317 fall back to an unsalted cred. */
1319 centry
= wcache_fetch(cache
, domain
, "CRED/%s",
1320 dom_sid_str_buf(sid
, &sidstr
));
1322 DBG_DEBUG("wcache_get_creds: entry for [CRED/%s] not found\n",
1323 dom_sid_str_buf(sid
, &sidstr
));
1324 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1328 * We don't use the time element at this moment,
1329 * but we have to consume it, so that we don't
1330 * need to change the disk format of the cache.
1332 (void)centry_time(centry
);
1334 /* In the salted case this isn't actually the nt_hash itself,
1335 but the MD5 of the salt + nt_hash. Let the caller
1336 sort this out. It can tell as we only return the cached_salt
1337 if we are returning a salted cred. */
1339 *cached_nt_pass
= (const uint8_t *)centry_hash16(centry
, mem_ctx
);
1340 if (*cached_nt_pass
== NULL
) {
1342 dom_sid_str_buf(sid
, &sidstr
);
1344 /* Bad (old) cred cache. Delete and pretend we
1346 DBG_WARNING("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1348 wcache_delete("CRED/%s", sidstr
.buf
);
1349 centry_free(centry
);
1350 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1353 /* We only have 17 bytes more data in the salted cred case. */
1354 if (centry
->len
- centry
->ofs
== 17) {
1355 *cached_salt
= (const uint8_t *)centry_hash16(centry
, mem_ctx
);
1357 *cached_salt
= NULL
;
1360 dump_data_pw("cached_nt_pass", *cached_nt_pass
, NT_HASH_LEN
);
1362 dump_data_pw("cached_salt", *cached_salt
, NT_HASH_LEN
);
1365 status
= centry
->status
;
1367 DBG_DEBUG("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1368 dom_sid_str_buf(sid
, &sidstr
),
1369 nt_errstr(status
) );
1371 centry_free(centry
);
1375 /* Store creds for a SID - only writes out new salted ones. */
1377 NTSTATUS
wcache_save_creds(struct winbindd_domain
*domain
,
1378 const struct dom_sid
*sid
,
1379 const uint8_t nt_pass
[NT_HASH_LEN
])
1381 struct cache_entry
*centry
;
1382 struct dom_sid_buf sid_str
;
1384 uint8_t cred_salt
[NT_HASH_LEN
];
1385 uint8_t salted_hash
[NT_HASH_LEN
];
1386 gnutls_hash_hd_t hash_hnd
= NULL
;
1389 if (is_null_sid(sid
)) {
1390 return NT_STATUS_INVALID_SID
;
1393 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1394 return NT_STATUS_INVALID_SID
;
1397 centry
= centry_start(domain
, NT_STATUS_OK
);
1399 return NT_STATUS_INTERNAL_DB_ERROR
;
1402 dump_data_pw("nt_pass", nt_pass
, NT_HASH_LEN
);
1404 centry_put_time(centry
, time(NULL
));
1406 /* Create a salt and then salt the hash. */
1407 generate_random_buffer(cred_salt
, NT_HASH_LEN
);
1409 rc
= gnutls_hash_init(&hash_hnd
, GNUTLS_DIG_MD5
);
1411 centry_free(centry
);
1412 return gnutls_error_to_ntstatus(rc
, NT_STATUS_HASH_NOT_SUPPORTED
);
1415 rc
= gnutls_hash(hash_hnd
, cred_salt
, 16);
1417 gnutls_hash_deinit(hash_hnd
, NULL
);
1418 centry_free(centry
);
1419 return gnutls_error_to_ntstatus(rc
, NT_STATUS_HASH_NOT_SUPPORTED
);
1421 rc
= gnutls_hash(hash_hnd
, nt_pass
, 16);
1423 gnutls_hash_deinit(hash_hnd
, NULL
);
1424 centry_free(centry
);
1425 return gnutls_error_to_ntstatus(rc
, NT_STATUS_HASH_NOT_SUPPORTED
);
1427 gnutls_hash_deinit(hash_hnd
, salted_hash
);
1429 centry_put_hash16(centry
, salted_hash
);
1430 centry_put_hash16(centry
, cred_salt
);
1431 centry_end(centry
, "CRED/%s", dom_sid_str_buf(sid
, &sid_str
));
1433 DBG_DEBUG("wcache_save_creds: %s\n", sid_str
.buf
);
1435 centry_free(centry
);
1437 return NT_STATUS_OK
;
1441 /* Query display info. This is the basic user list fn */
1442 NTSTATUS
wb_cache_query_user_list(struct winbindd_domain
*domain
,
1443 TALLOC_CTX
*mem_ctx
,
1446 struct winbind_cache
*cache
= get_cache(domain
);
1447 struct cache_entry
*centry
= NULL
;
1448 uint32_t num_rids
= 0;
1449 uint32_t *rids
= NULL
;
1451 unsigned int i
, retry
;
1452 bool old_status
= domain
->online
;
1459 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1464 num_rids
= centry_uint32(centry
);
1466 if (num_rids
== 0) {
1470 rids
= talloc_array(mem_ctx
, uint32_t, num_rids
);
1472 centry_free(centry
);
1473 return NT_STATUS_NO_MEMORY
;
1476 for (i
=0; i
<num_rids
; i
++) {
1477 rids
[i
] = centry_uint32(centry
);
1481 status
= centry
->status
;
1483 DBG_DEBUG("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1484 domain
->name
, nt_errstr(status
) );
1486 centry_free(centry
);
1491 /* Put the query_user_list() in a retry loop. There appears to be
1492 * some bug either with Windows 2000 or Samba's handling of large
1493 * rpc replies. This manifests itself as sudden disconnection
1494 * at a random point in the enumeration of a large (60k) user list.
1495 * The retry loop simply tries the operation again. )-: It's not
1496 * pretty but an acceptable workaround until we work out what the
1497 * real problem is. */
1502 DBG_DEBUG("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1506 status
= domain
->backend
->query_user_list(domain
, mem_ctx
,
1508 num_rids
= talloc_array_length(rids
);
1510 if (!NT_STATUS_IS_OK(status
)) {
1511 DBG_NOTICE("query_user_list: returned 0x%08x, "
1512 "retrying\n", NT_STATUS_V(status
));
1514 reset_cm_connection_on_error(domain
, NULL
, status
);
1515 if (NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1516 DBG_NOTICE("query_user_list: flushing "
1517 "connection cache\n");
1518 invalidate_cm_connection(domain
);
1520 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1521 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1522 if (!domain
->internal
&& old_status
) {
1523 set_domain_offline(domain
);
1525 /* store partial response. */
1528 * humm, what about the status used for cache?
1529 * Should it be NT_STATUS_OK?
1534 * domain is offline now, and there is no user entries,
1535 * try to fetch from cache again.
1537 if (cache
->tdb
&& !domain
->online
&& !domain
->internal
&& old_status
) {
1538 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1539 /* partial response... */
1543 goto do_fetch_cache
;
1550 } while (NT_STATUS_V(status
) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL
) &&
1554 refresh_sequence_number(domain
);
1555 if (!NT_STATUS_IS_OK(status
)) {
1558 centry
= centry_start(domain
, status
);
1561 centry_put_uint32(centry
, num_rids
);
1562 for (i
=0; i
<num_rids
; i
++) {
1563 centry_put_uint32(centry
, rids
[i
]);
1565 centry_end(centry
, "UL/%s", domain
->name
);
1566 centry_free(centry
);
1574 /* list all domain groups */
1575 NTSTATUS
wb_cache_enum_dom_groups(struct winbindd_domain
*domain
,
1576 TALLOC_CTX
*mem_ctx
,
1577 uint32_t *num_entries
,
1578 struct wb_acct_info
**info
)
1580 struct winbind_cache
*cache
= get_cache(domain
);
1581 struct cache_entry
*centry
= NULL
;
1586 old_status
= domain
->online
;
1590 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1595 *num_entries
= centry_uint32(centry
);
1597 if (*num_entries
== 0)
1600 (*info
) = talloc_array(mem_ctx
, struct wb_acct_info
, *num_entries
);
1602 smb_panic_fn("enum_dom_groups out of memory");
1604 for (i
=0; i
<(*num_entries
); i
++) {
1605 (*info
)[i
].acct_name
= centry_string(centry
, (*info
));
1606 (*info
)[i
].acct_desc
= centry_string(centry
, (*info
));
1607 (*info
)[i
].rid
= centry_uint32(centry
);
1611 status
= centry
->status
;
1613 DBG_DEBUG("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1614 domain
->name
, nt_errstr(status
) );
1616 centry_free(centry
);
1623 DBG_DEBUG("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1626 status
= domain
->backend
->enum_dom_groups(domain
, mem_ctx
, num_entries
, info
);
1628 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1629 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1630 if (!domain
->internal
&& old_status
) {
1631 set_domain_offline(domain
);
1635 !domain
->internal
&&
1637 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1639 goto do_fetch_cache
;
1644 refresh_sequence_number(domain
);
1645 if (!NT_STATUS_IS_OK(status
)) {
1648 centry
= centry_start(domain
, status
);
1651 centry_put_uint32(centry
, *num_entries
);
1652 for (i
=0; i
<(*num_entries
); i
++) {
1653 centry_put_string(centry
, (*info
)[i
].acct_name
);
1654 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1655 centry_put_uint32(centry
, (*info
)[i
].rid
);
1657 centry_end(centry
, "GL/%s/domain", domain
->name
);
1658 centry_free(centry
);
1664 /* list all domain groups */
1665 NTSTATUS
wb_cache_enum_local_groups(struct winbindd_domain
*domain
,
1666 TALLOC_CTX
*mem_ctx
,
1667 uint32_t *num_entries
,
1668 struct wb_acct_info
**info
)
1670 struct winbind_cache
*cache
= get_cache(domain
);
1671 struct cache_entry
*centry
= NULL
;
1676 old_status
= domain
->online
;
1680 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1685 *num_entries
= centry_uint32(centry
);
1687 if (*num_entries
== 0)
1690 (*info
) = talloc_array(mem_ctx
, struct wb_acct_info
, *num_entries
);
1692 smb_panic_fn("enum_dom_groups out of memory");
1694 for (i
=0; i
<(*num_entries
); i
++) {
1695 (*info
)[i
].acct_name
= centry_string(centry
, (*info
));
1696 (*info
)[i
].acct_desc
= centry_string(centry
, (*info
));
1697 (*info
)[i
].rid
= centry_uint32(centry
);
1702 /* If we are returning cached data and the domain controller
1703 is down then we don't know whether the data is up to date
1704 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1707 if (wcache_server_down(domain
)) {
1708 DBG_DEBUG("enum_local_groups: returning cached user list and server was down\n");
1709 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1711 status
= centry
->status
;
1713 DBG_DEBUG("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1714 domain
->name
, nt_errstr(status
) );
1716 centry_free(centry
);
1723 DBG_DEBUG("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1726 status
= domain
->backend
->enum_local_groups(domain
, mem_ctx
, num_entries
, info
);
1728 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1729 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1730 if (!domain
->internal
&& old_status
) {
1731 set_domain_offline(domain
);
1734 !domain
->internal
&&
1737 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1739 goto do_fetch_cache
;
1744 refresh_sequence_number(domain
);
1745 if (!NT_STATUS_IS_OK(status
)) {
1748 centry
= centry_start(domain
, status
);
1751 centry_put_uint32(centry
, *num_entries
);
1752 for (i
=0; i
<(*num_entries
); i
++) {
1753 centry_put_string(centry
, (*info
)[i
].acct_name
);
1754 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1755 centry_put_uint32(centry
, (*info
)[i
].rid
);
1757 centry_end(centry
, "GL/%s/local", domain
->name
);
1758 centry_free(centry
);
1764 struct wcache_name_to_sid_state
{
1765 struct dom_sid
*sid
;
1766 enum lsa_SidType
*type
;
1771 static void wcache_name_to_sid_fn(const struct dom_sid
*sid
,
1772 enum lsa_SidType type
,
1776 struct wcache_name_to_sid_state
*state
= private_data
;
1779 *state
->type
= type
;
1780 state
->found
= (!expired
|| state
->offline
);
1783 static NTSTATUS
wcache_name_to_sid(struct winbindd_domain
*domain
,
1784 const char *domain_name
,
1786 struct dom_sid
*sid
,
1787 enum lsa_SidType
*type
)
1789 struct wcache_name_to_sid_state state
= {
1790 .sid
= sid
, .type
= type
, .found
= false,
1791 .offline
= is_domain_offline(domain
),
1795 ok
= namemap_cache_find_name(domain_name
, name
, wcache_name_to_sid_fn
,
1798 DBG_DEBUG("namemap_cache_find_name failed\n");
1799 return NT_STATUS_NOT_FOUND
;
1802 DBG_DEBUG("cache entry not found\n");
1803 return NT_STATUS_NOT_FOUND
;
1806 return NT_STATUS_OK
;
1809 /* convert a single name to a sid in a domain */
1810 NTSTATUS
wb_cache_name_to_sid(struct winbindd_domain
*domain
,
1811 TALLOC_CTX
*mem_ctx
,
1812 const char *domain_name
,
1815 struct dom_sid
*sid
,
1816 enum lsa_SidType
*type
)
1820 const char *dom_name
;
1822 was_online
= domain
->online
;
1825 *type
= SID_NAME_UNKNOWN
;
1827 status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
1828 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
1832 DBG_DEBUG("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1835 winbindd_domain_init_backend(domain
);
1836 status
= domain
->backend
->name_to_sid(domain
, mem_ctx
, domain_name
,
1837 name
, flags
, &dom_name
, sid
, type
);
1839 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1840 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
))
1842 if (!domain
->internal
&& was_online
) {
1843 /* Set the domain offline and query the cache again */
1844 set_domain_offline(domain
);
1845 return wcache_name_to_sid(domain
,
1854 if (domain
->online
&& NT_STATUS_IS_OK(status
)) {
1855 wcache_save_name_to_sid(domain
, status
, domain_name
, name
, sid
,
1858 /* Only save the reverse mapping if this was not a UPN */
1859 if (!strchr(name
, '@')) {
1860 if (!strupper_m(discard_const_p(char, domain_name
))) {
1861 return NT_STATUS_INVALID_PARAMETER
;
1863 (void)strlower_m(discard_const_p(char, name
));
1864 wcache_save_sid_to_name(domain
, status
, sid
,
1865 dom_name
, name
, *type
);
1872 struct wcache_sid_to_name_state
{
1873 TALLOC_CTX
*mem_ctx
;
1876 enum lsa_SidType
*type
;
1881 static void wcache_sid_to_name_fn(const char *domain
,
1883 enum lsa_SidType type
,
1887 struct wcache_sid_to_name_state
*state
= private_data
;
1889 *state
->domain_name
= talloc_strdup(state
->mem_ctx
, domain
);
1890 if (*state
->domain_name
== NULL
) {
1893 *state
->name
= talloc_strdup(state
->mem_ctx
, name
);
1894 if (*state
->name
== NULL
) {
1897 *state
->type
= type
;
1898 state
->found
= (!expired
|| state
->offline
);
1901 static NTSTATUS
wcache_sid_to_name(struct winbindd_domain
*domain
,
1902 const struct dom_sid
*sid
,
1903 TALLOC_CTX
*mem_ctx
,
1906 enum lsa_SidType
*type
)
1908 struct wcache_sid_to_name_state state
= {
1909 .mem_ctx
= mem_ctx
, .found
= false,
1910 .domain_name
= domain_name
, .name
= name
, .type
= type
,
1911 .offline
= is_domain_offline(domain
)
1915 ok
= namemap_cache_find_sid(sid
, wcache_sid_to_name_fn
, &state
);
1917 DBG_DEBUG("namemap_cache_find_name failed\n");
1918 return NT_STATUS_NOT_FOUND
;
1921 DBG_DEBUG("cache entry not found\n");
1922 return NT_STATUS_NOT_FOUND
;
1924 if (*type
== SID_NAME_UNKNOWN
) {
1925 return NT_STATUS_NONE_MAPPED
;
1928 return NT_STATUS_OK
;
1931 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1933 NTSTATUS
wb_cache_sid_to_name(struct winbindd_domain
*domain
,
1934 TALLOC_CTX
*mem_ctx
,
1935 const struct dom_sid
*sid
,
1938 enum lsa_SidType
*type
)
1943 old_status
= domain
->online
;
1944 status
= wcache_sid_to_name(domain
, sid
, mem_ctx
, domain_name
, name
,
1946 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
1951 *domain_name
= NULL
;
1953 DBG_DEBUG("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1956 winbindd_domain_init_backend(domain
);
1958 status
= domain
->backend
->sid_to_name(domain
, mem_ctx
, sid
, domain_name
, name
, type
);
1960 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1961 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1962 if (!domain
->internal
&& old_status
) {
1963 set_domain_offline(domain
);
1965 if (!domain
->internal
&&
1968 NTSTATUS cache_status
;
1969 cache_status
= wcache_sid_to_name(domain
, sid
, mem_ctx
,
1970 domain_name
, name
, type
);
1971 return cache_status
;
1975 if (!NT_STATUS_IS_OK(status
)) {
1978 wcache_save_sid_to_name(domain
, status
, sid
, *domain_name
, *name
, *type
);
1980 /* We can't save the name to sid mapping here, as with sid history a
1981 * later name2sid would give the wrong sid. */
1986 NTSTATUS
wb_cache_rids_to_names(struct winbindd_domain
*domain
,
1987 TALLOC_CTX
*mem_ctx
,
1988 const struct dom_sid
*domain_sid
,
1993 enum lsa_SidType
**types
)
1995 struct winbind_cache
*cache
= get_cache(domain
);
1997 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2002 old_status
= domain
->online
;
2003 *domain_name
= NULL
;
2011 if (num_rids
== 0) {
2012 return NT_STATUS_OK
;
2015 *names
= talloc_array(mem_ctx
, char *, num_rids
);
2016 *types
= talloc_array(mem_ctx
, enum lsa_SidType
, num_rids
);
2018 if ((*names
== NULL
) || (*types
== NULL
)) {
2019 result
= NT_STATUS_NO_MEMORY
;
2023 have_mapped
= have_unmapped
= false;
2025 for (i
=0; i
<num_rids
; i
++) {
2028 enum lsa_SidType type
;
2031 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2032 result
= NT_STATUS_INTERNAL_ERROR
;
2036 status
= wcache_sid_to_name(domain
, &sid
, *names
, &dom
,
2039 (*types
)[i
] = SID_NAME_UNKNOWN
;
2040 (*names
)[i
] = talloc_strdup(*names
, "");
2042 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2047 if (NT_STATUS_IS_OK(status
)) {
2051 if (*domain_name
== NULL
) {
2059 } else if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
2060 have_unmapped
= true;
2062 /* something's definitely wrong */
2069 return NT_STATUS_NONE_MAPPED
;
2071 if (!have_unmapped
) {
2072 return NT_STATUS_OK
;
2074 return STATUS_SOME_UNMAPPED
;
2078 TALLOC_FREE(*names
);
2079 TALLOC_FREE(*types
);
2081 result
= domain
->backend
->rids_to_names(domain
, mem_ctx
, domain_sid
,
2082 rids
, num_rids
, domain_name
,
2085 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
2086 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2087 if (!domain
->internal
&& old_status
) {
2088 set_domain_offline(domain
);
2091 !domain
->internal
&&
2094 have_mapped
= have_unmapped
= false;
2096 *names
= talloc_array(mem_ctx
, char *, num_rids
);
2097 if (*names
== NULL
) {
2098 result
= NT_STATUS_NO_MEMORY
;
2102 *types
= talloc_array(mem_ctx
, enum lsa_SidType
,
2104 if (*types
== NULL
) {
2105 result
= NT_STATUS_NO_MEMORY
;
2109 for (i
=0; i
<num_rids
; i
++) {
2112 enum lsa_SidType type
;
2115 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2116 result
= NT_STATUS_INTERNAL_ERROR
;
2120 status
= wcache_sid_to_name(domain
, &sid
,
2124 (*types
)[i
] = SID_NAME_UNKNOWN
;
2125 (*names
)[i
] = talloc_strdup(*names
, "");
2127 if (NT_STATUS_IS_OK(status
)) {
2131 if (*domain_name
== NULL
) {
2139 } else if (NT_STATUS_EQUAL(
2141 NT_STATUS_NONE_MAPPED
)) {
2142 have_unmapped
= true;
2144 /* something's definitely wrong */
2151 return NT_STATUS_NONE_MAPPED
;
2153 if (!have_unmapped
) {
2154 return NT_STATUS_OK
;
2156 return STATUS_SOME_UNMAPPED
;
2160 None of the queried rids has been found so save all negative entries
2162 if (NT_STATUS_EQUAL(result
, NT_STATUS_NONE_MAPPED
)) {
2163 for (i
= 0; i
< num_rids
; i
++) {
2165 const char *name
= "";
2166 const enum lsa_SidType type
= SID_NAME_UNKNOWN
;
2167 NTSTATUS status
= NT_STATUS_NONE_MAPPED
;
2169 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2170 return NT_STATUS_INTERNAL_ERROR
;
2173 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
2181 Some or all of the queried rids have been found.
2183 if (!NT_STATUS_IS_OK(result
) &&
2184 !NT_STATUS_EQUAL(result
, STATUS_SOME_UNMAPPED
)) {
2188 refresh_sequence_number(domain
);
2190 for (i
=0; i
<num_rids
; i
++) {
2194 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2195 result
= NT_STATUS_INTERNAL_ERROR
;
2199 status
= (*types
)[i
] == SID_NAME_UNKNOWN
?
2200 NT_STATUS_NONE_MAPPED
: NT_STATUS_OK
;
2202 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
2203 (*names
)[i
], (*types
)[i
]);
2209 TALLOC_FREE(*names
);
2210 TALLOC_FREE(*types
);
2214 static NTSTATUS
wcache_query_user(struct winbindd_domain
*domain
,
2215 TALLOC_CTX
*mem_ctx
,
2216 const struct dom_sid
*user_sid
,
2217 struct wbint_userinfo
*info
)
2219 struct winbind_cache
*cache
= get_cache(domain
);
2220 struct cache_entry
*centry
= NULL
;
2222 struct dom_sid_buf sid_string
;
2224 if (cache
->tdb
== NULL
) {
2225 return NT_STATUS_NOT_FOUND
;
2228 centry
= wcache_fetch(
2229 cache
, domain
, "U/%s", dom_sid_str_buf(user_sid
, &sid_string
));
2230 if (centry
== NULL
) {
2231 return NT_STATUS_NOT_FOUND
;
2234 /* if status is not ok then this is a negative hit
2235 and the rest of the data doesn't matter */
2236 status
= centry
->status
;
2237 if (NT_STATUS_IS_OK(status
)) {
2238 info
->domain_name
= centry_string(centry
, mem_ctx
);
2239 info
->acct_name
= centry_string(centry
, mem_ctx
);
2240 info
->full_name
= centry_string(centry
, mem_ctx
);
2241 info
->homedir
= centry_string(centry
, mem_ctx
);
2242 info
->shell
= centry_string(centry
, mem_ctx
);
2243 info
->uid
= centry_uint32(centry
);
2244 info
->primary_gid
= centry_uint32(centry
);
2245 info
->primary_group_name
= centry_string(centry
, mem_ctx
);
2246 centry_sid(centry
, &info
->user_sid
);
2247 centry_sid(centry
, &info
->group_sid
);
2250 DBG_DEBUG("query_user: [Cached] - cached info for domain %s status: "
2251 "%s\n", domain
->name
, nt_errstr(status
) );
2253 centry_free(centry
);
2259 * @brief Query a fullname from the username cache (for further gecos processing)
2261 * @param domain A pointer to the winbindd_domain struct.
2262 * @param mem_ctx The talloc context.
2263 * @param user_sid The user sid.
2264 * @param full_name A pointer to the full_name string.
2266 * @return NTSTATUS code
2268 NTSTATUS
wcache_query_user_fullname(struct winbindd_domain
*domain
,
2269 TALLOC_CTX
*mem_ctx
,
2270 const struct dom_sid
*user_sid
,
2271 const char **full_name
)
2274 struct wbint_userinfo info
;
2276 status
= wcache_query_user(domain
, mem_ctx
, user_sid
, &info
);
2277 if (!NT_STATUS_IS_OK(status
)) {
2281 if (info
.full_name
!= NULL
) {
2282 *full_name
= talloc_strdup(mem_ctx
, info
.full_name
);
2283 if (*full_name
== NULL
) {
2284 return NT_STATUS_NO_MEMORY
;
2288 return NT_STATUS_OK
;
2291 static NTSTATUS
wcache_lookup_usergroups(struct winbindd_domain
*domain
,
2292 TALLOC_CTX
*mem_ctx
,
2293 const struct dom_sid
*user_sid
,
2294 uint32_t *pnum_sids
,
2295 struct dom_sid
**psids
)
2297 struct winbind_cache
*cache
= get_cache(domain
);
2298 struct cache_entry
*centry
= NULL
;
2300 uint32_t i
, num_sids
;
2301 struct dom_sid
*sids
;
2302 struct dom_sid_buf sid_string
;
2304 if (cache
->tdb
== NULL
) {
2305 return NT_STATUS_NOT_FOUND
;
2308 centry
= wcache_fetch(
2312 dom_sid_str_buf(user_sid
, &sid_string
));
2313 if (centry
== NULL
) {
2314 return NT_STATUS_NOT_FOUND
;
2317 num_sids
= centry_uint32(centry
);
2318 sids
= talloc_array(mem_ctx
, struct dom_sid
, num_sids
);
2320 centry_free(centry
);
2321 return NT_STATUS_NO_MEMORY
;
2324 for (i
=0; i
<num_sids
; i
++) {
2325 centry_sid(centry
, &sids
[i
]);
2328 status
= centry
->status
;
2330 DBG_DEBUG("lookup_usergroups: [Cached] - cached info for domain %s "
2331 "status: %s\n", domain
->name
, nt_errstr(status
));
2333 centry_free(centry
);
2335 *pnum_sids
= num_sids
;
2340 /* Lookup groups a user is a member of. */
2341 NTSTATUS
wb_cache_lookup_usergroups(struct winbindd_domain
*domain
,
2342 TALLOC_CTX
*mem_ctx
,
2343 const struct dom_sid
*user_sid
,
2344 uint32_t *num_groups
,
2345 struct dom_sid
**user_gids
)
2347 struct cache_entry
*centry
= NULL
;
2350 struct dom_sid_buf sid_string
;
2353 old_status
= domain
->online
;
2354 status
= wcache_lookup_usergroups(domain
, mem_ctx
, user_sid
,
2355 num_groups
, user_gids
);
2356 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2361 (*user_gids
) = NULL
;
2363 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2366 status
= domain
->backend
->lookup_usergroups(domain
, mem_ctx
, user_sid
, num_groups
, user_gids
);
2368 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2369 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2370 if (!domain
->internal
&& old_status
) {
2371 set_domain_offline(domain
);
2373 if (!domain
->internal
&&
2376 NTSTATUS cache_status
;
2377 cache_status
= wcache_lookup_usergroups(domain
, mem_ctx
, user_sid
,
2378 num_groups
, user_gids
);
2379 return cache_status
;
2382 if ( NT_STATUS_EQUAL(status
, NT_STATUS_SYNCHRONIZATION_REQUIRED
) )
2386 refresh_sequence_number(domain
);
2387 if (!NT_STATUS_IS_OK(status
)) {
2390 centry
= centry_start(domain
, status
);
2394 centry_put_uint32(centry
, *num_groups
);
2395 for (i
=0; i
<(*num_groups
); i
++) {
2396 centry_put_sid(centry
, &(*user_gids
)[i
]);
2399 centry_end(centry
, "UG/%s", dom_sid_str_buf(user_sid
, &sid_string
));
2400 centry_free(centry
);
2406 static char *wcache_make_sidlist(TALLOC_CTX
*mem_ctx
, uint32_t num_sids
,
2407 const struct dom_sid
*sids
)
2412 sidlist
= talloc_strdup(mem_ctx
, "");
2413 if (sidlist
== NULL
) {
2416 for (i
=0; i
<num_sids
; i
++) {
2417 struct dom_sid_buf tmp
;
2418 sidlist
= talloc_asprintf_append_buffer(
2421 dom_sid_str_buf(&sids
[i
], &tmp
));
2422 if (sidlist
== NULL
) {
2429 static NTSTATUS
wcache_lookup_useraliases(struct winbindd_domain
*domain
,
2430 TALLOC_CTX
*mem_ctx
,
2432 const struct dom_sid
*sids
,
2433 uint32_t *pnum_aliases
,
2434 uint32_t **paliases
)
2436 struct winbind_cache
*cache
= get_cache(domain
);
2437 struct cache_entry
*centry
= NULL
;
2438 uint32_t i
, num_aliases
;
2443 if (cache
->tdb
== NULL
) {
2444 return NT_STATUS_NOT_FOUND
;
2447 if (num_sids
== 0) {
2450 return NT_STATUS_OK
;
2453 /* We need to cache indexed by the whole list of SIDs, the aliases
2454 * resulting might come from any of the SIDs. */
2456 sidlist
= wcache_make_sidlist(talloc_tos(), num_sids
, sids
);
2457 if (sidlist
== NULL
) {
2458 return NT_STATUS_NO_MEMORY
;
2461 centry
= wcache_fetch(cache
, domain
, "UA%s", sidlist
);
2462 TALLOC_FREE(sidlist
);
2463 if (centry
== NULL
) {
2464 return NT_STATUS_NOT_FOUND
;
2467 num_aliases
= centry_uint32(centry
);
2468 aliases
= talloc_array(mem_ctx
, uint32_t, num_aliases
);
2469 if (aliases
== NULL
) {
2470 centry_free(centry
);
2471 return NT_STATUS_NO_MEMORY
;
2474 for (i
=0; i
<num_aliases
; i
++) {
2475 aliases
[i
] = centry_uint32(centry
);
2478 status
= centry
->status
;
2480 DBG_DEBUG("lookup_useraliases: [Cached] - cached info for domain: %s "
2481 "status %s\n", domain
->name
, nt_errstr(status
));
2483 centry_free(centry
);
2485 *pnum_aliases
= num_aliases
;
2486 *paliases
= aliases
;
2491 NTSTATUS
wb_cache_lookup_useraliases(struct winbindd_domain
*domain
,
2492 TALLOC_CTX
*mem_ctx
,
2494 const struct dom_sid
*sids
,
2495 uint32_t *num_aliases
,
2496 uint32_t **alias_rids
)
2498 struct cache_entry
*centry
= NULL
;
2504 old_status
= domain
->online
;
2505 status
= wcache_lookup_useraliases(domain
, mem_ctx
, num_sids
, sids
,
2506 num_aliases
, alias_rids
);
2507 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2512 (*alias_rids
) = NULL
;
2514 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info "
2515 "for domain %s\n", domain
->name
);
2517 sidlist
= wcache_make_sidlist(talloc_tos(), num_sids
, sids
);
2518 if (sidlist
== NULL
) {
2519 return NT_STATUS_NO_MEMORY
;
2522 status
= domain
->backend
->lookup_useraliases(domain
, mem_ctx
,
2524 num_aliases
, alias_rids
);
2526 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2527 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2528 if (!domain
->internal
&& old_status
) {
2529 set_domain_offline(domain
);
2531 if (!domain
->internal
&&
2534 NTSTATUS cache_status
;
2535 cache_status
= wcache_lookup_useraliases(domain
, mem_ctx
, num_sids
,
2536 sids
, num_aliases
, alias_rids
);
2537 return cache_status
;
2541 refresh_sequence_number(domain
);
2542 if (!NT_STATUS_IS_OK(status
)) {
2545 centry
= centry_start(domain
, status
);
2548 centry_put_uint32(centry
, *num_aliases
);
2549 for (i
=0; i
<(*num_aliases
); i
++)
2550 centry_put_uint32(centry
, (*alias_rids
)[i
]);
2551 centry_end(centry
, "UA%s", sidlist
);
2552 centry_free(centry
);
2558 static NTSTATUS
wcache_lookup_aliasmem(struct winbindd_domain
*domain
,
2559 TALLOC_CTX
*mem_ctx
,
2560 const struct dom_sid
*group_sid
,
2561 uint32_t *num_names
,
2562 struct dom_sid
**sid_mem
)
2564 struct winbind_cache
*cache
= get_cache(domain
);
2565 struct cache_entry
*centry
= NULL
;
2568 struct dom_sid_buf sid_string
;
2570 if (cache
->tdb
== NULL
) {
2571 return NT_STATUS_NOT_FOUND
;
2574 centry
= wcache_fetch(cache
,
2577 dom_sid_str_buf(group_sid
, &sid_string
));
2578 if (centry
== NULL
) {
2579 return NT_STATUS_NOT_FOUND
;
2584 *num_names
= centry_uint32(centry
);
2585 if (*num_names
== 0) {
2586 centry_free(centry
);
2587 return NT_STATUS_OK
;
2590 *sid_mem
= talloc_array(mem_ctx
, struct dom_sid
, *num_names
);
2591 if (*sid_mem
== NULL
) {
2592 TALLOC_FREE(*sid_mem
);
2593 centry_free(centry
);
2594 return NT_STATUS_NO_MEMORY
;
2597 for (i
= 0; i
< (*num_names
); i
++) {
2598 centry_sid(centry
, &(*sid_mem
)[i
]);
2601 status
= centry
->status
;
2603 D_DEBUG("[Cached] - cached info for domain %s "
2608 centry_free(centry
);
2612 NTSTATUS
wb_cache_lookup_aliasmem(struct winbindd_domain
*domain
,
2613 TALLOC_CTX
*mem_ctx
,
2614 const struct dom_sid
*group_sid
,
2615 enum lsa_SidType type
,
2617 struct dom_sid
**sid_mem
)
2619 struct cache_entry
*centry
= NULL
;
2622 struct dom_sid_buf sid_string
;
2625 old_status
= domain
->online
;
2626 status
= wcache_lookup_aliasmem(domain
,
2631 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2638 D_DEBUG("[Cached] - doing backend query for info for domain %s\n",
2641 status
= domain
->backend
->lookup_aliasmem(domain
,
2648 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2649 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2650 if (!domain
->internal
&& old_status
) {
2651 set_domain_offline(domain
);
2653 if (!domain
->internal
&& !domain
->online
&& old_status
) {
2654 NTSTATUS cache_status
;
2655 cache_status
= wcache_lookup_aliasmem(domain
,
2660 return cache_status
;
2664 refresh_sequence_number(domain
);
2665 if (!NT_STATUS_IS_OK(status
)) {
2668 centry
= centry_start(domain
, status
);
2671 centry_put_uint32(centry
, *num_sids
);
2672 for (i
= 0; i
< (*num_sids
); i
++) {
2673 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2675 centry_end(centry
, "AM/%s", dom_sid_str_buf(group_sid
, &sid_string
));
2676 centry_free(centry
);
2682 static NTSTATUS
wcache_lookup_groupmem(struct winbindd_domain
*domain
,
2683 TALLOC_CTX
*mem_ctx
,
2684 const struct dom_sid
*group_sid
,
2685 uint32_t *num_names
,
2686 struct dom_sid
**sid_mem
, char ***names
,
2687 uint32_t **name_types
)
2689 struct winbind_cache
*cache
= get_cache(domain
);
2690 struct cache_entry
*centry
= NULL
;
2693 struct dom_sid_buf sid_string
;
2695 if (cache
->tdb
== NULL
) {
2696 return NT_STATUS_NOT_FOUND
;
2699 centry
= wcache_fetch(
2703 dom_sid_str_buf(group_sid
, &sid_string
));
2704 if (centry
== NULL
) {
2705 return NT_STATUS_NOT_FOUND
;
2712 *num_names
= centry_uint32(centry
);
2713 if (*num_names
== 0) {
2714 centry_free(centry
);
2715 return NT_STATUS_OK
;
2718 *sid_mem
= talloc_array(mem_ctx
, struct dom_sid
, *num_names
);
2719 *names
= talloc_array(mem_ctx
, char *, *num_names
);
2720 *name_types
= talloc_array(mem_ctx
, uint32_t, *num_names
);
2722 if ((*sid_mem
== NULL
) || (*names
== NULL
) || (*name_types
== NULL
)) {
2723 TALLOC_FREE(*sid_mem
);
2724 TALLOC_FREE(*names
);
2725 TALLOC_FREE(*name_types
);
2726 centry_free(centry
);
2727 return NT_STATUS_NO_MEMORY
;
2730 for (i
=0; i
<(*num_names
); i
++) {
2731 centry_sid(centry
, &(*sid_mem
)[i
]);
2732 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2733 (*name_types
)[i
] = centry_uint32(centry
);
2736 status
= centry
->status
;
2738 DBG_DEBUG("lookup_groupmem: [Cached] - cached info for domain %s "
2739 "status: %s\n", domain
->name
, nt_errstr(status
));
2741 centry_free(centry
);
2745 NTSTATUS
wb_cache_lookup_groupmem(struct winbindd_domain
*domain
,
2746 TALLOC_CTX
*mem_ctx
,
2747 const struct dom_sid
*group_sid
,
2748 enum lsa_SidType type
,
2749 uint32_t *num_names
,
2750 struct dom_sid
**sid_mem
,
2752 uint32_t **name_types
)
2754 struct cache_entry
*centry
= NULL
;
2757 struct dom_sid_buf sid_string
;
2760 old_status
= domain
->online
;
2761 status
= wcache_lookup_groupmem(domain
, mem_ctx
, group_sid
, num_names
,
2762 sid_mem
, names
, name_types
);
2763 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2770 (*name_types
) = NULL
;
2772 DBG_DEBUG("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2775 status
= domain
->backend
->lookup_groupmem(domain
, mem_ctx
, group_sid
,
2777 sid_mem
, names
, name_types
);
2779 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2780 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2781 if (!domain
->internal
&& old_status
) {
2782 set_domain_offline(domain
);
2784 if (!domain
->internal
&&
2787 NTSTATUS cache_status
;
2788 cache_status
= wcache_lookup_groupmem(domain
, mem_ctx
, group_sid
,
2789 num_names
, sid_mem
, names
,
2791 return cache_status
;
2795 refresh_sequence_number(domain
);
2796 if (!NT_STATUS_IS_OK(status
)) {
2799 centry
= centry_start(domain
, status
);
2802 centry_put_uint32(centry
, *num_names
);
2803 for (i
=0; i
<(*num_names
); i
++) {
2804 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2805 centry_put_string(centry
, (*names
)[i
]);
2806 centry_put_uint32(centry
, (*name_types
)[i
]);
2810 dom_sid_str_buf(group_sid
, &sid_string
));
2811 centry_free(centry
);
2817 /* find the sequence number for a domain */
2818 NTSTATUS
wb_cache_sequence_number(struct winbindd_domain
*domain
,
2821 refresh_sequence_number(domain
);
2823 *seq
= domain
->sequence_number
;
2825 return NT_STATUS_OK
;
2828 /* enumerate trusted domains
2829 * (we need to have the list of trustdoms in the cache when we go offline) -
2831 NTSTATUS
wb_cache_trusted_domains(struct winbindd_domain
*domain
,
2832 TALLOC_CTX
*mem_ctx
,
2833 struct netr_DomainTrustList
*trusts
)
2836 struct winbind_cache
*cache
;
2837 struct winbindd_tdc_domain
*dom_list
= NULL
;
2838 size_t num_domains
= 0;
2839 bool retval
= false;
2843 old_status
= domain
->online
;
2845 trusts
->array
= NULL
;
2847 cache
= get_cache(domain
);
2848 if (!cache
|| !cache
->tdb
) {
2852 if (domain
->online
) {
2856 retval
= wcache_tdc_fetch_list(&dom_list
, &num_domains
);
2857 if (!retval
|| !num_domains
|| !dom_list
) {
2858 TALLOC_FREE(dom_list
);
2863 trusts
->array
= talloc_zero_array(mem_ctx
, struct netr_DomainTrust
, num_domains
);
2864 if (!trusts
->array
) {
2865 TALLOC_FREE(dom_list
);
2866 return NT_STATUS_NO_MEMORY
;
2869 for (i
= 0; i
< num_domains
; i
++) {
2870 struct netr_DomainTrust
*trust
;
2871 struct dom_sid
*sid
;
2872 struct winbindd_domain
*dom
;
2874 dom
= find_domain_from_name_noinit(dom_list
[i
].domain_name
);
2875 if (dom
&& dom
->internal
) {
2879 trust
= &trusts
->array
[trusts
->count
];
2880 trust
->netbios_name
= talloc_strdup(trusts
->array
, dom_list
[i
].domain_name
);
2881 trust
->dns_name
= talloc_strdup(trusts
->array
, dom_list
[i
].dns_name
);
2882 sid
= talloc(trusts
->array
, struct dom_sid
);
2883 if (!trust
->netbios_name
|| !trust
->dns_name
||
2885 TALLOC_FREE(dom_list
);
2886 TALLOC_FREE(trusts
->array
);
2887 return NT_STATUS_NO_MEMORY
;
2890 trust
->trust_flags
= dom_list
[i
].trust_flags
;
2891 trust
->trust_attributes
= dom_list
[i
].trust_attribs
;
2892 trust
->trust_type
= dom_list
[i
].trust_type
;
2893 sid_copy(sid
, &dom_list
[i
].sid
);
2898 TALLOC_FREE(dom_list
);
2899 return NT_STATUS_OK
;
2902 DBG_DEBUG("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2905 status
= domain
->backend
->trusted_domains(domain
, mem_ctx
, trusts
);
2907 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2908 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2909 if (!domain
->internal
&& old_status
) {
2910 set_domain_offline(domain
);
2912 if (!domain
->internal
&&
2915 retval
= wcache_tdc_fetch_list(&dom_list
, &num_domains
);
2916 if (retval
&& num_domains
&& dom_list
) {
2917 TALLOC_FREE(trusts
->array
);
2919 goto do_fetch_cache
;
2923 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2924 * so that the generic centry handling still applies correctly -
2927 if (!NT_STATUS_IS_ERR(status
)) {
2928 status
= NT_STATUS_OK
;
2933 /* get lockout policy */
2934 NTSTATUS
wb_cache_lockout_policy(struct winbindd_domain
*domain
,
2935 TALLOC_CTX
*mem_ctx
,
2936 struct samr_DomInfo12
*policy
)
2938 struct winbind_cache
*cache
= get_cache(domain
);
2939 struct cache_entry
*centry
= NULL
;
2943 old_status
= domain
->online
;
2947 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2953 policy
->lockout_duration
= centry_nttime(centry
);
2954 policy
->lockout_window
= centry_nttime(centry
);
2955 policy
->lockout_threshold
= centry_uint16(centry
);
2957 status
= centry
->status
;
2959 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2960 domain
->name
, nt_errstr(status
) );
2962 centry_free(centry
);
2966 ZERO_STRUCTP(policy
);
2968 DBG_DEBUG("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2971 status
= domain
->backend
->lockout_policy(domain
, mem_ctx
, policy
);
2973 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2974 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2975 if (!domain
->internal
&& old_status
) {
2976 set_domain_offline(domain
);
2979 !domain
->internal
&&
2982 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2984 goto do_fetch_cache
;
2989 refresh_sequence_number(domain
);
2990 if (!NT_STATUS_IS_OK(status
)) {
2993 wcache_save_lockout_policy(domain
, status
, policy
);
2998 /* get password policy */
2999 NTSTATUS
wb_cache_password_policy(struct winbindd_domain
*domain
,
3000 TALLOC_CTX
*mem_ctx
,
3001 struct samr_DomInfo1
*policy
)
3003 struct winbind_cache
*cache
= get_cache(domain
);
3004 struct cache_entry
*centry
= NULL
;
3008 old_status
= domain
->online
;
3012 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
3018 policy
->min_password_length
= centry_uint16(centry
);
3019 policy
->password_history_length
= centry_uint16(centry
);
3020 policy
->password_properties
= centry_uint32(centry
);
3021 policy
->max_password_age
= centry_nttime(centry
);
3022 policy
->min_password_age
= centry_nttime(centry
);
3024 status
= centry
->status
;
3026 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3027 domain
->name
, nt_errstr(status
) );
3029 centry_free(centry
);
3033 ZERO_STRUCTP(policy
);
3035 DBG_DEBUG("password_policy: [Cached] - doing backend query for info for domain %s\n",
3038 status
= domain
->backend
->password_policy(domain
, mem_ctx
, policy
);
3040 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
3041 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
3042 if (!domain
->internal
&& old_status
) {
3043 set_domain_offline(domain
);
3046 !domain
->internal
&&
3049 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
3051 goto do_fetch_cache
;
3056 refresh_sequence_number(domain
);
3057 if (!NT_STATUS_IS_OK(status
)) {
3060 wcache_save_password_policy(domain
, status
, policy
);
3066 /* Invalidate cached user and group lists coherently */
3068 static int traverse_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3071 if (strncmp((const char *)kbuf
.dptr
, "UL/", 3) == 0 ||
3072 strncmp((const char *)kbuf
.dptr
, "GL/", 3) == 0)
3073 tdb_delete(the_tdb
, kbuf
);
3078 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3080 void wcache_invalidate_samlogon(struct winbindd_domain
*domain
,
3081 const struct dom_sid
*sid
)
3084 struct dom_sid_buf sid_string
;
3085 struct winbind_cache
*cache
;
3087 /* don't clear cached U/SID and UG/SID entries when we want to logon
3090 if (lp_winbind_offline_logon()) {
3097 cache
= get_cache(domain
);
3103 /* Clear U/SID cache entry */
3104 fstr_sprintf(key_str
, "U/%s", dom_sid_str_buf(sid
, &sid_string
));
3105 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str
);
3106 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3108 /* Clear UG/SID cache entry */
3109 fstr_sprintf(key_str
, "UG/%s", dom_sid_str_buf(sid
, &sid_string
));
3110 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str
);
3111 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3113 /* Samba/winbindd never needs this. */
3114 netsamlogon_clear_cached_user(sid
);
3117 bool wcache_invalidate_cache(void)
3119 struct winbindd_domain
*domain
;
3121 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
3122 struct winbind_cache
*cache
= get_cache(domain
);
3124 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3125 "entries for %s\n", domain
->name
);
3128 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
3137 bool wcache_invalidate_cache_noinit(void)
3139 struct winbindd_domain
*domain
;
3141 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
3142 struct winbind_cache
*cache
;
3144 /* Skip uninitialized domains. */
3145 if (!domain
->initialized
&& !domain
->internal
) {
3149 cache
= get_cache(domain
);
3151 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3152 "entries for %s\n", domain
->name
);
3155 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
3157 * Flushing cache has nothing to with domains.
3158 * return here if we successfully flushed once.
3159 * To avoid unnecessary traversing the cache.
3170 static bool init_wcache(void)
3174 if (wcache
== NULL
) {
3175 wcache
= SMB_XMALLOC_P(struct winbind_cache
);
3176 ZERO_STRUCTP(wcache
);
3179 if (wcache
->tdb
!= NULL
)
3182 db_path
= wcache_path();
3183 if (db_path
== NULL
) {
3187 /* when working offline we must not clear the cache on restart */
3188 wcache
->tdb
= tdb_open_log(db_path
,
3189 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3190 TDB_INCOMPATIBLE_HASH
|
3191 (lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
)),
3192 O_RDWR
|O_CREAT
, 0600);
3193 TALLOC_FREE(db_path
);
3194 if (wcache
->tdb
== NULL
) {
3195 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3202 /************************************************************************
3203 This is called by the parent to initialize the cache file.
3204 We don't need sophisticated locking here as we know we're the
3206 ************************************************************************/
3208 bool initialize_winbindd_cache(void)
3210 bool cache_bad
= false;
3214 if (!init_wcache()) {
3215 DBG_ERR("initialize_winbindd_cache: init_wcache failed.\n");
3219 /* Check version number. */
3220 ok
= tdb_fetch_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers
);
3222 DBG_DEBUG("Failed to get cache version\n");
3225 if (vers
!= WINBINDD_CACHE_VERSION
) {
3226 DBG_DEBUG("Invalid cache version %u != %u\n",
3228 WINBINDD_CACHE_VERSION
);
3235 DBG_NOTICE("initialize_winbindd_cache: clearing cache "
3236 "and re-creating with version number %d\n",
3237 WINBINDD_CACHE_VERSION
);
3239 tdb_close(wcache
->tdb
);
3242 db_path
= wcache_path();
3243 if (db_path
== NULL
) {
3247 if (unlink(db_path
) == -1) {
3248 DBG_ERR("initialize_winbindd_cache: unlink %s failed %s\n",
3251 TALLOC_FREE(db_path
);
3254 TALLOC_FREE(db_path
);
3255 if (!init_wcache()) {
3256 DBG_ERR("initialize_winbindd_cache: re-initialization "
3257 "init_wcache failed.\n");
3261 /* Write the version. */
3262 if (!tdb_store_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
)) {
3263 DBG_ERR("initialize_winbindd_cache: version number store failed %s\n",
3264 tdb_errorstr(wcache
->tdb
) );
3269 tdb_close(wcache
->tdb
);
3274 void close_winbindd_cache(void)
3280 tdb_close(wcache
->tdb
);
3285 bool lookup_cached_sid(TALLOC_CTX
*mem_ctx
, const struct dom_sid
*sid
,
3286 char **domain_name
, char **name
,
3287 enum lsa_SidType
*type
)
3289 struct winbindd_domain
*domain
;
3292 domain
= find_lookup_domain_from_sid(sid
);
3293 if (domain
== NULL
) {
3296 status
= wcache_sid_to_name(domain
, sid
, mem_ctx
, domain_name
, name
,
3298 return NT_STATUS_IS_OK(status
);
3301 bool lookup_cached_name(const char *namespace,
3302 const char *domain_name
,
3304 struct dom_sid
*sid
,
3305 enum lsa_SidType
*type
)
3307 struct winbindd_domain
*domain
;
3309 bool original_online_state
;
3311 domain
= find_lookup_domain_from_name(namespace);
3312 if (domain
== NULL
) {
3316 /* If we are doing a cached logon, temporarily set the domain
3317 offline so the cache won't expire the entry */
3319 original_online_state
= domain
->online
;
3320 domain
->online
= false;
3321 status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
3322 domain
->online
= original_online_state
;
3324 return NT_STATUS_IS_OK(status
);
3328 * Cache a name to sid without checking the sequence number.
3329 * Used when caching from a trusted PAC.
3332 void cache_name2sid_trusted(struct winbindd_domain
*domain
,
3333 const char *domain_name
,
3335 enum lsa_SidType type
,
3336 const struct dom_sid
*sid
)
3339 * Ensure we store the mapping with the
3340 * existing sequence number from the cache.
3343 (void)fetch_cache_seqnum(domain
, time(NULL
));
3344 wcache_save_name_to_sid(domain
,
3352 void cache_name2sid(struct winbindd_domain
*domain
,
3353 const char *domain_name
, const char *name
,
3354 enum lsa_SidType type
, const struct dom_sid
*sid
)
3356 refresh_sequence_number(domain
);
3357 wcache_save_name_to_sid(domain
, NT_STATUS_OK
, domain_name
, name
,
3362 * The original idea that this cache only contains centries has
3363 * been blurred - now other stuff gets put in here. Ensure we
3364 * ignore these things on cleanup.
3367 static int traverse_fn_cleanup(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
,
3368 TDB_DATA dbuf
, void *state
)
3370 struct cache_entry
*centry
;
3372 if (is_non_centry_key(kbuf
)) {
3376 centry
= wcache_fetch_raw((char *)kbuf
.dptr
);
3381 if (!NT_STATUS_IS_OK(centry
->status
)) {
3382 DBG_DEBUG("deleting centry %s\n", (const char *)kbuf
.dptr
);
3383 tdb_delete(the_tdb
, kbuf
);
3386 centry_free(centry
);
3390 /* flush the cache */
3391 static void wcache_flush_cache(void)
3398 tdb_close(wcache
->tdb
);
3401 if (!winbindd_use_cache()) {
3405 db_path
= wcache_path();
3406 if (db_path
== NULL
) {
3410 /* when working offline we must not clear the cache on restart */
3411 wcache
->tdb
= tdb_open_log(db_path
,
3412 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3413 TDB_INCOMPATIBLE_HASH
|
3414 (lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
)),
3415 O_RDWR
|O_CREAT
, 0600);
3416 TALLOC_FREE(db_path
);
3418 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3422 tdb_traverse(wcache
->tdb
, traverse_fn_cleanup
, NULL
);
3424 DBG_DEBUG("wcache_flush_cache success\n");
3427 /* Count cached creds */
3429 static int traverse_fn_cached_creds(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3432 int *cred_count
= (int*)state
;
3434 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
3440 NTSTATUS
wcache_count_cached_creds(struct winbindd_domain
*domain
, int *count
)
3442 struct winbind_cache
*cache
= get_cache(domain
);
3447 return NT_STATUS_INTERNAL_DB_ERROR
;
3450 tdb_traverse(cache
->tdb
, traverse_fn_cached_creds
, (void *)count
);
3452 return NT_STATUS_OK
;
3456 struct cred_list
*prev
, *next
;
3461 static struct cred_list
*wcache_cred_list
;
3463 static int traverse_fn_get_credlist(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3466 struct cred_list
*cred
;
3468 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
3470 cred
= SMB_MALLOC_P(struct cred_list
);
3472 DBG_ERR("traverse_fn_remove_first_creds: failed to malloc new entry for list\n");
3478 /* save a copy of the key */
3480 fstrcpy(cred
->name
, (const char *)kbuf
.dptr
);
3481 DLIST_ADD(wcache_cred_list
, cred
);
3487 NTSTATUS
wcache_remove_oldest_cached_creds(struct winbindd_domain
*domain
, const struct dom_sid
*sid
)
3489 struct winbind_cache
*cache
= get_cache(domain
);
3492 struct cred_list
*cred
, *next
, *oldest
= NULL
;
3495 return NT_STATUS_INTERNAL_DB_ERROR
;
3498 /* we possibly already have an entry */
3499 if (sid
&& NT_STATUS_IS_OK(wcache_cached_creds_exist(domain
, sid
))) {
3502 struct dom_sid_buf tmp
;
3504 DBG_DEBUG("we already have an entry, deleting that\n");
3506 fstr_sprintf(key_str
, "CRED/%s", dom_sid_str_buf(sid
, &tmp
));
3508 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3510 return NT_STATUS_OK
;
3513 ret
= tdb_traverse(cache
->tdb
, traverse_fn_get_credlist
, NULL
);
3515 return NT_STATUS_OK
;
3516 } else if ((ret
< 0) || (wcache_cred_list
== NULL
)) {
3517 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
3520 ZERO_STRUCTP(oldest
);
3522 for (cred
= wcache_cred_list
; cred
; cred
= cred
->next
) {
3527 data
= tdb_fetch(cache
->tdb
, string_tdb_data(cred
->name
));
3529 DBG_DEBUG("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3531 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
3535 t
= IVAL(data
.dptr
, 0);
3536 SAFE_FREE(data
.dptr
);
3539 oldest
= SMB_MALLOC_P(struct cred_list
);
3540 if (oldest
== NULL
) {
3541 status
= NT_STATUS_NO_MEMORY
;
3545 fstrcpy(oldest
->name
, cred
->name
);
3546 oldest
->created
= t
;
3550 if (t
< oldest
->created
) {
3551 fstrcpy(oldest
->name
, cred
->name
);
3552 oldest
->created
= t
;
3556 if (tdb_delete(cache
->tdb
, string_tdb_data(oldest
->name
)) == 0) {
3557 status
= NT_STATUS_OK
;
3559 status
= NT_STATUS_UNSUCCESSFUL
;
3562 for (cred
= wcache_cred_list
; cred
; cred
= next
) {
3564 DLIST_REMOVE(wcache_cred_list
, cred
);
3572 /* Change the global online/offline state. */
3573 bool set_global_winbindd_state_offline(void)
3576 uint8_t buf
[4] = {0};
3579 .dsize
= sizeof(buf
)
3583 DBG_NOTICE("Offline requested\n");
3585 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
3586 DBG_NOTICE("Winbind cache doesn't exist yet\n");
3590 if (!lp_winbind_offline_logon()) {
3591 DBG_DEBUG("Rejecting request to set winbind offline, "
3592 "offline logons are disabled in smb.conf\n");
3596 ok
= get_global_winbindd_state_offline();
3601 PUSH_LE_U32(buf
, 0, time(NULL
));
3603 rc
= tdb_store_bystring(wcache
->tdb
,
3615 void set_global_winbindd_state_online(void)
3617 DBG_DEBUG("set_global_winbindd_state_online: online requested.\n");
3619 if (!lp_winbind_offline_logon()) {
3620 DBG_DEBUG("Rejecting request to set winbind online, "
3621 "offline logons are disabled in smb.conf.\n");
3629 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3630 tdb_delete_bystring(wcache
->tdb
, "WINBINDD_OFFLINE");
3633 bool get_global_winbindd_state_offline(void)
3637 data
= tdb_fetch_bystring(wcache
->tdb
, "WINBINDD_OFFLINE");
3638 if (data
.dptr
== NULL
|| data
.dsize
!= 4) {
3639 DBG_DEBUG("Offline state not set.\n");
3640 SAFE_FREE(data
.dptr
);
3647 /***********************************************************************
3648 Validate functions for all possible cache tdb keys.
3649 ***********************************************************************/
3651 static struct cache_entry
*create_centry_validate(const char *kstr
, TDB_DATA data
,
3652 struct tdb_validation_status
*state
)
3654 struct cache_entry
*centry
;
3656 centry
= SMB_XMALLOC_P(struct cache_entry
);
3657 centry
->data
= (unsigned char *)smb_memdup(data
.dptr
, data
.dsize
);
3658 if (!centry
->data
) {
3662 centry
->len
= data
.dsize
;
3665 if (centry
->len
< 16) {
3666 /* huh? corrupt cache? */
3667 DBG_ERR("create_centry_validate: Corrupt cache for key %s "
3668 "(len < 16) ?\n", kstr
);
3669 centry_free(centry
);
3670 state
->bad_entry
= true;
3671 state
->success
= false;
3675 centry
->status
= NT_STATUS(centry_uint32(centry
));
3676 centry
->sequence_number
= centry_uint32(centry
);
3677 centry
->timeout
= centry_uint64_t(centry
);
3681 static int validate_seqnum(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3682 struct tdb_validation_status
*state
)
3684 if (dbuf
.dsize
!= 8) {
3685 DBG_ERR("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3686 keystr
, (unsigned int)dbuf
.dsize
);
3687 state
->bad_entry
= true;
3693 static int validate_u(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3694 struct tdb_validation_status
*state
)
3696 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3703 (void)centry_string(centry
, mem_ctx
);
3704 (void)centry_string(centry
, mem_ctx
);
3705 (void)centry_string(centry
, mem_ctx
);
3706 (void)centry_string(centry
, mem_ctx
);
3707 (void)centry_string(centry
, mem_ctx
);
3708 (void)centry_uint32(centry
);
3709 (void)centry_uint32(centry
);
3710 (void)centry_string(centry
, mem_ctx
);
3711 (void)centry_sid(centry
, &sid
);
3712 (void)centry_sid(centry
, &sid
);
3714 centry_free(centry
);
3716 if (!(state
->success
)) {
3719 DBG_DEBUG("validate_u: %s ok\n", keystr
);
3723 static int validate_loc_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3724 struct tdb_validation_status
*state
)
3726 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3732 (void)centry_nttime(centry
);
3733 (void)centry_nttime(centry
);
3734 (void)centry_uint16(centry
);
3736 centry_free(centry
);
3738 if (!(state
->success
)) {
3741 DBG_DEBUG("validate_loc_pol: %s ok\n", keystr
);
3745 static int validate_pwd_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3746 struct tdb_validation_status
*state
)
3748 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3754 (void)centry_uint16(centry
);
3755 (void)centry_uint16(centry
);
3756 (void)centry_uint32(centry
);
3757 (void)centry_nttime(centry
);
3758 (void)centry_nttime(centry
);
3760 centry_free(centry
);
3762 if (!(state
->success
)) {
3765 DBG_DEBUG("validate_pwd_pol: %s ok\n", keystr
);
3769 static int validate_cred(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3770 struct tdb_validation_status
*state
)
3772 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3778 (void)centry_time(centry
);
3779 (void)centry_hash16(centry
, mem_ctx
);
3781 /* We only have 17 bytes more data in the salted cred case. */
3782 if (centry
->len
- centry
->ofs
== 17) {
3783 (void)centry_hash16(centry
, mem_ctx
);
3786 centry_free(centry
);
3788 if (!(state
->success
)) {
3791 DBG_DEBUG("validate_cred: %s ok\n", keystr
);
3795 static int validate_ul(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3796 struct tdb_validation_status
*state
)
3798 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3799 int32_t num_entries
, i
;
3805 num_entries
= (int32_t)centry_uint32(centry
);
3807 for (i
=0; i
< num_entries
; i
++) {
3808 (void)centry_uint32(centry
);
3811 centry_free(centry
);
3813 if (!(state
->success
)) {
3816 DBG_DEBUG("validate_ul: %s ok\n", keystr
);
3820 static int validate_gl(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3821 struct tdb_validation_status
*state
)
3823 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3824 int32_t num_entries
, i
;
3830 num_entries
= centry_uint32(centry
);
3832 for (i
=0; i
< num_entries
; i
++) {
3833 (void)centry_string(centry
, mem_ctx
);
3834 (void)centry_string(centry
, mem_ctx
);
3835 (void)centry_uint32(centry
);
3838 centry_free(centry
);
3840 if (!(state
->success
)) {
3843 DBG_DEBUG("validate_gl: %s ok\n", keystr
);
3847 static int validate_ug(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3848 struct tdb_validation_status
*state
)
3850 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3851 int32_t num_groups
, i
;
3857 num_groups
= centry_uint32(centry
);
3859 for (i
=0; i
< num_groups
; i
++) {
3861 centry_sid(centry
, &sid
);
3864 centry_free(centry
);
3866 if (!(state
->success
)) {
3869 DBG_DEBUG("validate_ug: %s ok\n", keystr
);
3873 static int validate_ua(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3874 struct tdb_validation_status
*state
)
3876 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3877 int32_t num_aliases
, i
;
3883 num_aliases
= centry_uint32(centry
);
3885 for (i
=0; i
< num_aliases
; i
++) {
3886 (void)centry_uint32(centry
);
3889 centry_free(centry
);
3891 if (!(state
->success
)) {
3894 DBG_DEBUG("validate_ua: %s ok\n", keystr
);
3898 static int validate_gm(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3899 struct tdb_validation_status
*state
)
3901 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3902 int32_t num_names
, i
;
3908 num_names
= centry_uint32(centry
);
3910 for (i
=0; i
< num_names
; i
++) {
3912 centry_sid(centry
, &sid
);
3913 (void)centry_string(centry
, mem_ctx
);
3914 (void)centry_uint32(centry
);
3917 centry_free(centry
);
3919 if (!(state
->success
)) {
3922 DBG_DEBUG("validate_gm: %s ok\n", keystr
);
3926 static int validate_dr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3927 struct tdb_validation_status
*state
)
3929 /* Can't say anything about this other than must be nonzero. */
3930 if (dbuf
.dsize
== 0) {
3931 DBG_ERR("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3933 state
->bad_entry
= true;
3934 state
->success
= false;
3938 DBG_DEBUG("validate_dr: %s ok\n", keystr
);
3942 static int validate_de(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3943 struct tdb_validation_status
*state
)
3945 /* Can't say anything about this other than must be nonzero. */
3946 if (dbuf
.dsize
== 0) {
3947 DBG_ERR("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3949 state
->bad_entry
= true;
3950 state
->success
= false;
3954 DBG_DEBUG("validate_de: %s ok\n", keystr
);
3958 static int validate_nss_an(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3960 struct tdb_validation_status
*state
)
3962 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3968 (void)centry_string( centry
, mem_ctx
);
3970 centry_free(centry
);
3972 if (!(state
->success
)) {
3975 DBG_DEBUG("validate_pwinfo: %s ok\n", keystr
);
3979 static int validate_nss_na(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3981 struct tdb_validation_status
*state
)
3983 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3989 (void)centry_string( centry
, mem_ctx
);
3991 centry_free(centry
);
3993 if (!(state
->success
)) {
3996 DBG_DEBUG("%s ok\n", keystr
);
4000 static int validate_trustdomcache(TALLOC_CTX
*mem_ctx
, const char *keystr
,
4002 struct tdb_validation_status
*state
)
4004 if (dbuf
.dsize
== 0) {
4005 DBG_ERR("validate_trustdomcache: Corrupt cache for "
4006 "key %s (len ==0) ?\n", keystr
);
4007 state
->bad_entry
= true;
4008 state
->success
= false;
4012 DBG_DEBUG("validate_trustdomcache: %s ok\n"
4013 " Don't trust me, I am a DUMMY!\n",
4018 static int validate_offline(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4019 struct tdb_validation_status
*state
)
4021 if (dbuf
.dsize
!= 4) {
4022 DBG_ERR("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4023 keystr
, (unsigned int)dbuf
.dsize
);
4024 state
->bad_entry
= true;
4025 state
->success
= false;
4028 DBG_DEBUG("validate_offline: %s ok\n", keystr
);
4032 static int validate_ndr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4033 struct tdb_validation_status
*state
)
4036 * Ignore validation for now. The proper way to do this is with a
4037 * checksum. Just pure parsing does not really catch much.
4042 static int validate_cache_version(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4043 struct tdb_validation_status
*state
)
4045 if (dbuf
.dsize
!= 4) {
4046 DBG_ERR("validate_cache_version: Corrupt cache for "
4047 "key %s (len %u != 4) ?\n",
4048 keystr
, (unsigned int)dbuf
.dsize
);
4049 state
->bad_entry
= true;
4050 state
->success
= false;
4054 DBG_DEBUG("validate_cache_version: %s ok\n", keystr
);
4058 /***********************************************************************
4059 A list of all possible cache tdb keys with associated validation
4061 ***********************************************************************/
4063 struct key_val_struct
{
4064 const char *keyname
;
4065 int (*validate_data_fn
)(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
, struct tdb_validation_status
* state
);
4067 {"SEQNUM/", validate_seqnum
},
4069 {"LOC_POL/", validate_loc_pol
},
4070 {"PWD_POL/", validate_pwd_pol
},
4071 {"CRED/", validate_cred
},
4072 {"UL/", validate_ul
},
4073 {"GL/", validate_gl
},
4074 {"UG/", validate_ug
},
4075 {"UA", validate_ua
},
4076 {"GM/", validate_gm
},
4077 {"DR/", validate_dr
},
4078 {"DE/", validate_de
},
4079 {"TRUSTDOMCACHE/", validate_trustdomcache
},
4080 {"NSS/NA/", validate_nss_na
},
4081 {"NSS/AN/", validate_nss_an
},
4082 {"WINBINDD_OFFLINE", validate_offline
},
4083 {"NDR/", validate_ndr
},
4084 {WINBINDD_CACHE_VERSION_KEYSTR
, validate_cache_version
},
4088 /***********************************************************************
4089 Function to look at every entry in the tdb and validate it as far as
4091 ***********************************************************************/
4093 static int cache_traverse_validate_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
4096 unsigned int max_key_len
= 1024;
4097 struct tdb_validation_status
*v_state
= (struct tdb_validation_status
*)state
;
4099 /* Paranoia check. */
4100 if (strncmp("UA/", (const char *)kbuf
.dptr
, 3) == 0 ||
4101 strncmp("NDR/", (const char *)kbuf
.dptr
, 4) == 0) {
4102 max_key_len
= 1024 * 1024;
4104 if (kbuf
.dsize
> max_key_len
) {
4105 DBG_ERR("cache_traverse_validate_fn: key length too large: "
4107 (unsigned int)kbuf
.dsize
, (unsigned int)max_key_len
);
4111 for (i
= 0; key_val
[i
].keyname
; i
++) {
4112 size_t namelen
= strlen(key_val
[i
].keyname
);
4113 if (kbuf
.dsize
>= namelen
&& (
4114 strncmp(key_val
[i
].keyname
, (const char *)kbuf
.dptr
, namelen
)) == 0) {
4115 TALLOC_CTX
*mem_ctx
;
4119 keystr
= SMB_MALLOC_ARRAY(char, kbuf
.dsize
+1);
4123 memcpy(keystr
, kbuf
.dptr
, kbuf
.dsize
);
4124 keystr
[kbuf
.dsize
] = '\0';
4126 mem_ctx
= talloc_init("validate_ctx");
4132 ret
= key_val
[i
].validate_data_fn(mem_ctx
, keystr
, dbuf
,
4136 talloc_destroy(mem_ctx
);
4141 DBG_ERR("cache_traverse_validate_fn: unknown cache entry\nkey :\n");
4142 dump_data(0, (uint8_t *)kbuf
.dptr
, kbuf
.dsize
);
4143 DBG_ERR("data :\n");
4144 dump_data(0, (uint8_t *)dbuf
.dptr
, dbuf
.dsize
);
4145 v_state
->unknown_key
= true;
4146 v_state
->success
= false;
4147 return 1; /* terminate. */
4150 static void validate_panic(const char *const why
)
4152 DBG_ERR("validating cache: would panic %s\n"
4153 "exiting instead (cache validation mode)\n", why
);
4157 static int wbcache_update_centry_fn(TDB_CONTEXT
*tdb
,
4165 if (is_non_centry_key(key
)) {
4169 if (data
.dptr
== NULL
|| data
.dsize
== 0) {
4170 if (tdb_delete(tdb
, key
) < 0) {
4171 DBG_ERR("tdb_delete for [%s] failed!\n",
4177 /* add timeout to blob (uint64_t) */
4178 blob
.dsize
= data
.dsize
+ 8;
4180 blob
.dptr
= SMB_XMALLOC_ARRAY(uint8_t, blob
.dsize
);
4181 if (blob
.dptr
== NULL
) {
4184 memset(blob
.dptr
, 0, blob
.dsize
);
4186 /* copy status and seqnum */
4187 memcpy(blob
.dptr
, data
.dptr
, 8);
4190 ctimeout
= lp_winbind_cache_time() + time(NULL
);
4191 SBVAL(blob
.dptr
, 8, ctimeout
);
4194 memcpy(blob
.dptr
+ 16, data
.dptr
+ 8, data
.dsize
- 8);
4196 if (tdb_store(tdb
, key
, blob
, TDB_REPLACE
) < 0) {
4197 DBG_ERR("tdb_store to update [%s] failed!\n",
4199 SAFE_FREE(blob
.dptr
);
4203 SAFE_FREE(blob
.dptr
);
4207 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT
*tdb
)
4211 DBG_NOTICE("Upgrade to version 2 of the winbindd_cache.tdb\n");
4213 rc
= tdb_traverse(tdb
, wbcache_update_centry_fn
, NULL
);
4221 /***********************************************************************
4222 Try and validate every entry in the winbindd cache. If we fail here,
4223 delete the cache tdb and return non-zero.
4224 ***********************************************************************/
4226 int winbindd_validate_cache(void)
4229 char *tdb_path
= NULL
;
4230 TDB_CONTEXT
*tdb
= NULL
;
4234 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4235 smb_panic_fn
= validate_panic
;
4237 tdb_path
= wcache_path();
4238 if (tdb_path
== NULL
) {
4242 tdb
= tdb_open_log(tdb_path
,
4243 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
4244 TDB_INCOMPATIBLE_HASH
|
4245 ( lp_winbind_offline_logon()
4247 : TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
4251 DBG_ERR("winbindd_validate_cache: "
4252 "error opening/initializing tdb\n");
4256 /* Version check and upgrade code. */
4257 if (!tdb_fetch_uint32(tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers_id
)) {
4258 DBG_DEBUG("Fresh database\n");
4259 tdb_store_uint32(tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
);
4260 vers_id
= WINBINDD_CACHE_VERSION
;
4263 if (vers_id
!= WINBINDD_CACHE_VERSION
) {
4264 if (vers_id
== WINBINDD_CACHE_VER1
) {
4265 ok
= wbcache_upgrade_v1_to_v2(tdb
);
4267 DBG_DEBUG("winbindd_validate_cache: upgrade to version 2 failed.\n");
4272 tdb_store_uint32(tdb
,
4273 WINBINDD_CACHE_VERSION_KEYSTR
,
4274 WINBINDD_CACHE_VERSION
);
4275 vers_id
= WINBINDD_CACHE_VER2
;
4281 ret
= tdb_validate_and_backup(tdb_path
, cache_traverse_validate_fn
);
4284 DBG_DEBUG("winbindd_validate_cache: validation not successful.\n"
4285 "removing tdb %s.\n", tdb_path
);
4290 TALLOC_FREE(tdb_path
);
4291 DBG_DEBUG("winbindd_validate_cache: restoring panic function\n");
4292 smb_panic_fn
= smb_panic
;
4296 /***********************************************************************
4297 Try and validate every entry in the winbindd cache.
4298 ***********************************************************************/
4300 int winbindd_validate_cache_nobackup(void)
4305 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4306 smb_panic_fn
= validate_panic
;
4308 tdb_path
= wcache_path();
4309 if (tdb_path
== NULL
) {
4310 goto err_panic_restore
;
4313 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
4314 ret
= tdb_validate_open(tdb_path
, cache_traverse_validate_fn
);
4316 ret
= tdb_validate(wcache
->tdb
, cache_traverse_validate_fn
);
4320 DBG_DEBUG("winbindd_validate_cache_nobackup: validation not "
4324 TALLOC_FREE(tdb_path
);
4326 DBG_DEBUG("winbindd_validate_cache_nobackup: restoring panic "
4328 smb_panic_fn
= smb_panic
;
4332 bool winbindd_cache_validate_and_initialize(void)
4334 close_winbindd_cache();
4336 if (lp_winbind_offline_logon()) {
4337 if (winbindd_validate_cache() < 0) {
4338 DBG_ERR("winbindd cache tdb corrupt and no backup "
4339 "could be restored.\n");
4343 return initialize_winbindd_cache();
4346 /*********************************************************************
4347 ********************************************************************/
4349 static bool add_wbdomain_to_tdc_array( struct winbindd_domain
*new_dom
,
4350 struct winbindd_tdc_domain
**domains
,
4351 size_t *num_domains
)
4353 struct winbindd_tdc_domain
*list
= NULL
;
4355 bool set_only
= false;
4357 /* don't allow duplicates */
4362 for ( i
=0; i
< (*num_domains
); i
++ ) {
4363 if ( strequal( new_dom
->name
, list
[i
].domain_name
) ) {
4364 DBG_DEBUG("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4375 list
= talloc_array( NULL
, struct winbindd_tdc_domain
, 1 );
4378 list
= talloc_realloc( *domains
, *domains
,
4379 struct winbindd_tdc_domain
,
4384 ZERO_STRUCT( list
[idx
] );
4390 list
[idx
].domain_name
= talloc_strdup(list
, new_dom
->name
);
4391 if (list
[idx
].domain_name
== NULL
) {
4394 if (new_dom
->alt_name
!= NULL
) {
4395 list
[idx
].dns_name
= talloc_strdup(list
, new_dom
->alt_name
);
4396 if (list
[idx
].dns_name
== NULL
) {
4401 if ( !is_null_sid( &new_dom
->sid
) ) {
4402 sid_copy( &list
[idx
].sid
, &new_dom
->sid
);
4404 sid_copy(&list
[idx
].sid
, &global_sid_NULL
);
4407 if ( new_dom
->domain_flags
!= 0x0 )
4408 list
[idx
].trust_flags
= new_dom
->domain_flags
;
4410 if ( new_dom
->domain_type
!= 0x0 )
4411 list
[idx
].trust_type
= new_dom
->domain_type
;
4413 if ( new_dom
->domain_trust_attribs
!= 0x0 )
4414 list
[idx
].trust_attribs
= new_dom
->domain_trust_attribs
;
4418 *num_domains
= idx
+ 1;
4424 /*********************************************************************
4425 ********************************************************************/
4427 static TDB_DATA
make_tdc_key( const char *domain_name
)
4429 char *keystr
= NULL
;
4430 TDB_DATA key
= { NULL
, 0 };
4432 if ( !domain_name
) {
4433 DBG_INFO("make_tdc_key: Keyname workgroup is NULL!\n");
4437 if (asprintf( &keystr
, "TRUSTDOMCACHE/%s", domain_name
) == -1) {
4440 key
= string_term_tdb_data(keystr
);
4445 /*********************************************************************
4446 ********************************************************************/
4448 static int pack_tdc_domains( struct winbindd_tdc_domain
*domains
,
4450 unsigned char **buf
)
4452 unsigned char *buffer
= NULL
;
4457 DBG_DEBUG("pack_tdc_domains: Packing %d trusted domains\n",
4465 /* Store the number of array items first */
4466 len
+= tdb_pack( buffer
? buffer
+len
: NULL
,
4467 buffer
? buflen
-len
: 0, "d",
4470 /* now pack each domain trust record */
4471 for ( i
=0; i
<num_domains
; i
++ ) {
4473 struct dom_sid_buf tmp
;
4476 DBG_DEBUG("pack_tdc_domains: Packing domain %s (%s)\n",
4477 domains
[i
].domain_name
,
4478 domains
[i
].dns_name
? domains
[i
].dns_name
: "UNKNOWN" );
4481 len
+= tdb_pack( buffer
? buffer
+len
: NULL
,
4482 buffer
? buflen
-len
: 0, "fffddd",
4483 domains
[i
].domain_name
,
4484 domains
[i
].dns_name
? domains
[i
].dns_name
: "",
4485 dom_sid_str_buf(&domains
[i
].sid
, &tmp
),
4486 domains
[i
].trust_flags
,
4487 domains
[i
].trust_attribs
,
4488 domains
[i
].trust_type
);
4491 if ( buflen
< len
) {
4493 if ( (buffer
= SMB_MALLOC_ARRAY(unsigned char, len
)) == NULL
) {
4494 DBG_ERR("pack_tdc_domains: failed to alloc buffer!\n");
4508 /*********************************************************************
4509 ********************************************************************/
4511 static size_t unpack_tdc_domains( unsigned char *buf
, int buflen
,
4512 struct winbindd_tdc_domain
**domains
)
4514 fstring domain_name
, dns_name
, sid_string
;
4515 uint32_t type
, attribs
, flags
;
4519 struct winbindd_tdc_domain
*list
= NULL
;
4521 /* get the number of domains */
4522 len
+= tdb_unpack( buf
+len
, buflen
-len
, "d", &num_domains
);
4524 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4528 list
= talloc_array( NULL
, struct winbindd_tdc_domain
, num_domains
);
4530 DBG_ERR("unpack_tdc_domains: Failed to talloc() domain list!\n");
4534 for ( i
=0; i
<num_domains
; i
++ ) {
4537 this_len
= tdb_unpack( buf
+len
, buflen
-len
, "fffddd",
4545 if ( this_len
== -1 ) {
4546 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4547 TALLOC_FREE( list
);
4552 DBG_DEBUG("unpack_tdc_domains: Unpacking domain %s (%s) "
4553 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4554 domain_name
, dns_name
, sid_string
,
4555 flags
, attribs
, type
);
4557 list
[i
].domain_name
= talloc_strdup( list
, domain_name
);
4558 list
[i
].dns_name
= NULL
;
4559 if (dns_name
[0] != '\0') {
4560 list
[i
].dns_name
= talloc_strdup(list
, dns_name
);
4562 if ( !string_to_sid( &(list
[i
].sid
), sid_string
) ) {
4563 DBG_DEBUG("unpack_tdc_domains: no SID for domain %s\n",
4566 list
[i
].trust_flags
= flags
;
4567 list
[i
].trust_attribs
= attribs
;
4568 list
[i
].trust_type
= type
;
4576 /*********************************************************************
4577 ********************************************************************/
4579 static bool wcache_tdc_store_list( struct winbindd_tdc_domain
*domains
, size_t num_domains
)
4581 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4582 TDB_DATA data
= { NULL
, 0 };
4588 /* See if we were asked to delete the cache entry */
4591 ret
= tdb_delete( wcache
->tdb
, key
);
4595 data
.dsize
= pack_tdc_domains( domains
, num_domains
, &data
.dptr
);
4602 ret
= tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
4605 SAFE_FREE( data
.dptr
);
4606 SAFE_FREE( key
.dptr
);
4608 return ( ret
== 0 );
4611 /*********************************************************************
4612 ********************************************************************/
4614 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain
**domains
, size_t *num_domains
)
4616 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4617 TDB_DATA data
= { NULL
, 0 };
4625 data
= tdb_fetch( wcache
->tdb
, key
);
4627 SAFE_FREE( key
.dptr
);
4632 *num_domains
= unpack_tdc_domains( data
.dptr
, data
.dsize
, domains
);
4634 SAFE_FREE( data
.dptr
);
4642 /*********************************************************************
4643 ********************************************************************/
4645 bool wcache_tdc_add_domain( struct winbindd_domain
*domain
)
4647 struct winbindd_tdc_domain
*dom_list
= NULL
;
4648 size_t num_domains
= 0;
4650 struct dom_sid_buf buf
;
4652 DBG_DEBUG("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4653 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4654 domain
->name
, domain
->alt_name
,
4655 dom_sid_str_buf(&domain
->sid
, &buf
),
4656 domain
->domain_flags
,
4657 domain
->domain_trust_attribs
,
4658 domain
->domain_type
);
4660 if ( !init_wcache() ) {
4664 /* fetch the list */
4666 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4668 /* add the new domain */
4670 if ( !add_wbdomain_to_tdc_array( domain
, &dom_list
, &num_domains
) ) {
4674 /* pack the domain */
4676 if ( !wcache_tdc_store_list( dom_list
, num_domains
) ) {
4684 TALLOC_FREE( dom_list
);
4689 static struct winbindd_tdc_domain
*wcache_tdc_dup_domain(
4690 TALLOC_CTX
*mem_ctx
, const struct winbindd_tdc_domain
*src
)
4692 struct winbindd_tdc_domain
*dst
;
4694 dst
= talloc(mem_ctx
, struct winbindd_tdc_domain
);
4698 dst
->domain_name
= talloc_strdup(dst
, src
->domain_name
);
4699 if (dst
->domain_name
== NULL
) {
4703 dst
->dns_name
= NULL
;
4704 if (src
->dns_name
!= NULL
) {
4705 dst
->dns_name
= talloc_strdup(dst
, src
->dns_name
);
4706 if (dst
->dns_name
== NULL
) {
4711 sid_copy(&dst
->sid
, &src
->sid
);
4712 dst
->trust_flags
= src
->trust_flags
;
4713 dst
->trust_type
= src
->trust_type
;
4714 dst
->trust_attribs
= src
->trust_attribs
;
4721 /*********************************************************************
4722 ********************************************************************/
4724 struct winbindd_tdc_domain
* wcache_tdc_fetch_domain( TALLOC_CTX
*ctx
, const char *name
)
4726 struct winbindd_tdc_domain
*dom_list
= NULL
;
4727 size_t num_domains
= 0;
4729 struct winbindd_tdc_domain
*d
= NULL
;
4731 DBG_DEBUG("wcache_tdc_fetch_domain: Searching for domain %s\n", name
);
4733 if ( !init_wcache() ) {
4737 /* fetch the list */
4739 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4741 for ( i
=0; i
<num_domains
; i
++ ) {
4742 if ( strequal(name
, dom_list
[i
].domain_name
) ||
4743 strequal(name
, dom_list
[i
].dns_name
) )
4745 DBG_DEBUG("wcache_tdc_fetch_domain: Found domain %s\n",
4748 d
= wcache_tdc_dup_domain(ctx
, &dom_list
[i
]);
4753 TALLOC_FREE( dom_list
);
4758 /*********************************************************************
4759 ********************************************************************/
4761 void wcache_tdc_clear( void )
4763 if ( !init_wcache() )
4766 wcache_tdc_store_list( NULL
, 0 );
4771 static bool wcache_ndr_key(TALLOC_CTX
*mem_ctx
, const char *domain_name
,
4772 uint32_t opnum
, const DATA_BLOB
*req
,
4778 key
= talloc_asprintf(mem_ctx
, "NDR/%s/%d/", domain_name
, (int)opnum
);
4782 keylen
= talloc_get_size(key
) - 1;
4784 key
= talloc_realloc(mem_ctx
, key
, char, keylen
+ req
->length
);
4788 memcpy(key
+ keylen
, req
->data
, req
->length
);
4790 pkey
->dptr
= (uint8_t *)key
;
4791 pkey
->dsize
= talloc_get_size(key
);
4795 static bool wcache_opnum_cacheable(uint32_t opnum
)
4798 case NDR_WBINT_LOOKUPSID
:
4799 case NDR_WBINT_LOOKUPSIDS
:
4800 case NDR_WBINT_LOOKUPNAME
:
4801 case NDR_WBINT_SIDS2UNIXIDS
:
4802 case NDR_WBINT_UNIXIDS2SIDS
:
4803 case NDR_WBINT_GETNSSINFO
:
4804 case NDR_WBINT_LOOKUPUSERALIASES
:
4805 case NDR_WBINT_LOOKUPUSERGROUPS
:
4806 case NDR_WBINT_LOOKUPGROUPMEMBERS
:
4807 case NDR_WBINT_QUERYGROUPLIST
:
4808 case NDR_WBINT_QUERYUSERRIDLIST
:
4809 case NDR_WBINT_DSGETDCNAME
:
4810 case NDR_WBINT_LOOKUPRIDS
:
4816 bool wcache_fetch_ndr(TALLOC_CTX
*mem_ctx
, struct winbindd_domain
*domain
,
4817 uint32_t opnum
, const DATA_BLOB
*req
, DATA_BLOB
*resp
)
4822 if (!wcache_opnum_cacheable(opnum
) ||
4823 is_my_own_sam_domain(domain
) ||
4824 is_builtin_domain(domain
)) {
4828 if (wcache
->tdb
== NULL
) {
4832 if (!wcache_ndr_key(talloc_tos(), domain
->name
, opnum
, req
, &key
)) {
4835 data
= tdb_fetch(wcache
->tdb
, key
);
4836 TALLOC_FREE(key
.dptr
);
4838 if (data
.dptr
== NULL
) {
4841 if (data
.dsize
< 12) {
4845 if (is_domain_online(domain
)) {
4846 uint32_t entry_seqnum
, dom_seqnum
, last_check
;
4847 uint64_t entry_timeout
;
4849 if (!wcache_fetch_seqnum(domain
->name
, &dom_seqnum
,
4853 entry_seqnum
= IVAL(data
.dptr
, 0);
4854 if (entry_seqnum
!= dom_seqnum
) {
4855 DBG_DEBUG("Entry has wrong sequence number: %d\n",
4859 entry_timeout
= BVAL(data
.dptr
, 4);
4860 if (time(NULL
) > (time_t)entry_timeout
) {
4861 DBG_DEBUG("Entry has timed out\n");
4866 resp
->data
= (uint8_t *)talloc_memdup(mem_ctx
, data
.dptr
+ 12,
4868 if (resp
->data
== NULL
) {
4869 DBG_DEBUG("talloc failed\n");
4872 resp
->length
= data
.dsize
- 12;
4876 SAFE_FREE(data
.dptr
);
4880 void wcache_store_ndr(struct winbindd_domain
*domain
, uint32_t opnum
,
4881 const DATA_BLOB
*req
, const DATA_BLOB
*resp
)
4884 uint32_t dom_seqnum
, last_check
;
4887 if (!wcache_opnum_cacheable(opnum
) ||
4888 is_my_own_sam_domain(domain
) ||
4889 is_builtin_domain(domain
)) {
4893 if (wcache
->tdb
== NULL
) {
4897 if (!wcache_fetch_seqnum(domain
->name
, &dom_seqnum
, &last_check
)) {
4898 DBG_DEBUG("could not fetch seqnum for domain %s\n",
4903 if (!wcache_ndr_key(talloc_tos(), domain
->name
, opnum
, req
, &key
)) {
4907 timeout
= time(NULL
) + lp_winbind_cache_time();
4909 data
.dsize
= resp
->length
+ 12;
4910 data
.dptr
= talloc_array(key
.dptr
, uint8_t, data
.dsize
);
4911 if (data
.dptr
== NULL
) {
4915 SIVAL(data
.dptr
, 0, dom_seqnum
);
4916 SBVAL(data
.dptr
, 4, timeout
);
4917 memcpy(data
.dptr
+ 12, resp
->data
, resp
->length
);
4919 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
4922 TALLOC_FREE(key
.dptr
);