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 (%d) to: 254\n", len
);
851 centry_put_uint8(centry
, len
);
852 centry_expand(centry
, len
);
853 memcpy(centry
->data
+ centry
->ofs
, s
, len
);
858 push a 16 byte hash into a centry - treat as 16 byte string.
860 static void centry_put_hash16(struct cache_entry
*centry
, const uint8_t val
[16])
862 centry_put_uint8(centry
, 16);
863 centry_expand(centry
, 16);
864 memcpy(centry
->data
+ centry
->ofs
, val
, 16);
868 static void centry_put_sid(struct cache_entry
*centry
, const struct dom_sid
*sid
)
870 struct dom_sid_buf sid_string
;
871 centry_put_string(centry
, dom_sid_str_buf(sid
, &sid_string
));
876 put NTSTATUS into a centry
878 static void centry_put_ntstatus(struct cache_entry
*centry
, NTSTATUS status
)
880 uint32_t status_value
= NT_STATUS_V(status
);
881 centry_put_uint32(centry
, status_value
);
886 push a NTTIME into a centry
888 static void centry_put_nttime(struct cache_entry
*centry
, NTTIME nt
)
890 centry_expand(centry
, 8);
891 SIVAL(centry
->data
, centry
->ofs
, nt
& 0xFFFFFFFF);
893 SIVAL(centry
->data
, centry
->ofs
, nt
>> 32);
898 push a time_t into a centry - use a 64 bit size.
899 NTTIME here is being used as a convenient 64-bit size.
901 static void centry_put_time(struct cache_entry
*centry
, time_t t
)
903 NTTIME nt
= (NTTIME
)t
;
904 centry_put_nttime(centry
, nt
);
908 start a centry for output. When finished, call centry_end()
910 static struct cache_entry
*centry_start(struct winbindd_domain
*domain
,
913 struct cache_entry
*centry
;
918 centry
= SMB_XMALLOC_P(struct cache_entry
);
920 centry
->len
= 8192; /* reasonable default */
921 centry
->data
= SMB_XMALLOC_ARRAY(uint8_t, centry
->len
);
923 centry
->sequence_number
= domain
->sequence_number
;
924 centry
->timeout
= lp_winbind_cache_time() + time(NULL
);
925 centry_put_ntstatus(centry
, status
);
926 centry_put_uint32(centry
, centry
->sequence_number
);
927 centry_put_uint64_t(centry
, centry
->timeout
);
932 finish a centry and write it to the tdb
934 static void centry_end(struct cache_entry
*centry
, const char *format
, ...) PRINTF_ATTRIBUTE(2,3);
935 static void centry_end(struct cache_entry
*centry
, const char *format
, ...)
942 if (!winbindd_use_cache()) {
946 va_start(ap
, format
);
947 ret
= vasprintf(&kstr
, format
, ap
);
954 key
= string_tdb_data(kstr
);
955 data
.dptr
= centry
->data
;
956 data
.dsize
= centry
->ofs
;
958 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
962 static void wcache_save_name_to_sid(struct winbindd_domain
*domain
,
963 NTSTATUS status
, const char *domain_name
,
964 const char *name
, const struct dom_sid
*sid
,
965 enum lsa_SidType type
)
969 ok
= namemap_cache_set_name2sid(domain_name
, name
, sid
, type
,
970 time(NULL
) + lp_winbind_cache_time());
972 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
976 * Don't store the reverse mapping. The name came from user
977 * input, and we might not have the correct capitalization,
978 * which is important for nsswitch.
982 static void wcache_save_sid_to_name(struct winbindd_domain
*domain
, NTSTATUS status
,
983 const struct dom_sid
*sid
, const char *domain_name
, const char *name
, enum lsa_SidType type
)
987 ok
= namemap_cache_set_sid2name(sid
, domain_name
, name
, type
,
988 time(NULL
) + lp_winbind_cache_time());
990 DBG_DEBUG("namemap_cache_set_sid2name failed\n");
993 if (type
!= SID_NAME_UNKNOWN
) {
994 ok
= namemap_cache_set_name2sid(
995 domain_name
, name
, sid
, type
,
996 time(NULL
) + lp_winbind_cache_time());
998 DBG_DEBUG("namemap_cache_set_name2sid failed\n");
1003 static void wcache_save_lockout_policy(struct winbindd_domain
*domain
,
1005 struct samr_DomInfo12
*lockout_policy
)
1007 struct cache_entry
*centry
;
1009 centry
= centry_start(domain
, status
);
1013 centry_put_nttime(centry
, lockout_policy
->lockout_duration
);
1014 centry_put_nttime(centry
, lockout_policy
->lockout_window
);
1015 centry_put_uint16(centry
, lockout_policy
->lockout_threshold
);
1017 centry_end(centry
, "LOC_POL/%s", domain
->name
);
1019 DBG_DEBUG("wcache_save_lockout_policy: %s\n", domain
->name
);
1021 centry_free(centry
);
1026 static void wcache_save_password_policy(struct winbindd_domain
*domain
,
1028 struct samr_DomInfo1
*policy
)
1030 struct cache_entry
*centry
;
1032 centry
= centry_start(domain
, status
);
1036 centry_put_uint16(centry
, policy
->min_password_length
);
1037 centry_put_uint16(centry
, policy
->password_history_length
);
1038 centry_put_uint32(centry
, policy
->password_properties
);
1039 centry_put_nttime(centry
, policy
->max_password_age
);
1040 centry_put_nttime(centry
, policy
->min_password_age
);
1042 centry_end(centry
, "PWD_POL/%s", domain
->name
);
1044 DBG_DEBUG("wcache_save_password_policy: %s\n", domain
->name
);
1046 centry_free(centry
);
1049 /***************************************************************************
1050 ***************************************************************************/
1052 static void wcache_save_username_alias(struct winbindd_domain
*domain
,
1054 const char *name
, const char *alias
)
1056 struct cache_entry
*centry
;
1059 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1062 centry_put_string( centry
, alias
);
1064 fstrcpy(uname
, name
);
1065 (void)strupper_m(uname
);
1066 centry_end(centry
, "NSS/NA/%s", uname
);
1068 DBG_DEBUG("wcache_save_username_alias: %s -> %s\n", name
, alias
);
1070 centry_free(centry
);
1073 static void wcache_save_alias_username(struct winbindd_domain
*domain
,
1075 const char *alias
, const char *name
)
1077 struct cache_entry
*centry
;
1080 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1083 centry_put_string( centry
, name
);
1085 fstrcpy(uname
, alias
);
1086 (void)strupper_m(uname
);
1087 centry_end(centry
, "NSS/AN/%s", uname
);
1089 DBG_DEBUG("wcache_save_alias_username: %s -> %s\n", alias
, name
);
1091 centry_free(centry
);
1094 /***************************************************************************
1095 ***************************************************************************/
1097 NTSTATUS
resolve_username_to_alias( TALLOC_CTX
*mem_ctx
,
1098 struct winbindd_domain
*domain
,
1099 const char *name
, char **alias
)
1101 struct winbind_cache
*cache
= get_cache(domain
);
1102 struct cache_entry
*centry
= NULL
;
1106 if ( domain
->internal
)
1107 return NT_STATUS_NOT_SUPPORTED
;
1112 upper_name
= talloc_strdup_upper(mem_ctx
, name
);
1113 if (upper_name
== NULL
) {
1114 return NT_STATUS_NO_MEMORY
;
1117 centry
= wcache_fetch(cache
, domain
, "NSS/NA/%s", upper_name
);
1119 talloc_free(upper_name
);
1124 status
= centry
->status
;
1126 if (!NT_STATUS_IS_OK(status
)) {
1127 centry_free(centry
);
1131 *alias
= centry_string( centry
, mem_ctx
);
1133 centry_free(centry
);
1135 DBG_DEBUG("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1136 name
, *alias
? *alias
: "(none)");
1138 return (*alias
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1142 /* If its not in cache and we are offline, then fail */
1144 if (is_domain_offline(domain
)) {
1145 DBG_DEBUG("resolve_username_to_alias: rejecting query "
1146 "in offline mode\n");
1147 return NT_STATUS_NOT_FOUND
;
1150 status
= nss_map_to_alias( mem_ctx
, domain
->name
, name
, alias
);
1152 if ( NT_STATUS_IS_OK( status
) ) {
1153 wcache_save_username_alias(domain
, status
, name
, *alias
);
1156 if ( NT_STATUS_EQUAL( status
, NT_STATUS_NONE_MAPPED
) ) {
1157 wcache_save_username_alias(domain
, status
, name
, "(NULL)");
1160 DBG_INFO("resolve_username_to_alias: backend query returned %s\n",
1163 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1164 set_domain_offline( domain
);
1170 /***************************************************************************
1171 ***************************************************************************/
1173 NTSTATUS
resolve_alias_to_username( TALLOC_CTX
*mem_ctx
,
1174 struct winbindd_domain
*domain
,
1175 const char *alias
, char **name
)
1177 struct winbind_cache
*cache
= get_cache(domain
);
1178 struct cache_entry
*centry
= NULL
;
1182 if ( domain
->internal
)
1183 return NT_STATUS_NOT_SUPPORTED
;
1188 upper_name
= talloc_strdup(mem_ctx
, alias
);
1189 if (upper_name
== NULL
) {
1190 return NT_STATUS_NO_MEMORY
;
1192 if (!strupper_m(upper_name
)) {
1193 talloc_free(upper_name
);
1194 return NT_STATUS_INVALID_PARAMETER
;
1197 centry
= wcache_fetch(cache
, domain
, "NSS/AN/%s", upper_name
);
1199 talloc_free(upper_name
);
1204 status
= centry
->status
;
1206 if (!NT_STATUS_IS_OK(status
)) {
1207 centry_free(centry
);
1211 *name
= centry_string( centry
, mem_ctx
);
1213 centry_free(centry
);
1215 DBG_DEBUG("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1216 alias
, *name
? *name
: "(none)");
1218 return (*name
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1222 /* If its not in cache and we are offline, then fail */
1224 if (is_domain_offline(domain
)) {
1225 DBG_DEBUG("resolve_alias_to_username: rejecting query "
1226 "in offline mode\n");
1227 return NT_STATUS_NOT_FOUND
;
1230 /* an alias cannot contain a domain prefix or '@' */
1232 if (strchr(alias
, '\\') || strchr(alias
, '@')) {
1233 DBG_DEBUG("resolve_alias_to_username: skipping fully "
1234 "qualified name %s\n", alias
);
1235 return NT_STATUS_OBJECT_NAME_INVALID
;
1238 status
= nss_map_from_alias( mem_ctx
, domain
->name
, alias
, name
);
1240 if ( NT_STATUS_IS_OK( status
) ) {
1241 wcache_save_alias_username( domain
, status
, alias
, *name
);
1244 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
1245 wcache_save_alias_username(domain
, status
, alias
, "(NULL)");
1248 DBG_INFO("resolve_alias_to_username: backend query returned %s\n",
1251 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1252 set_domain_offline( domain
);
1258 NTSTATUS
wcache_cached_creds_exist(struct winbindd_domain
*domain
, const struct dom_sid
*sid
)
1260 struct winbind_cache
*cache
= get_cache(domain
);
1262 struct dom_sid_buf tmp
;
1267 return NT_STATUS_INTERNAL_DB_ERROR
;
1270 if (is_null_sid(sid
)) {
1271 return NT_STATUS_INVALID_SID
;
1274 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1275 return NT_STATUS_INVALID_SID
;
1278 fstr_sprintf(key_str
, "CRED/%s", dom_sid_str_buf(sid
, &tmp
));
1280 ret
= tdb_exists(cache
->tdb
, string_tdb_data(key_str
));
1282 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1285 return NT_STATUS_OK
;
1288 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1289 as new salted ones. */
1291 NTSTATUS
wcache_get_creds(struct winbindd_domain
*domain
,
1292 TALLOC_CTX
*mem_ctx
,
1293 const struct dom_sid
*sid
,
1294 const uint8_t **cached_nt_pass
,
1295 const uint8_t **cached_salt
)
1297 struct winbind_cache
*cache
= get_cache(domain
);
1298 struct cache_entry
*centry
= NULL
;
1301 struct dom_sid_buf sidstr
;
1304 return NT_STATUS_INTERNAL_DB_ERROR
;
1307 if (is_null_sid(sid
)) {
1308 return NT_STATUS_INVALID_SID
;
1311 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1312 return NT_STATUS_INVALID_SID
;
1315 /* Try and get a salted cred first. If we can't
1316 fall back to an unsalted cred. */
1318 centry
= wcache_fetch(cache
, domain
, "CRED/%s",
1319 dom_sid_str_buf(sid
, &sidstr
));
1321 DBG_DEBUG("wcache_get_creds: entry for [CRED/%s] not found\n",
1322 dom_sid_str_buf(sid
, &sidstr
));
1323 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1327 * We don't use the time element at this moment,
1328 * but we have to consume it, so that we don't
1329 * need to change the disk format of the cache.
1331 (void)centry_time(centry
);
1333 /* In the salted case this isn't actually the nt_hash itself,
1334 but the MD5 of the salt + nt_hash. Let the caller
1335 sort this out. It can tell as we only return the cached_salt
1336 if we are returning a salted cred. */
1338 *cached_nt_pass
= (const uint8_t *)centry_hash16(centry
, mem_ctx
);
1339 if (*cached_nt_pass
== NULL
) {
1341 dom_sid_str_buf(sid
, &sidstr
);
1343 /* Bad (old) cred cache. Delete and pretend we
1345 DBG_WARNING("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1347 wcache_delete("CRED/%s", sidstr
.buf
);
1348 centry_free(centry
);
1349 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1352 /* We only have 17 bytes more data in the salted cred case. */
1353 if (centry
->len
- centry
->ofs
== 17) {
1354 *cached_salt
= (const uint8_t *)centry_hash16(centry
, mem_ctx
);
1356 *cached_salt
= NULL
;
1359 dump_data_pw("cached_nt_pass", *cached_nt_pass
, NT_HASH_LEN
);
1361 dump_data_pw("cached_salt", *cached_salt
, NT_HASH_LEN
);
1364 status
= centry
->status
;
1366 DBG_DEBUG("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1367 dom_sid_str_buf(sid
, &sidstr
),
1368 nt_errstr(status
) );
1370 centry_free(centry
);
1374 /* Store creds for a SID - only writes out new salted ones. */
1376 NTSTATUS
wcache_save_creds(struct winbindd_domain
*domain
,
1377 const struct dom_sid
*sid
,
1378 const uint8_t nt_pass
[NT_HASH_LEN
])
1380 struct cache_entry
*centry
;
1381 struct dom_sid_buf sid_str
;
1383 uint8_t cred_salt
[NT_HASH_LEN
];
1384 uint8_t salted_hash
[NT_HASH_LEN
];
1385 gnutls_hash_hd_t hash_hnd
= NULL
;
1388 if (is_null_sid(sid
)) {
1389 return NT_STATUS_INVALID_SID
;
1392 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1393 return NT_STATUS_INVALID_SID
;
1396 centry
= centry_start(domain
, NT_STATUS_OK
);
1398 return NT_STATUS_INTERNAL_DB_ERROR
;
1401 dump_data_pw("nt_pass", nt_pass
, NT_HASH_LEN
);
1403 centry_put_time(centry
, time(NULL
));
1405 /* Create a salt and then salt the hash. */
1406 generate_random_buffer(cred_salt
, NT_HASH_LEN
);
1408 rc
= gnutls_hash_init(&hash_hnd
, GNUTLS_DIG_MD5
);
1410 centry_free(centry
);
1411 return gnutls_error_to_ntstatus(rc
, NT_STATUS_HASH_NOT_SUPPORTED
);
1414 rc
= gnutls_hash(hash_hnd
, cred_salt
, 16);
1416 gnutls_hash_deinit(hash_hnd
, NULL
);
1417 centry_free(centry
);
1418 return gnutls_error_to_ntstatus(rc
, NT_STATUS_HASH_NOT_SUPPORTED
);
1420 rc
= gnutls_hash(hash_hnd
, nt_pass
, 16);
1422 gnutls_hash_deinit(hash_hnd
, NULL
);
1423 centry_free(centry
);
1424 return gnutls_error_to_ntstatus(rc
, NT_STATUS_HASH_NOT_SUPPORTED
);
1426 gnutls_hash_deinit(hash_hnd
, salted_hash
);
1428 centry_put_hash16(centry
, salted_hash
);
1429 centry_put_hash16(centry
, cred_salt
);
1430 centry_end(centry
, "CRED/%s", dom_sid_str_buf(sid
, &sid_str
));
1432 DBG_DEBUG("wcache_save_creds: %s\n", sid_str
.buf
);
1434 centry_free(centry
);
1436 return NT_STATUS_OK
;
1440 /* Query display info. This is the basic user list fn */
1441 NTSTATUS
wb_cache_query_user_list(struct winbindd_domain
*domain
,
1442 TALLOC_CTX
*mem_ctx
,
1445 struct winbind_cache
*cache
= get_cache(domain
);
1446 struct cache_entry
*centry
= NULL
;
1447 uint32_t num_rids
= 0;
1448 uint32_t *rids
= NULL
;
1450 unsigned int i
, retry
;
1451 bool old_status
= domain
->online
;
1458 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1463 num_rids
= centry_uint32(centry
);
1465 if (num_rids
== 0) {
1469 rids
= talloc_array(mem_ctx
, uint32_t, num_rids
);
1471 centry_free(centry
);
1472 return NT_STATUS_NO_MEMORY
;
1475 for (i
=0; i
<num_rids
; i
++) {
1476 rids
[i
] = centry_uint32(centry
);
1480 status
= centry
->status
;
1482 DBG_DEBUG("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1483 domain
->name
, nt_errstr(status
) );
1485 centry_free(centry
);
1490 /* Put the query_user_list() in a retry loop. There appears to be
1491 * some bug either with Windows 2000 or Samba's handling of large
1492 * rpc replies. This manifests itself as sudden disconnection
1493 * at a random point in the enumeration of a large (60k) user list.
1494 * The retry loop simply tries the operation again. )-: It's not
1495 * pretty but an acceptable workaround until we work out what the
1496 * real problem is. */
1501 DBG_DEBUG("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1505 status
= domain
->backend
->query_user_list(domain
, mem_ctx
,
1507 num_rids
= talloc_array_length(rids
);
1509 if (!NT_STATUS_IS_OK(status
)) {
1510 DBG_NOTICE("query_user_list: returned 0x%08x, "
1511 "retrying\n", NT_STATUS_V(status
));
1513 reset_cm_connection_on_error(domain
, NULL
, status
);
1514 if (NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1515 DBG_NOTICE("query_user_list: flushing "
1516 "connection cache\n");
1517 invalidate_cm_connection(domain
);
1519 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1520 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1521 if (!domain
->internal
&& old_status
) {
1522 set_domain_offline(domain
);
1524 /* store partial response. */
1527 * humm, what about the status used for cache?
1528 * Should it be NT_STATUS_OK?
1533 * domain is offline now, and there is no user entries,
1534 * try to fetch from cache again.
1536 if (cache
->tdb
&& !domain
->online
&& !domain
->internal
&& old_status
) {
1537 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1538 /* partial response... */
1542 goto do_fetch_cache
;
1549 } while (NT_STATUS_V(status
) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL
) &&
1553 refresh_sequence_number(domain
);
1554 if (!NT_STATUS_IS_OK(status
)) {
1557 centry
= centry_start(domain
, status
);
1560 centry_put_uint32(centry
, num_rids
);
1561 for (i
=0; i
<num_rids
; i
++) {
1562 centry_put_uint32(centry
, rids
[i
]);
1564 centry_end(centry
, "UL/%s", domain
->name
);
1565 centry_free(centry
);
1573 /* list all domain groups */
1574 NTSTATUS
wb_cache_enum_dom_groups(struct winbindd_domain
*domain
,
1575 TALLOC_CTX
*mem_ctx
,
1576 uint32_t *num_entries
,
1577 struct wb_acct_info
**info
)
1579 struct winbind_cache
*cache
= get_cache(domain
);
1580 struct cache_entry
*centry
= NULL
;
1585 old_status
= domain
->online
;
1589 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1594 *num_entries
= centry_uint32(centry
);
1596 if (*num_entries
== 0)
1599 (*info
) = talloc_array(mem_ctx
, struct wb_acct_info
, *num_entries
);
1601 smb_panic_fn("enum_dom_groups out of memory");
1603 for (i
=0; i
<(*num_entries
); i
++) {
1604 (*info
)[i
].acct_name
= centry_string(centry
, (*info
));
1605 (*info
)[i
].acct_desc
= centry_string(centry
, (*info
));
1606 (*info
)[i
].rid
= centry_uint32(centry
);
1610 status
= centry
->status
;
1612 DBG_DEBUG("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1613 domain
->name
, nt_errstr(status
) );
1615 centry_free(centry
);
1622 DBG_DEBUG("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1625 status
= domain
->backend
->enum_dom_groups(domain
, mem_ctx
, num_entries
, info
);
1627 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1628 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1629 if (!domain
->internal
&& old_status
) {
1630 set_domain_offline(domain
);
1634 !domain
->internal
&&
1636 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1638 goto do_fetch_cache
;
1643 refresh_sequence_number(domain
);
1644 if (!NT_STATUS_IS_OK(status
)) {
1647 centry
= centry_start(domain
, status
);
1650 centry_put_uint32(centry
, *num_entries
);
1651 for (i
=0; i
<(*num_entries
); i
++) {
1652 centry_put_string(centry
, (*info
)[i
].acct_name
);
1653 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1654 centry_put_uint32(centry
, (*info
)[i
].rid
);
1656 centry_end(centry
, "GL/%s/domain", domain
->name
);
1657 centry_free(centry
);
1663 /* list all domain groups */
1664 NTSTATUS
wb_cache_enum_local_groups(struct winbindd_domain
*domain
,
1665 TALLOC_CTX
*mem_ctx
,
1666 uint32_t *num_entries
,
1667 struct wb_acct_info
**info
)
1669 struct winbind_cache
*cache
= get_cache(domain
);
1670 struct cache_entry
*centry
= NULL
;
1675 old_status
= domain
->online
;
1679 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1684 *num_entries
= centry_uint32(centry
);
1686 if (*num_entries
== 0)
1689 (*info
) = talloc_array(mem_ctx
, struct wb_acct_info
, *num_entries
);
1691 smb_panic_fn("enum_dom_groups out of memory");
1693 for (i
=0; i
<(*num_entries
); i
++) {
1694 (*info
)[i
].acct_name
= centry_string(centry
, (*info
));
1695 (*info
)[i
].acct_desc
= centry_string(centry
, (*info
));
1696 (*info
)[i
].rid
= centry_uint32(centry
);
1701 /* If we are returning cached data and the domain controller
1702 is down then we don't know whether the data is up to date
1703 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1706 if (wcache_server_down(domain
)) {
1707 DBG_DEBUG("enum_local_groups: returning cached user list and server was down\n");
1708 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1710 status
= centry
->status
;
1712 DBG_DEBUG("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1713 domain
->name
, nt_errstr(status
) );
1715 centry_free(centry
);
1722 DBG_DEBUG("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1725 status
= domain
->backend
->enum_local_groups(domain
, mem_ctx
, num_entries
, info
);
1727 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1728 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1729 if (!domain
->internal
&& old_status
) {
1730 set_domain_offline(domain
);
1733 !domain
->internal
&&
1736 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1738 goto do_fetch_cache
;
1743 refresh_sequence_number(domain
);
1744 if (!NT_STATUS_IS_OK(status
)) {
1747 centry
= centry_start(domain
, status
);
1750 centry_put_uint32(centry
, *num_entries
);
1751 for (i
=0; i
<(*num_entries
); i
++) {
1752 centry_put_string(centry
, (*info
)[i
].acct_name
);
1753 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1754 centry_put_uint32(centry
, (*info
)[i
].rid
);
1756 centry_end(centry
, "GL/%s/local", domain
->name
);
1757 centry_free(centry
);
1763 struct wcache_name_to_sid_state
{
1764 struct dom_sid
*sid
;
1765 enum lsa_SidType
*type
;
1770 static void wcache_name_to_sid_fn(const struct dom_sid
*sid
,
1771 enum lsa_SidType type
,
1775 struct wcache_name_to_sid_state
*state
= private_data
;
1778 *state
->type
= type
;
1779 state
->found
= (!expired
|| state
->offline
);
1782 static NTSTATUS
wcache_name_to_sid(struct winbindd_domain
*domain
,
1783 const char *domain_name
,
1785 struct dom_sid
*sid
,
1786 enum lsa_SidType
*type
)
1788 struct wcache_name_to_sid_state state
= {
1789 .sid
= sid
, .type
= type
, .found
= false,
1790 .offline
= is_domain_offline(domain
),
1794 ok
= namemap_cache_find_name(domain_name
, name
, wcache_name_to_sid_fn
,
1797 DBG_DEBUG("namemap_cache_find_name failed\n");
1798 return NT_STATUS_NOT_FOUND
;
1801 DBG_DEBUG("cache entry not found\n");
1802 return NT_STATUS_NOT_FOUND
;
1804 if (*type
== SID_NAME_UNKNOWN
) {
1805 return NT_STATUS_NONE_MAPPED
;
1808 return NT_STATUS_OK
;
1811 /* convert a single name to a sid in a domain */
1812 NTSTATUS
wb_cache_name_to_sid(struct winbindd_domain
*domain
,
1813 TALLOC_CTX
*mem_ctx
,
1814 const char *domain_name
,
1817 struct dom_sid
*sid
,
1818 enum lsa_SidType
*type
)
1822 const char *dom_name
;
1824 old_status
= domain
->online
;
1826 status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
1827 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
1833 DBG_DEBUG("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1836 winbindd_domain_init_backend(domain
);
1837 status
= domain
->backend
->name_to_sid(domain
, mem_ctx
, domain_name
,
1838 name
, flags
, &dom_name
, sid
, type
);
1840 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1841 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1842 if (!domain
->internal
&& old_status
) {
1843 set_domain_offline(domain
);
1845 if (!domain
->internal
&&
1848 NTSTATUS cache_status
;
1849 cache_status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
1850 return cache_status
;
1855 if (domain
->online
&&
1856 (NT_STATUS_IS_OK(status
) || NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))) {
1857 enum lsa_SidType save_type
= *type
;
1859 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
1860 save_type
= SID_NAME_UNKNOWN
;
1863 wcache_save_name_to_sid(domain
, status
, domain_name
, name
, sid
,
1866 /* Only save the reverse mapping if this was not a UPN */
1867 if (!strchr(name
, '@')) {
1868 if (!strupper_m(discard_const_p(char, domain_name
))) {
1869 return NT_STATUS_INVALID_PARAMETER
;
1871 (void)strlower_m(discard_const_p(char, name
));
1872 wcache_save_sid_to_name(domain
, status
, sid
,
1873 dom_name
, name
, save_type
);
1880 struct wcache_sid_to_name_state
{
1881 TALLOC_CTX
*mem_ctx
;
1884 enum lsa_SidType
*type
;
1889 static void wcache_sid_to_name_fn(const char *domain
,
1891 enum lsa_SidType type
,
1895 struct wcache_sid_to_name_state
*state
= private_data
;
1897 *state
->domain_name
= talloc_strdup(state
->mem_ctx
, domain
);
1898 if (*state
->domain_name
== NULL
) {
1901 *state
->name
= talloc_strdup(state
->mem_ctx
, name
);
1902 if (*state
->name
== NULL
) {
1905 *state
->type
= type
;
1906 state
->found
= (!expired
|| state
->offline
);
1909 static NTSTATUS
wcache_sid_to_name(struct winbindd_domain
*domain
,
1910 const struct dom_sid
*sid
,
1911 TALLOC_CTX
*mem_ctx
,
1914 enum lsa_SidType
*type
)
1916 struct wcache_sid_to_name_state state
= {
1917 .mem_ctx
= mem_ctx
, .found
= false,
1918 .domain_name
= domain_name
, .name
= name
, .type
= type
,
1919 .offline
= is_domain_offline(domain
)
1923 ok
= namemap_cache_find_sid(sid
, wcache_sid_to_name_fn
, &state
);
1925 DBG_DEBUG("namemap_cache_find_name failed\n");
1926 return NT_STATUS_NOT_FOUND
;
1929 DBG_DEBUG("cache entry not found\n");
1930 return NT_STATUS_NOT_FOUND
;
1932 if (*type
== SID_NAME_UNKNOWN
) {
1933 return NT_STATUS_NONE_MAPPED
;
1936 return NT_STATUS_OK
;
1939 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1941 NTSTATUS
wb_cache_sid_to_name(struct winbindd_domain
*domain
,
1942 TALLOC_CTX
*mem_ctx
,
1943 const struct dom_sid
*sid
,
1946 enum lsa_SidType
*type
)
1951 old_status
= domain
->online
;
1952 status
= wcache_sid_to_name(domain
, sid
, mem_ctx
, domain_name
, name
,
1954 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
1959 *domain_name
= NULL
;
1961 DBG_DEBUG("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1964 winbindd_domain_init_backend(domain
);
1966 status
= domain
->backend
->sid_to_name(domain
, mem_ctx
, sid
, domain_name
, name
, type
);
1968 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1969 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1970 if (!domain
->internal
&& old_status
) {
1971 set_domain_offline(domain
);
1973 if (!domain
->internal
&&
1976 NTSTATUS cache_status
;
1977 cache_status
= wcache_sid_to_name(domain
, sid
, mem_ctx
,
1978 domain_name
, name
, type
);
1979 return cache_status
;
1983 if (!NT_STATUS_IS_OK(status
)) {
1986 wcache_save_sid_to_name(domain
, status
, sid
, *domain_name
, *name
, *type
);
1988 /* We can't save the name to sid mapping here, as with sid history a
1989 * later name2sid would give the wrong sid. */
1994 NTSTATUS
wb_cache_rids_to_names(struct winbindd_domain
*domain
,
1995 TALLOC_CTX
*mem_ctx
,
1996 const struct dom_sid
*domain_sid
,
2001 enum lsa_SidType
**types
)
2003 struct winbind_cache
*cache
= get_cache(domain
);
2005 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2010 old_status
= domain
->online
;
2011 *domain_name
= NULL
;
2019 if (num_rids
== 0) {
2020 return NT_STATUS_OK
;
2023 *names
= talloc_array(mem_ctx
, char *, num_rids
);
2024 *types
= talloc_array(mem_ctx
, enum lsa_SidType
, num_rids
);
2026 if ((*names
== NULL
) || (*types
== NULL
)) {
2027 result
= NT_STATUS_NO_MEMORY
;
2031 have_mapped
= have_unmapped
= false;
2033 for (i
=0; i
<num_rids
; i
++) {
2036 enum lsa_SidType type
;
2039 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2040 result
= NT_STATUS_INTERNAL_ERROR
;
2044 status
= wcache_sid_to_name(domain
, &sid
, *names
, &dom
,
2047 (*types
)[i
] = SID_NAME_UNKNOWN
;
2048 (*names
)[i
] = talloc_strdup(*names
, "");
2050 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2055 if (NT_STATUS_IS_OK(status
)) {
2059 if (*domain_name
== NULL
) {
2067 } else if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
2068 have_unmapped
= true;
2070 /* something's definitely wrong */
2077 return NT_STATUS_NONE_MAPPED
;
2079 if (!have_unmapped
) {
2080 return NT_STATUS_OK
;
2082 return STATUS_SOME_UNMAPPED
;
2086 TALLOC_FREE(*names
);
2087 TALLOC_FREE(*types
);
2089 result
= domain
->backend
->rids_to_names(domain
, mem_ctx
, domain_sid
,
2090 rids
, num_rids
, domain_name
,
2093 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
2094 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2095 if (!domain
->internal
&& old_status
) {
2096 set_domain_offline(domain
);
2099 !domain
->internal
&&
2102 have_mapped
= have_unmapped
= false;
2104 *names
= talloc_array(mem_ctx
, char *, num_rids
);
2105 if (*names
== NULL
) {
2106 result
= NT_STATUS_NO_MEMORY
;
2110 *types
= talloc_array(mem_ctx
, enum lsa_SidType
,
2112 if (*types
== NULL
) {
2113 result
= NT_STATUS_NO_MEMORY
;
2117 for (i
=0; i
<num_rids
; i
++) {
2120 enum lsa_SidType type
;
2123 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2124 result
= NT_STATUS_INTERNAL_ERROR
;
2128 status
= wcache_sid_to_name(domain
, &sid
,
2132 (*types
)[i
] = SID_NAME_UNKNOWN
;
2133 (*names
)[i
] = talloc_strdup(*names
, "");
2135 if (NT_STATUS_IS_OK(status
)) {
2139 if (*domain_name
== NULL
) {
2147 } else if (NT_STATUS_EQUAL(
2149 NT_STATUS_NONE_MAPPED
)) {
2150 have_unmapped
= true;
2152 /* something's definitely wrong */
2159 return NT_STATUS_NONE_MAPPED
;
2161 if (!have_unmapped
) {
2162 return NT_STATUS_OK
;
2164 return STATUS_SOME_UNMAPPED
;
2168 None of the queried rids has been found so save all negative entries
2170 if (NT_STATUS_EQUAL(result
, NT_STATUS_NONE_MAPPED
)) {
2171 for (i
= 0; i
< num_rids
; i
++) {
2173 const char *name
= "";
2174 const enum lsa_SidType type
= SID_NAME_UNKNOWN
;
2175 NTSTATUS status
= NT_STATUS_NONE_MAPPED
;
2177 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2178 return NT_STATUS_INTERNAL_ERROR
;
2181 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
2189 Some or all of the queried rids have been found.
2191 if (!NT_STATUS_IS_OK(result
) &&
2192 !NT_STATUS_EQUAL(result
, STATUS_SOME_UNMAPPED
)) {
2196 refresh_sequence_number(domain
);
2198 for (i
=0; i
<num_rids
; i
++) {
2202 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2203 result
= NT_STATUS_INTERNAL_ERROR
;
2207 status
= (*types
)[i
] == SID_NAME_UNKNOWN
?
2208 NT_STATUS_NONE_MAPPED
: NT_STATUS_OK
;
2210 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
2211 (*names
)[i
], (*types
)[i
]);
2217 TALLOC_FREE(*names
);
2218 TALLOC_FREE(*types
);
2222 static NTSTATUS
wcache_query_user(struct winbindd_domain
*domain
,
2223 TALLOC_CTX
*mem_ctx
,
2224 const struct dom_sid
*user_sid
,
2225 struct wbint_userinfo
*info
)
2227 struct winbind_cache
*cache
= get_cache(domain
);
2228 struct cache_entry
*centry
= NULL
;
2230 struct dom_sid_buf sid_string
;
2232 if (cache
->tdb
== NULL
) {
2233 return NT_STATUS_NOT_FOUND
;
2236 centry
= wcache_fetch(
2237 cache
, domain
, "U/%s", dom_sid_str_buf(user_sid
, &sid_string
));
2238 if (centry
== NULL
) {
2239 return NT_STATUS_NOT_FOUND
;
2242 /* if status is not ok then this is a negative hit
2243 and the rest of the data doesn't matter */
2244 status
= centry
->status
;
2245 if (NT_STATUS_IS_OK(status
)) {
2246 info
->domain_name
= centry_string(centry
, mem_ctx
);
2247 info
->acct_name
= centry_string(centry
, mem_ctx
);
2248 info
->full_name
= centry_string(centry
, mem_ctx
);
2249 info
->homedir
= centry_string(centry
, mem_ctx
);
2250 info
->shell
= centry_string(centry
, mem_ctx
);
2251 info
->uid
= centry_uint32(centry
);
2252 info
->primary_gid
= centry_uint32(centry
);
2253 info
->primary_group_name
= centry_string(centry
, mem_ctx
);
2254 centry_sid(centry
, &info
->user_sid
);
2255 centry_sid(centry
, &info
->group_sid
);
2258 DBG_DEBUG("query_user: [Cached] - cached info for domain %s status: "
2259 "%s\n", domain
->name
, nt_errstr(status
) );
2261 centry_free(centry
);
2267 * @brief Query a fullname from the username cache (for further gecos processing)
2269 * @param domain A pointer to the winbindd_domain struct.
2270 * @param mem_ctx The talloc context.
2271 * @param user_sid The user sid.
2272 * @param full_name A pointer to the full_name string.
2274 * @return NTSTATUS code
2276 NTSTATUS
wcache_query_user_fullname(struct winbindd_domain
*domain
,
2277 TALLOC_CTX
*mem_ctx
,
2278 const struct dom_sid
*user_sid
,
2279 const char **full_name
)
2282 struct wbint_userinfo info
;
2284 status
= wcache_query_user(domain
, mem_ctx
, user_sid
, &info
);
2285 if (!NT_STATUS_IS_OK(status
)) {
2289 if (info
.full_name
!= NULL
) {
2290 *full_name
= talloc_strdup(mem_ctx
, info
.full_name
);
2291 if (*full_name
== NULL
) {
2292 return NT_STATUS_NO_MEMORY
;
2296 return NT_STATUS_OK
;
2299 static NTSTATUS
wcache_lookup_usergroups(struct winbindd_domain
*domain
,
2300 TALLOC_CTX
*mem_ctx
,
2301 const struct dom_sid
*user_sid
,
2302 uint32_t *pnum_sids
,
2303 struct dom_sid
**psids
)
2305 struct winbind_cache
*cache
= get_cache(domain
);
2306 struct cache_entry
*centry
= NULL
;
2308 uint32_t i
, num_sids
;
2309 struct dom_sid
*sids
;
2310 struct dom_sid_buf sid_string
;
2312 if (cache
->tdb
== NULL
) {
2313 return NT_STATUS_NOT_FOUND
;
2316 centry
= wcache_fetch(
2320 dom_sid_str_buf(user_sid
, &sid_string
));
2321 if (centry
== NULL
) {
2322 return NT_STATUS_NOT_FOUND
;
2325 num_sids
= centry_uint32(centry
);
2326 sids
= talloc_array(mem_ctx
, struct dom_sid
, num_sids
);
2328 centry_free(centry
);
2329 return NT_STATUS_NO_MEMORY
;
2332 for (i
=0; i
<num_sids
; i
++) {
2333 centry_sid(centry
, &sids
[i
]);
2336 status
= centry
->status
;
2338 DBG_DEBUG("lookup_usergroups: [Cached] - cached info for domain %s "
2339 "status: %s\n", domain
->name
, nt_errstr(status
));
2341 centry_free(centry
);
2343 *pnum_sids
= num_sids
;
2348 /* Lookup groups a user is a member of. */
2349 NTSTATUS
wb_cache_lookup_usergroups(struct winbindd_domain
*domain
,
2350 TALLOC_CTX
*mem_ctx
,
2351 const struct dom_sid
*user_sid
,
2352 uint32_t *num_groups
,
2353 struct dom_sid
**user_gids
)
2355 struct cache_entry
*centry
= NULL
;
2358 struct dom_sid_buf sid_string
;
2361 old_status
= domain
->online
;
2362 status
= wcache_lookup_usergroups(domain
, mem_ctx
, user_sid
,
2363 num_groups
, user_gids
);
2364 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2369 (*user_gids
) = NULL
;
2371 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2374 status
= domain
->backend
->lookup_usergroups(domain
, mem_ctx
, user_sid
, num_groups
, user_gids
);
2376 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2377 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2378 if (!domain
->internal
&& old_status
) {
2379 set_domain_offline(domain
);
2381 if (!domain
->internal
&&
2384 NTSTATUS cache_status
;
2385 cache_status
= wcache_lookup_usergroups(domain
, mem_ctx
, user_sid
,
2386 num_groups
, user_gids
);
2387 return cache_status
;
2390 if ( NT_STATUS_EQUAL(status
, NT_STATUS_SYNCHRONIZATION_REQUIRED
) )
2394 refresh_sequence_number(domain
);
2395 if (!NT_STATUS_IS_OK(status
)) {
2398 centry
= centry_start(domain
, status
);
2402 centry_put_uint32(centry
, *num_groups
);
2403 for (i
=0; i
<(*num_groups
); i
++) {
2404 centry_put_sid(centry
, &(*user_gids
)[i
]);
2407 centry_end(centry
, "UG/%s", dom_sid_str_buf(user_sid
, &sid_string
));
2408 centry_free(centry
);
2414 static char *wcache_make_sidlist(TALLOC_CTX
*mem_ctx
, uint32_t num_sids
,
2415 const struct dom_sid
*sids
)
2420 sidlist
= talloc_strdup(mem_ctx
, "");
2421 if (sidlist
== NULL
) {
2424 for (i
=0; i
<num_sids
; i
++) {
2425 struct dom_sid_buf tmp
;
2426 sidlist
= talloc_asprintf_append_buffer(
2429 dom_sid_str_buf(&sids
[i
], &tmp
));
2430 if (sidlist
== NULL
) {
2437 static NTSTATUS
wcache_lookup_useraliases(struct winbindd_domain
*domain
,
2438 TALLOC_CTX
*mem_ctx
,
2440 const struct dom_sid
*sids
,
2441 uint32_t *pnum_aliases
,
2442 uint32_t **paliases
)
2444 struct winbind_cache
*cache
= get_cache(domain
);
2445 struct cache_entry
*centry
= NULL
;
2446 uint32_t i
, num_aliases
;
2451 if (cache
->tdb
== NULL
) {
2452 return NT_STATUS_NOT_FOUND
;
2455 if (num_sids
== 0) {
2458 return NT_STATUS_OK
;
2461 /* We need to cache indexed by the whole list of SIDs, the aliases
2462 * resulting might come from any of the SIDs. */
2464 sidlist
= wcache_make_sidlist(talloc_tos(), num_sids
, sids
);
2465 if (sidlist
== NULL
) {
2466 return NT_STATUS_NO_MEMORY
;
2469 centry
= wcache_fetch(cache
, domain
, "UA%s", sidlist
);
2470 TALLOC_FREE(sidlist
);
2471 if (centry
== NULL
) {
2472 return NT_STATUS_NOT_FOUND
;
2475 num_aliases
= centry_uint32(centry
);
2476 aliases
= talloc_array(mem_ctx
, uint32_t, num_aliases
);
2477 if (aliases
== NULL
) {
2478 centry_free(centry
);
2479 return NT_STATUS_NO_MEMORY
;
2482 for (i
=0; i
<num_aliases
; i
++) {
2483 aliases
[i
] = centry_uint32(centry
);
2486 status
= centry
->status
;
2488 DBG_DEBUG("lookup_useraliases: [Cached] - cached info for domain: %s "
2489 "status %s\n", domain
->name
, nt_errstr(status
));
2491 centry_free(centry
);
2493 *pnum_aliases
= num_aliases
;
2494 *paliases
= aliases
;
2499 NTSTATUS
wb_cache_lookup_useraliases(struct winbindd_domain
*domain
,
2500 TALLOC_CTX
*mem_ctx
,
2502 const struct dom_sid
*sids
,
2503 uint32_t *num_aliases
,
2504 uint32_t **alias_rids
)
2506 struct cache_entry
*centry
= NULL
;
2512 old_status
= domain
->online
;
2513 status
= wcache_lookup_useraliases(domain
, mem_ctx
, num_sids
, sids
,
2514 num_aliases
, alias_rids
);
2515 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2520 (*alias_rids
) = NULL
;
2522 DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info "
2523 "for domain %s\n", domain
->name
);
2525 sidlist
= wcache_make_sidlist(talloc_tos(), num_sids
, sids
);
2526 if (sidlist
== NULL
) {
2527 return NT_STATUS_NO_MEMORY
;
2530 status
= domain
->backend
->lookup_useraliases(domain
, mem_ctx
,
2532 num_aliases
, alias_rids
);
2534 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2535 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2536 if (!domain
->internal
&& old_status
) {
2537 set_domain_offline(domain
);
2539 if (!domain
->internal
&&
2542 NTSTATUS cache_status
;
2543 cache_status
= wcache_lookup_useraliases(domain
, mem_ctx
, num_sids
,
2544 sids
, num_aliases
, alias_rids
);
2545 return cache_status
;
2549 refresh_sequence_number(domain
);
2550 if (!NT_STATUS_IS_OK(status
)) {
2553 centry
= centry_start(domain
, status
);
2556 centry_put_uint32(centry
, *num_aliases
);
2557 for (i
=0; i
<(*num_aliases
); i
++)
2558 centry_put_uint32(centry
, (*alias_rids
)[i
]);
2559 centry_end(centry
, "UA%s", sidlist
);
2560 centry_free(centry
);
2566 static NTSTATUS
wcache_lookup_aliasmem(struct winbindd_domain
*domain
,
2567 TALLOC_CTX
*mem_ctx
,
2568 const struct dom_sid
*group_sid
,
2569 uint32_t *num_names
,
2570 struct dom_sid
**sid_mem
)
2572 struct winbind_cache
*cache
= get_cache(domain
);
2573 struct cache_entry
*centry
= NULL
;
2576 struct dom_sid_buf sid_string
;
2578 if (cache
->tdb
== NULL
) {
2579 return NT_STATUS_NOT_FOUND
;
2582 centry
= wcache_fetch(cache
,
2585 dom_sid_str_buf(group_sid
, &sid_string
));
2586 if (centry
== NULL
) {
2587 return NT_STATUS_NOT_FOUND
;
2592 *num_names
= centry_uint32(centry
);
2593 if (*num_names
== 0) {
2594 centry_free(centry
);
2595 return NT_STATUS_OK
;
2598 *sid_mem
= talloc_array(mem_ctx
, struct dom_sid
, *num_names
);
2599 if (*sid_mem
== NULL
) {
2600 TALLOC_FREE(*sid_mem
);
2601 centry_free(centry
);
2602 return NT_STATUS_NO_MEMORY
;
2605 for (i
= 0; i
< (*num_names
); i
++) {
2606 centry_sid(centry
, &(*sid_mem
)[i
]);
2609 status
= centry
->status
;
2611 D_DEBUG("[Cached] - cached info for domain %s "
2616 centry_free(centry
);
2620 NTSTATUS
wb_cache_lookup_aliasmem(struct winbindd_domain
*domain
,
2621 TALLOC_CTX
*mem_ctx
,
2622 const struct dom_sid
*group_sid
,
2623 enum lsa_SidType type
,
2625 struct dom_sid
**sid_mem
)
2627 struct cache_entry
*centry
= NULL
;
2630 struct dom_sid_buf sid_string
;
2633 old_status
= domain
->online
;
2634 status
= wcache_lookup_aliasmem(domain
,
2639 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2646 D_DEBUG("[Cached] - doing backend query for info for domain %s\n",
2649 status
= domain
->backend
->lookup_aliasmem(domain
,
2656 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2657 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2658 if (!domain
->internal
&& old_status
) {
2659 set_domain_offline(domain
);
2661 if (!domain
->internal
&& !domain
->online
&& old_status
) {
2662 NTSTATUS cache_status
;
2663 cache_status
= wcache_lookup_aliasmem(domain
,
2668 return cache_status
;
2672 refresh_sequence_number(domain
);
2673 if (!NT_STATUS_IS_OK(status
)) {
2676 centry
= centry_start(domain
, status
);
2679 centry_put_uint32(centry
, *num_sids
);
2680 for (i
= 0; i
< (*num_sids
); i
++) {
2681 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2683 centry_end(centry
, "AM/%s", dom_sid_str_buf(group_sid
, &sid_string
));
2684 centry_free(centry
);
2690 static NTSTATUS
wcache_lookup_groupmem(struct winbindd_domain
*domain
,
2691 TALLOC_CTX
*mem_ctx
,
2692 const struct dom_sid
*group_sid
,
2693 uint32_t *num_names
,
2694 struct dom_sid
**sid_mem
, char ***names
,
2695 uint32_t **name_types
)
2697 struct winbind_cache
*cache
= get_cache(domain
);
2698 struct cache_entry
*centry
= NULL
;
2701 struct dom_sid_buf sid_string
;
2703 if (cache
->tdb
== NULL
) {
2704 return NT_STATUS_NOT_FOUND
;
2707 centry
= wcache_fetch(
2711 dom_sid_str_buf(group_sid
, &sid_string
));
2712 if (centry
== NULL
) {
2713 return NT_STATUS_NOT_FOUND
;
2720 *num_names
= centry_uint32(centry
);
2721 if (*num_names
== 0) {
2722 centry_free(centry
);
2723 return NT_STATUS_OK
;
2726 *sid_mem
= talloc_array(mem_ctx
, struct dom_sid
, *num_names
);
2727 *names
= talloc_array(mem_ctx
, char *, *num_names
);
2728 *name_types
= talloc_array(mem_ctx
, uint32_t, *num_names
);
2730 if ((*sid_mem
== NULL
) || (*names
== NULL
) || (*name_types
== NULL
)) {
2731 TALLOC_FREE(*sid_mem
);
2732 TALLOC_FREE(*names
);
2733 TALLOC_FREE(*name_types
);
2734 centry_free(centry
);
2735 return NT_STATUS_NO_MEMORY
;
2738 for (i
=0; i
<(*num_names
); i
++) {
2739 centry_sid(centry
, &(*sid_mem
)[i
]);
2740 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2741 (*name_types
)[i
] = centry_uint32(centry
);
2744 status
= centry
->status
;
2746 DBG_DEBUG("lookup_groupmem: [Cached] - cached info for domain %s "
2747 "status: %s\n", domain
->name
, nt_errstr(status
));
2749 centry_free(centry
);
2753 NTSTATUS
wb_cache_lookup_groupmem(struct winbindd_domain
*domain
,
2754 TALLOC_CTX
*mem_ctx
,
2755 const struct dom_sid
*group_sid
,
2756 enum lsa_SidType type
,
2757 uint32_t *num_names
,
2758 struct dom_sid
**sid_mem
,
2760 uint32_t **name_types
)
2762 struct cache_entry
*centry
= NULL
;
2765 struct dom_sid_buf sid_string
;
2768 old_status
= domain
->online
;
2769 status
= wcache_lookup_groupmem(domain
, mem_ctx
, group_sid
, num_names
,
2770 sid_mem
, names
, name_types
);
2771 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2778 (*name_types
) = NULL
;
2780 DBG_DEBUG("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2783 status
= domain
->backend
->lookup_groupmem(domain
, mem_ctx
, group_sid
,
2785 sid_mem
, names
, name_types
);
2787 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2788 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2789 if (!domain
->internal
&& old_status
) {
2790 set_domain_offline(domain
);
2792 if (!domain
->internal
&&
2795 NTSTATUS cache_status
;
2796 cache_status
= wcache_lookup_groupmem(domain
, mem_ctx
, group_sid
,
2797 num_names
, sid_mem
, names
,
2799 return cache_status
;
2803 refresh_sequence_number(domain
);
2804 if (!NT_STATUS_IS_OK(status
)) {
2807 centry
= centry_start(domain
, status
);
2810 centry_put_uint32(centry
, *num_names
);
2811 for (i
=0; i
<(*num_names
); i
++) {
2812 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2813 centry_put_string(centry
, (*names
)[i
]);
2814 centry_put_uint32(centry
, (*name_types
)[i
]);
2818 dom_sid_str_buf(group_sid
, &sid_string
));
2819 centry_free(centry
);
2825 /* find the sequence number for a domain */
2826 NTSTATUS
wb_cache_sequence_number(struct winbindd_domain
*domain
,
2829 refresh_sequence_number(domain
);
2831 *seq
= domain
->sequence_number
;
2833 return NT_STATUS_OK
;
2836 /* enumerate trusted domains
2837 * (we need to have the list of trustdoms in the cache when we go offline) -
2839 NTSTATUS
wb_cache_trusted_domains(struct winbindd_domain
*domain
,
2840 TALLOC_CTX
*mem_ctx
,
2841 struct netr_DomainTrustList
*trusts
)
2844 struct winbind_cache
*cache
;
2845 struct winbindd_tdc_domain
*dom_list
= NULL
;
2846 size_t num_domains
= 0;
2847 bool retval
= false;
2851 old_status
= domain
->online
;
2853 trusts
->array
= NULL
;
2855 cache
= get_cache(domain
);
2856 if (!cache
|| !cache
->tdb
) {
2860 if (domain
->online
) {
2864 retval
= wcache_tdc_fetch_list(&dom_list
, &num_domains
);
2865 if (!retval
|| !num_domains
|| !dom_list
) {
2866 TALLOC_FREE(dom_list
);
2871 trusts
->array
= talloc_zero_array(mem_ctx
, struct netr_DomainTrust
, num_domains
);
2872 if (!trusts
->array
) {
2873 TALLOC_FREE(dom_list
);
2874 return NT_STATUS_NO_MEMORY
;
2877 for (i
= 0; i
< num_domains
; i
++) {
2878 struct netr_DomainTrust
*trust
;
2879 struct dom_sid
*sid
;
2880 struct winbindd_domain
*dom
;
2882 dom
= find_domain_from_name_noinit(dom_list
[i
].domain_name
);
2883 if (dom
&& dom
->internal
) {
2887 trust
= &trusts
->array
[trusts
->count
];
2888 trust
->netbios_name
= talloc_strdup(trusts
->array
, dom_list
[i
].domain_name
);
2889 trust
->dns_name
= talloc_strdup(trusts
->array
, dom_list
[i
].dns_name
);
2890 sid
= talloc(trusts
->array
, struct dom_sid
);
2891 if (!trust
->netbios_name
|| !trust
->dns_name
||
2893 TALLOC_FREE(dom_list
);
2894 TALLOC_FREE(trusts
->array
);
2895 return NT_STATUS_NO_MEMORY
;
2898 trust
->trust_flags
= dom_list
[i
].trust_flags
;
2899 trust
->trust_attributes
= dom_list
[i
].trust_attribs
;
2900 trust
->trust_type
= dom_list
[i
].trust_type
;
2901 sid_copy(sid
, &dom_list
[i
].sid
);
2906 TALLOC_FREE(dom_list
);
2907 return NT_STATUS_OK
;
2910 DBG_DEBUG("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2913 status
= domain
->backend
->trusted_domains(domain
, mem_ctx
, trusts
);
2915 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2916 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2917 if (!domain
->internal
&& old_status
) {
2918 set_domain_offline(domain
);
2920 if (!domain
->internal
&&
2923 retval
= wcache_tdc_fetch_list(&dom_list
, &num_domains
);
2924 if (retval
&& num_domains
&& dom_list
) {
2925 TALLOC_FREE(trusts
->array
);
2927 goto do_fetch_cache
;
2931 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2932 * so that the generic centry handling still applies correctly -
2935 if (!NT_STATUS_IS_ERR(status
)) {
2936 status
= NT_STATUS_OK
;
2941 /* get lockout policy */
2942 NTSTATUS
wb_cache_lockout_policy(struct winbindd_domain
*domain
,
2943 TALLOC_CTX
*mem_ctx
,
2944 struct samr_DomInfo12
*policy
)
2946 struct winbind_cache
*cache
= get_cache(domain
);
2947 struct cache_entry
*centry
= NULL
;
2951 old_status
= domain
->online
;
2955 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2961 policy
->lockout_duration
= centry_nttime(centry
);
2962 policy
->lockout_window
= centry_nttime(centry
);
2963 policy
->lockout_threshold
= centry_uint16(centry
);
2965 status
= centry
->status
;
2967 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2968 domain
->name
, nt_errstr(status
) );
2970 centry_free(centry
);
2974 ZERO_STRUCTP(policy
);
2976 DBG_DEBUG("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2979 status
= domain
->backend
->lockout_policy(domain
, mem_ctx
, policy
);
2981 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2982 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2983 if (!domain
->internal
&& old_status
) {
2984 set_domain_offline(domain
);
2987 !domain
->internal
&&
2990 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2992 goto do_fetch_cache
;
2997 refresh_sequence_number(domain
);
2998 if (!NT_STATUS_IS_OK(status
)) {
3001 wcache_save_lockout_policy(domain
, status
, policy
);
3006 /* get password policy */
3007 NTSTATUS
wb_cache_password_policy(struct winbindd_domain
*domain
,
3008 TALLOC_CTX
*mem_ctx
,
3009 struct samr_DomInfo1
*policy
)
3011 struct winbind_cache
*cache
= get_cache(domain
);
3012 struct cache_entry
*centry
= NULL
;
3016 old_status
= domain
->online
;
3020 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
3026 policy
->min_password_length
= centry_uint16(centry
);
3027 policy
->password_history_length
= centry_uint16(centry
);
3028 policy
->password_properties
= centry_uint32(centry
);
3029 policy
->max_password_age
= centry_nttime(centry
);
3030 policy
->min_password_age
= centry_nttime(centry
);
3032 status
= centry
->status
;
3034 DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3035 domain
->name
, nt_errstr(status
) );
3037 centry_free(centry
);
3041 ZERO_STRUCTP(policy
);
3043 DBG_DEBUG("password_policy: [Cached] - doing backend query for info for domain %s\n",
3046 status
= domain
->backend
->password_policy(domain
, mem_ctx
, policy
);
3048 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
3049 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
3050 if (!domain
->internal
&& old_status
) {
3051 set_domain_offline(domain
);
3054 !domain
->internal
&&
3057 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
3059 goto do_fetch_cache
;
3064 refresh_sequence_number(domain
);
3065 if (!NT_STATUS_IS_OK(status
)) {
3068 wcache_save_password_policy(domain
, status
, policy
);
3074 /* Invalidate cached user and group lists coherently */
3076 static int traverse_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3079 if (strncmp((const char *)kbuf
.dptr
, "UL/", 3) == 0 ||
3080 strncmp((const char *)kbuf
.dptr
, "GL/", 3) == 0)
3081 tdb_delete(the_tdb
, kbuf
);
3086 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3088 void wcache_invalidate_samlogon(struct winbindd_domain
*domain
,
3089 const struct dom_sid
*sid
)
3092 struct dom_sid_buf sid_string
;
3093 struct winbind_cache
*cache
;
3095 /* don't clear cached U/SID and UG/SID entries when we want to logon
3098 if (lp_winbind_offline_logon()) {
3105 cache
= get_cache(domain
);
3111 /* Clear U/SID cache entry */
3112 fstr_sprintf(key_str
, "U/%s", dom_sid_str_buf(sid
, &sid_string
));
3113 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str
);
3114 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3116 /* Clear UG/SID cache entry */
3117 fstr_sprintf(key_str
, "UG/%s", dom_sid_str_buf(sid
, &sid_string
));
3118 DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str
);
3119 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3121 /* Samba/winbindd never needs this. */
3122 netsamlogon_clear_cached_user(sid
);
3125 bool wcache_invalidate_cache(void)
3127 struct winbindd_domain
*domain
;
3129 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
3130 struct winbind_cache
*cache
= get_cache(domain
);
3132 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3133 "entries for %s\n", domain
->name
);
3136 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
3145 bool wcache_invalidate_cache_noinit(void)
3147 struct winbindd_domain
*domain
;
3149 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
3150 struct winbind_cache
*cache
;
3152 /* Skip uninitialized domains. */
3153 if (!domain
->initialized
&& !domain
->internal
) {
3157 cache
= get_cache(domain
);
3159 DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
3160 "entries for %s\n", domain
->name
);
3163 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
3165 * Flushing cache has nothing to with domains.
3166 * return here if we successfully flushed once.
3167 * To avoid unnecessary traversing the cache.
3178 static bool init_wcache(void)
3182 if (wcache
== NULL
) {
3183 wcache
= SMB_XMALLOC_P(struct winbind_cache
);
3184 ZERO_STRUCTP(wcache
);
3187 if (wcache
->tdb
!= NULL
)
3190 db_path
= wcache_path();
3191 if (db_path
== NULL
) {
3195 /* when working offline we must not clear the cache on restart */
3196 wcache
->tdb
= tdb_open_log(db_path
,
3197 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3198 TDB_INCOMPATIBLE_HASH
|
3199 (lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
)),
3200 O_RDWR
|O_CREAT
, 0600);
3201 TALLOC_FREE(db_path
);
3202 if (wcache
->tdb
== NULL
) {
3203 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3210 /************************************************************************
3211 This is called by the parent to initialize the cache file.
3212 We don't need sophisticated locking here as we know we're the
3214 ************************************************************************/
3216 bool initialize_winbindd_cache(void)
3218 bool cache_bad
= false;
3222 if (!init_wcache()) {
3223 DBG_ERR("initialize_winbindd_cache: init_wcache failed.\n");
3227 /* Check version number. */
3228 ok
= tdb_fetch_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers
);
3230 DBG_DEBUG("Failed to get cache version\n");
3233 if (vers
!= WINBINDD_CACHE_VERSION
) {
3234 DBG_DEBUG("Invalid cache version %u != %u\n",
3236 WINBINDD_CACHE_VERSION
);
3243 DBG_NOTICE("initialize_winbindd_cache: clearing cache "
3244 "and re-creating with version number %d\n",
3245 WINBINDD_CACHE_VERSION
);
3247 tdb_close(wcache
->tdb
);
3250 db_path
= wcache_path();
3251 if (db_path
== NULL
) {
3255 if (unlink(db_path
) == -1) {
3256 DBG_ERR("initialize_winbindd_cache: unlink %s failed %s\n",
3259 TALLOC_FREE(db_path
);
3262 TALLOC_FREE(db_path
);
3263 if (!init_wcache()) {
3264 DBG_ERR("initialize_winbindd_cache: re-initialization "
3265 "init_wcache failed.\n");
3269 /* Write the version. */
3270 if (!tdb_store_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
)) {
3271 DBG_ERR("initialize_winbindd_cache: version number store failed %s\n",
3272 tdb_errorstr(wcache
->tdb
) );
3277 tdb_close(wcache
->tdb
);
3282 void close_winbindd_cache(void)
3288 tdb_close(wcache
->tdb
);
3293 bool lookup_cached_sid(TALLOC_CTX
*mem_ctx
, const struct dom_sid
*sid
,
3294 char **domain_name
, char **name
,
3295 enum lsa_SidType
*type
)
3297 struct winbindd_domain
*domain
;
3300 domain
= find_lookup_domain_from_sid(sid
);
3301 if (domain
== NULL
) {
3304 status
= wcache_sid_to_name(domain
, sid
, mem_ctx
, domain_name
, name
,
3306 return NT_STATUS_IS_OK(status
);
3309 bool lookup_cached_name(const char *namespace,
3310 const char *domain_name
,
3312 struct dom_sid
*sid
,
3313 enum lsa_SidType
*type
)
3315 struct winbindd_domain
*domain
;
3317 bool original_online_state
;
3319 domain
= find_lookup_domain_from_name(namespace);
3320 if (domain
== NULL
) {
3324 /* If we are doing a cached logon, temporarily set the domain
3325 offline so the cache won't expire the entry */
3327 original_online_state
= domain
->online
;
3328 domain
->online
= false;
3329 status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
3330 domain
->online
= original_online_state
;
3332 return NT_STATUS_IS_OK(status
);
3336 * Cache a name to sid without checking the sequence number.
3337 * Used when caching from a trusted PAC.
3340 void cache_name2sid_trusted(struct winbindd_domain
*domain
,
3341 const char *domain_name
,
3343 enum lsa_SidType type
,
3344 const struct dom_sid
*sid
)
3347 * Ensure we store the mapping with the
3348 * existing sequence number from the cache.
3351 (void)fetch_cache_seqnum(domain
, time(NULL
));
3352 wcache_save_name_to_sid(domain
,
3360 void cache_name2sid(struct winbindd_domain
*domain
,
3361 const char *domain_name
, const char *name
,
3362 enum lsa_SidType type
, const struct dom_sid
*sid
)
3364 refresh_sequence_number(domain
);
3365 wcache_save_name_to_sid(domain
, NT_STATUS_OK
, domain_name
, name
,
3370 * The original idea that this cache only contains centries has
3371 * been blurred - now other stuff gets put in here. Ensure we
3372 * ignore these things on cleanup.
3375 static int traverse_fn_cleanup(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
,
3376 TDB_DATA dbuf
, void *state
)
3378 struct cache_entry
*centry
;
3380 if (is_non_centry_key(kbuf
)) {
3384 centry
= wcache_fetch_raw((char *)kbuf
.dptr
);
3389 if (!NT_STATUS_IS_OK(centry
->status
)) {
3390 DBG_DEBUG("deleting centry %s\n", (const char *)kbuf
.dptr
);
3391 tdb_delete(the_tdb
, kbuf
);
3394 centry_free(centry
);
3398 /* flush the cache */
3399 static void wcache_flush_cache(void)
3406 tdb_close(wcache
->tdb
);
3409 if (!winbindd_use_cache()) {
3413 db_path
= wcache_path();
3414 if (db_path
== NULL
) {
3418 /* when working offline we must not clear the cache on restart */
3419 wcache
->tdb
= tdb_open_log(db_path
,
3420 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3421 TDB_INCOMPATIBLE_HASH
|
3422 (lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
)),
3423 O_RDWR
|O_CREAT
, 0600);
3424 TALLOC_FREE(db_path
);
3426 DBG_ERR("Failed to open winbindd_cache.tdb!\n");
3430 tdb_traverse(wcache
->tdb
, traverse_fn_cleanup
, NULL
);
3432 DBG_DEBUG("wcache_flush_cache success\n");
3435 /* Count cached creds */
3437 static int traverse_fn_cached_creds(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3440 int *cred_count
= (int*)state
;
3442 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
3448 NTSTATUS
wcache_count_cached_creds(struct winbindd_domain
*domain
, int *count
)
3450 struct winbind_cache
*cache
= get_cache(domain
);
3455 return NT_STATUS_INTERNAL_DB_ERROR
;
3458 tdb_traverse(cache
->tdb
, traverse_fn_cached_creds
, (void *)count
);
3460 return NT_STATUS_OK
;
3464 struct cred_list
*prev
, *next
;
3469 static struct cred_list
*wcache_cred_list
;
3471 static int traverse_fn_get_credlist(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3474 struct cred_list
*cred
;
3476 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
3478 cred
= SMB_MALLOC_P(struct cred_list
);
3480 DBG_ERR("traverse_fn_remove_first_creds: failed to malloc new entry for list\n");
3486 /* save a copy of the key */
3488 fstrcpy(cred
->name
, (const char *)kbuf
.dptr
);
3489 DLIST_ADD(wcache_cred_list
, cred
);
3495 NTSTATUS
wcache_remove_oldest_cached_creds(struct winbindd_domain
*domain
, const struct dom_sid
*sid
)
3497 struct winbind_cache
*cache
= get_cache(domain
);
3500 struct cred_list
*cred
, *next
, *oldest
= NULL
;
3503 return NT_STATUS_INTERNAL_DB_ERROR
;
3506 /* we possibly already have an entry */
3507 if (sid
&& NT_STATUS_IS_OK(wcache_cached_creds_exist(domain
, sid
))) {
3510 struct dom_sid_buf tmp
;
3512 DBG_DEBUG("we already have an entry, deleting that\n");
3514 fstr_sprintf(key_str
, "CRED/%s", dom_sid_str_buf(sid
, &tmp
));
3516 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3518 return NT_STATUS_OK
;
3521 ret
= tdb_traverse(cache
->tdb
, traverse_fn_get_credlist
, NULL
);
3523 return NT_STATUS_OK
;
3524 } else if ((ret
< 0) || (wcache_cred_list
== NULL
)) {
3525 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
3528 ZERO_STRUCTP(oldest
);
3530 for (cred
= wcache_cred_list
; cred
; cred
= cred
->next
) {
3535 data
= tdb_fetch(cache
->tdb
, string_tdb_data(cred
->name
));
3537 DBG_DEBUG("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3539 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
3543 t
= IVAL(data
.dptr
, 0);
3544 SAFE_FREE(data
.dptr
);
3547 oldest
= SMB_MALLOC_P(struct cred_list
);
3548 if (oldest
== NULL
) {
3549 status
= NT_STATUS_NO_MEMORY
;
3553 fstrcpy(oldest
->name
, cred
->name
);
3554 oldest
->created
= t
;
3558 if (t
< oldest
->created
) {
3559 fstrcpy(oldest
->name
, cred
->name
);
3560 oldest
->created
= t
;
3564 if (tdb_delete(cache
->tdb
, string_tdb_data(oldest
->name
)) == 0) {
3565 status
= NT_STATUS_OK
;
3567 status
= NT_STATUS_UNSUCCESSFUL
;
3570 for (cred
= wcache_cred_list
; cred
; cred
= next
) {
3572 DLIST_REMOVE(wcache_cred_list
, cred
);
3580 /* Change the global online/offline state. */
3581 bool set_global_winbindd_state_offline(void)
3584 uint8_t buf
[4] = {0};
3587 .dsize
= sizeof(buf
)
3591 DBG_NOTICE("Offline requested\n");
3593 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
3594 DBG_NOTICE("Winbind cache doesn't exist yet\n");
3598 if (!lp_winbind_offline_logon()) {
3599 DBG_DEBUG("Rejecting request to set winbind offline, "
3600 "offline logons are disabled in smb.conf\n");
3604 ok
= get_global_winbindd_state_offline();
3609 PUSH_LE_U32(buf
, 0, time(NULL
));
3611 rc
= tdb_store_bystring(wcache
->tdb
,
3623 void set_global_winbindd_state_online(void)
3625 DBG_DEBUG("set_global_winbindd_state_online: online requested.\n");
3627 if (!lp_winbind_offline_logon()) {
3628 DBG_DEBUG("Rejecting request to set winbind online, "
3629 "offline logons are disabled in smb.conf.\n");
3637 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3638 tdb_delete_bystring(wcache
->tdb
, "WINBINDD_OFFLINE");
3641 bool get_global_winbindd_state_offline(void)
3645 data
= tdb_fetch_bystring(wcache
->tdb
, "WINBINDD_OFFLINE");
3646 if (data
.dptr
== NULL
|| data
.dsize
!= 4) {
3647 DBG_DEBUG("Offline state not set.\n");
3648 SAFE_FREE(data
.dptr
);
3655 /***********************************************************************
3656 Validate functions for all possible cache tdb keys.
3657 ***********************************************************************/
3659 static struct cache_entry
*create_centry_validate(const char *kstr
, TDB_DATA data
,
3660 struct tdb_validation_status
*state
)
3662 struct cache_entry
*centry
;
3664 centry
= SMB_XMALLOC_P(struct cache_entry
);
3665 centry
->data
= (unsigned char *)smb_memdup(data
.dptr
, data
.dsize
);
3666 if (!centry
->data
) {
3670 centry
->len
= data
.dsize
;
3673 if (centry
->len
< 16) {
3674 /* huh? corrupt cache? */
3675 DBG_ERR("create_centry_validate: Corrupt cache for key %s "
3676 "(len < 16) ?\n", kstr
);
3677 centry_free(centry
);
3678 state
->bad_entry
= true;
3679 state
->success
= false;
3683 centry
->status
= NT_STATUS(centry_uint32(centry
));
3684 centry
->sequence_number
= centry_uint32(centry
);
3685 centry
->timeout
= centry_uint64_t(centry
);
3689 static int validate_seqnum(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3690 struct tdb_validation_status
*state
)
3692 if (dbuf
.dsize
!= 8) {
3693 DBG_ERR("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3694 keystr
, (unsigned int)dbuf
.dsize
);
3695 state
->bad_entry
= true;
3701 static int validate_u(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3702 struct tdb_validation_status
*state
)
3704 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3711 (void)centry_string(centry
, mem_ctx
);
3712 (void)centry_string(centry
, mem_ctx
);
3713 (void)centry_string(centry
, mem_ctx
);
3714 (void)centry_string(centry
, mem_ctx
);
3715 (void)centry_string(centry
, mem_ctx
);
3716 (void)centry_uint32(centry
);
3717 (void)centry_uint32(centry
);
3718 (void)centry_string(centry
, mem_ctx
);
3719 (void)centry_sid(centry
, &sid
);
3720 (void)centry_sid(centry
, &sid
);
3722 centry_free(centry
);
3724 if (!(state
->success
)) {
3727 DBG_DEBUG("validate_u: %s ok\n", keystr
);
3731 static int validate_loc_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3732 struct tdb_validation_status
*state
)
3734 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3740 (void)centry_nttime(centry
);
3741 (void)centry_nttime(centry
);
3742 (void)centry_uint16(centry
);
3744 centry_free(centry
);
3746 if (!(state
->success
)) {
3749 DBG_DEBUG("validate_loc_pol: %s ok\n", keystr
);
3753 static int validate_pwd_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3754 struct tdb_validation_status
*state
)
3756 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3762 (void)centry_uint16(centry
);
3763 (void)centry_uint16(centry
);
3764 (void)centry_uint32(centry
);
3765 (void)centry_nttime(centry
);
3766 (void)centry_nttime(centry
);
3768 centry_free(centry
);
3770 if (!(state
->success
)) {
3773 DBG_DEBUG("validate_pwd_pol: %s ok\n", keystr
);
3777 static int validate_cred(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3778 struct tdb_validation_status
*state
)
3780 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3786 (void)centry_time(centry
);
3787 (void)centry_hash16(centry
, mem_ctx
);
3789 /* We only have 17 bytes more data in the salted cred case. */
3790 if (centry
->len
- centry
->ofs
== 17) {
3791 (void)centry_hash16(centry
, mem_ctx
);
3794 centry_free(centry
);
3796 if (!(state
->success
)) {
3799 DBG_DEBUG("validate_cred: %s ok\n", keystr
);
3803 static int validate_ul(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3804 struct tdb_validation_status
*state
)
3806 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3807 int32_t num_entries
, i
;
3813 num_entries
= (int32_t)centry_uint32(centry
);
3815 for (i
=0; i
< num_entries
; i
++) {
3816 (void)centry_uint32(centry
);
3819 centry_free(centry
);
3821 if (!(state
->success
)) {
3824 DBG_DEBUG("validate_ul: %s ok\n", keystr
);
3828 static int validate_gl(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3829 struct tdb_validation_status
*state
)
3831 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3832 int32_t num_entries
, i
;
3838 num_entries
= centry_uint32(centry
);
3840 for (i
=0; i
< num_entries
; i
++) {
3841 (void)centry_string(centry
, mem_ctx
);
3842 (void)centry_string(centry
, mem_ctx
);
3843 (void)centry_uint32(centry
);
3846 centry_free(centry
);
3848 if (!(state
->success
)) {
3851 DBG_DEBUG("validate_gl: %s ok\n", keystr
);
3855 static int validate_ug(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3856 struct tdb_validation_status
*state
)
3858 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3859 int32_t num_groups
, i
;
3865 num_groups
= centry_uint32(centry
);
3867 for (i
=0; i
< num_groups
; i
++) {
3869 centry_sid(centry
, &sid
);
3872 centry_free(centry
);
3874 if (!(state
->success
)) {
3877 DBG_DEBUG("validate_ug: %s ok\n", keystr
);
3881 static int validate_ua(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3882 struct tdb_validation_status
*state
)
3884 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3885 int32_t num_aliases
, i
;
3891 num_aliases
= centry_uint32(centry
);
3893 for (i
=0; i
< num_aliases
; i
++) {
3894 (void)centry_uint32(centry
);
3897 centry_free(centry
);
3899 if (!(state
->success
)) {
3902 DBG_DEBUG("validate_ua: %s ok\n", keystr
);
3906 static int validate_gm(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3907 struct tdb_validation_status
*state
)
3909 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3910 int32_t num_names
, i
;
3916 num_names
= centry_uint32(centry
);
3918 for (i
=0; i
< num_names
; i
++) {
3920 centry_sid(centry
, &sid
);
3921 (void)centry_string(centry
, mem_ctx
);
3922 (void)centry_uint32(centry
);
3925 centry_free(centry
);
3927 if (!(state
->success
)) {
3930 DBG_DEBUG("validate_gm: %s ok\n", keystr
);
3934 static int validate_dr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3935 struct tdb_validation_status
*state
)
3937 /* Can't say anything about this other than must be nonzero. */
3938 if (dbuf
.dsize
== 0) {
3939 DBG_ERR("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3941 state
->bad_entry
= true;
3942 state
->success
= false;
3946 DBG_DEBUG("validate_dr: %s ok\n", keystr
);
3950 static int validate_de(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3951 struct tdb_validation_status
*state
)
3953 /* Can't say anything about this other than must be nonzero. */
3954 if (dbuf
.dsize
== 0) {
3955 DBG_ERR("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3957 state
->bad_entry
= true;
3958 state
->success
= false;
3962 DBG_DEBUG("validate_de: %s ok\n", keystr
);
3966 static int validate_nss_an(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3968 struct tdb_validation_status
*state
)
3970 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3976 (void)centry_string( centry
, mem_ctx
);
3978 centry_free(centry
);
3980 if (!(state
->success
)) {
3983 DBG_DEBUG("validate_pwinfo: %s ok\n", keystr
);
3987 static int validate_nss_na(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3989 struct tdb_validation_status
*state
)
3991 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3997 (void)centry_string( centry
, mem_ctx
);
3999 centry_free(centry
);
4001 if (!(state
->success
)) {
4004 DBG_DEBUG("%s ok\n", keystr
);
4008 static int validate_trustdomcache(TALLOC_CTX
*mem_ctx
, const char *keystr
,
4010 struct tdb_validation_status
*state
)
4012 if (dbuf
.dsize
== 0) {
4013 DBG_ERR("validate_trustdomcache: Corrupt cache for "
4014 "key %s (len ==0) ?\n", keystr
);
4015 state
->bad_entry
= true;
4016 state
->success
= false;
4020 DBG_DEBUG("validate_trustdomcache: %s ok\n"
4021 " Don't trust me, I am a DUMMY!\n",
4026 static int validate_offline(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4027 struct tdb_validation_status
*state
)
4029 if (dbuf
.dsize
!= 4) {
4030 DBG_ERR("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4031 keystr
, (unsigned int)dbuf
.dsize
);
4032 state
->bad_entry
= true;
4033 state
->success
= false;
4036 DBG_DEBUG("validate_offline: %s ok\n", keystr
);
4040 static int validate_ndr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4041 struct tdb_validation_status
*state
)
4044 * Ignore validation for now. The proper way to do this is with a
4045 * checksum. Just pure parsing does not really catch much.
4050 static int validate_cache_version(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4051 struct tdb_validation_status
*state
)
4053 if (dbuf
.dsize
!= 4) {
4054 DBG_ERR("validate_cache_version: Corrupt cache for "
4055 "key %s (len %u != 4) ?\n",
4056 keystr
, (unsigned int)dbuf
.dsize
);
4057 state
->bad_entry
= true;
4058 state
->success
= false;
4062 DBG_DEBUG("validate_cache_version: %s ok\n", keystr
);
4066 /***********************************************************************
4067 A list of all possible cache tdb keys with associated validation
4069 ***********************************************************************/
4071 struct key_val_struct
{
4072 const char *keyname
;
4073 int (*validate_data_fn
)(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
, struct tdb_validation_status
* state
);
4075 {"SEQNUM/", validate_seqnum
},
4077 {"LOC_POL/", validate_loc_pol
},
4078 {"PWD_POL/", validate_pwd_pol
},
4079 {"CRED/", validate_cred
},
4080 {"UL/", validate_ul
},
4081 {"GL/", validate_gl
},
4082 {"UG/", validate_ug
},
4083 {"UA", validate_ua
},
4084 {"GM/", validate_gm
},
4085 {"DR/", validate_dr
},
4086 {"DE/", validate_de
},
4087 {"TRUSTDOMCACHE/", validate_trustdomcache
},
4088 {"NSS/NA/", validate_nss_na
},
4089 {"NSS/AN/", validate_nss_an
},
4090 {"WINBINDD_OFFLINE", validate_offline
},
4091 {"NDR/", validate_ndr
},
4092 {WINBINDD_CACHE_VERSION_KEYSTR
, validate_cache_version
},
4096 /***********************************************************************
4097 Function to look at every entry in the tdb and validate it as far as
4099 ***********************************************************************/
4101 static int cache_traverse_validate_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
4104 unsigned int max_key_len
= 1024;
4105 struct tdb_validation_status
*v_state
= (struct tdb_validation_status
*)state
;
4107 /* Paranoia check. */
4108 if (strncmp("UA/", (const char *)kbuf
.dptr
, 3) == 0 ||
4109 strncmp("NDR/", (const char *)kbuf
.dptr
, 4) == 0) {
4110 max_key_len
= 1024 * 1024;
4112 if (kbuf
.dsize
> max_key_len
) {
4113 DBG_ERR("cache_traverse_validate_fn: key length too large: "
4115 (unsigned int)kbuf
.dsize
, (unsigned int)max_key_len
);
4119 for (i
= 0; key_val
[i
].keyname
; i
++) {
4120 size_t namelen
= strlen(key_val
[i
].keyname
);
4121 if (kbuf
.dsize
>= namelen
&& (
4122 strncmp(key_val
[i
].keyname
, (const char *)kbuf
.dptr
, namelen
)) == 0) {
4123 TALLOC_CTX
*mem_ctx
;
4127 keystr
= SMB_MALLOC_ARRAY(char, kbuf
.dsize
+1);
4131 memcpy(keystr
, kbuf
.dptr
, kbuf
.dsize
);
4132 keystr
[kbuf
.dsize
] = '\0';
4134 mem_ctx
= talloc_init("validate_ctx");
4140 ret
= key_val
[i
].validate_data_fn(mem_ctx
, keystr
, dbuf
,
4144 talloc_destroy(mem_ctx
);
4149 DBG_ERR("cache_traverse_validate_fn: unknown cache entry\nkey :\n");
4150 dump_data(0, (uint8_t *)kbuf
.dptr
, kbuf
.dsize
);
4151 DBG_ERR("data :\n");
4152 dump_data(0, (uint8_t *)dbuf
.dptr
, dbuf
.dsize
);
4153 v_state
->unknown_key
= true;
4154 v_state
->success
= false;
4155 return 1; /* terminate. */
4158 static void validate_panic(const char *const why
)
4160 DBG_ERR("validating cache: would panic %s\n"
4161 "exiting instead (cache validation mode)\n", why
);
4165 static int wbcache_update_centry_fn(TDB_CONTEXT
*tdb
,
4173 if (is_non_centry_key(key
)) {
4177 if (data
.dptr
== NULL
|| data
.dsize
== 0) {
4178 if (tdb_delete(tdb
, key
) < 0) {
4179 DBG_ERR("tdb_delete for [%s] failed!\n",
4185 /* add timeout to blob (uint64_t) */
4186 blob
.dsize
= data
.dsize
+ 8;
4188 blob
.dptr
= SMB_XMALLOC_ARRAY(uint8_t, blob
.dsize
);
4189 if (blob
.dptr
== NULL
) {
4192 memset(blob
.dptr
, 0, blob
.dsize
);
4194 /* copy status and seqnum */
4195 memcpy(blob
.dptr
, data
.dptr
, 8);
4198 ctimeout
= lp_winbind_cache_time() + time(NULL
);
4199 SBVAL(blob
.dptr
, 8, ctimeout
);
4202 memcpy(blob
.dptr
+ 16, data
.dptr
+ 8, data
.dsize
- 8);
4204 if (tdb_store(tdb
, key
, blob
, TDB_REPLACE
) < 0) {
4205 DBG_ERR("tdb_store to update [%s] failed!\n",
4207 SAFE_FREE(blob
.dptr
);
4211 SAFE_FREE(blob
.dptr
);
4215 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT
*tdb
)
4219 DBG_NOTICE("Upgrade to version 2 of the winbindd_cache.tdb\n");
4221 rc
= tdb_traverse(tdb
, wbcache_update_centry_fn
, NULL
);
4229 /***********************************************************************
4230 Try and validate every entry in the winbindd cache. If we fail here,
4231 delete the cache tdb and return non-zero.
4232 ***********************************************************************/
4234 int winbindd_validate_cache(void)
4237 char *tdb_path
= NULL
;
4238 TDB_CONTEXT
*tdb
= NULL
;
4242 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4243 smb_panic_fn
= validate_panic
;
4245 tdb_path
= wcache_path();
4246 if (tdb_path
== NULL
) {
4250 tdb
= tdb_open_log(tdb_path
,
4251 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
4252 TDB_INCOMPATIBLE_HASH
|
4253 ( lp_winbind_offline_logon()
4255 : TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
4259 DBG_ERR("winbindd_validate_cache: "
4260 "error opening/initializing tdb\n");
4264 /* Version check and upgrade code. */
4265 if (!tdb_fetch_uint32(tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers_id
)) {
4266 DBG_DEBUG("Fresh database\n");
4267 tdb_store_uint32(tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
);
4268 vers_id
= WINBINDD_CACHE_VERSION
;
4271 if (vers_id
!= WINBINDD_CACHE_VERSION
) {
4272 if (vers_id
== WINBINDD_CACHE_VER1
) {
4273 ok
= wbcache_upgrade_v1_to_v2(tdb
);
4275 DBG_DEBUG("winbindd_validate_cache: upgrade to version 2 failed.\n");
4280 tdb_store_uint32(tdb
,
4281 WINBINDD_CACHE_VERSION_KEYSTR
,
4282 WINBINDD_CACHE_VERSION
);
4283 vers_id
= WINBINDD_CACHE_VER2
;
4289 ret
= tdb_validate_and_backup(tdb_path
, cache_traverse_validate_fn
);
4292 DBG_DEBUG("winbindd_validate_cache: validation not successful.\n"
4293 "removing tdb %s.\n", tdb_path
);
4298 TALLOC_FREE(tdb_path
);
4299 DBG_DEBUG("winbindd_validate_cache: restoring panic function\n");
4300 smb_panic_fn
= smb_panic
;
4304 /***********************************************************************
4305 Try and validate every entry in the winbindd cache.
4306 ***********************************************************************/
4308 int winbindd_validate_cache_nobackup(void)
4313 DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
4314 smb_panic_fn
= validate_panic
;
4316 tdb_path
= wcache_path();
4317 if (tdb_path
== NULL
) {
4318 goto err_panic_restore
;
4321 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
4322 ret
= tdb_validate_open(tdb_path
, cache_traverse_validate_fn
);
4324 ret
= tdb_validate(wcache
->tdb
, cache_traverse_validate_fn
);
4328 DBG_DEBUG("winbindd_validate_cache_nobackup: validation not "
4332 TALLOC_FREE(tdb_path
);
4334 DBG_DEBUG("winbindd_validate_cache_nobackup: restoring panic "
4336 smb_panic_fn
= smb_panic
;
4340 bool winbindd_cache_validate_and_initialize(void)
4342 close_winbindd_cache();
4344 if (lp_winbind_offline_logon()) {
4345 if (winbindd_validate_cache() < 0) {
4346 DBG_ERR("winbindd cache tdb corrupt and no backup "
4347 "could be restored.\n");
4351 return initialize_winbindd_cache();
4354 /*********************************************************************
4355 ********************************************************************/
4357 static bool add_wbdomain_to_tdc_array( struct winbindd_domain
*new_dom
,
4358 struct winbindd_tdc_domain
**domains
,
4359 size_t *num_domains
)
4361 struct winbindd_tdc_domain
*list
= NULL
;
4363 bool set_only
= false;
4365 /* don't allow duplicates */
4370 for ( i
=0; i
< (*num_domains
); i
++ ) {
4371 if ( strequal( new_dom
->name
, list
[i
].domain_name
) ) {
4372 DBG_DEBUG("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4383 list
= talloc_array( NULL
, struct winbindd_tdc_domain
, 1 );
4386 list
= talloc_realloc( *domains
, *domains
,
4387 struct winbindd_tdc_domain
,
4392 ZERO_STRUCT( list
[idx
] );
4398 list
[idx
].domain_name
= talloc_strdup(list
, new_dom
->name
);
4399 if (list
[idx
].domain_name
== NULL
) {
4402 if (new_dom
->alt_name
!= NULL
) {
4403 list
[idx
].dns_name
= talloc_strdup(list
, new_dom
->alt_name
);
4404 if (list
[idx
].dns_name
== NULL
) {
4409 if ( !is_null_sid( &new_dom
->sid
) ) {
4410 sid_copy( &list
[idx
].sid
, &new_dom
->sid
);
4412 sid_copy(&list
[idx
].sid
, &global_sid_NULL
);
4415 if ( new_dom
->domain_flags
!= 0x0 )
4416 list
[idx
].trust_flags
= new_dom
->domain_flags
;
4418 if ( new_dom
->domain_type
!= 0x0 )
4419 list
[idx
].trust_type
= new_dom
->domain_type
;
4421 if ( new_dom
->domain_trust_attribs
!= 0x0 )
4422 list
[idx
].trust_attribs
= new_dom
->domain_trust_attribs
;
4426 *num_domains
= idx
+ 1;
4432 /*********************************************************************
4433 ********************************************************************/
4435 static TDB_DATA
make_tdc_key( const char *domain_name
)
4437 char *keystr
= NULL
;
4438 TDB_DATA key
= { NULL
, 0 };
4440 if ( !domain_name
) {
4441 DBG_INFO("make_tdc_key: Keyname workgroup is NULL!\n");
4445 if (asprintf( &keystr
, "TRUSTDOMCACHE/%s", domain_name
) == -1) {
4448 key
= string_term_tdb_data(keystr
);
4453 /*********************************************************************
4454 ********************************************************************/
4456 static int pack_tdc_domains( struct winbindd_tdc_domain
*domains
,
4458 unsigned char **buf
)
4460 unsigned char *buffer
= NULL
;
4465 DBG_DEBUG("pack_tdc_domains: Packing %d trusted domains\n",
4473 /* Store the number of array items first */
4474 len
+= tdb_pack( buffer
? buffer
+len
: NULL
,
4475 buffer
? buflen
-len
: 0, "d",
4478 /* now pack each domain trust record */
4479 for ( i
=0; i
<num_domains
; i
++ ) {
4481 struct dom_sid_buf tmp
;
4484 DBG_DEBUG("pack_tdc_domains: Packing domain %s (%s)\n",
4485 domains
[i
].domain_name
,
4486 domains
[i
].dns_name
? domains
[i
].dns_name
: "UNKNOWN" );
4489 len
+= tdb_pack( buffer
? buffer
+len
: NULL
,
4490 buffer
? buflen
-len
: 0, "fffddd",
4491 domains
[i
].domain_name
,
4492 domains
[i
].dns_name
? domains
[i
].dns_name
: "",
4493 dom_sid_str_buf(&domains
[i
].sid
, &tmp
),
4494 domains
[i
].trust_flags
,
4495 domains
[i
].trust_attribs
,
4496 domains
[i
].trust_type
);
4499 if ( buflen
< len
) {
4501 if ( (buffer
= SMB_MALLOC_ARRAY(unsigned char, len
)) == NULL
) {
4502 DBG_ERR("pack_tdc_domains: failed to alloc buffer!\n");
4516 /*********************************************************************
4517 ********************************************************************/
4519 static size_t unpack_tdc_domains( unsigned char *buf
, int buflen
,
4520 struct winbindd_tdc_domain
**domains
)
4522 fstring domain_name
, dns_name
, sid_string
;
4523 uint32_t type
, attribs
, flags
;
4527 struct winbindd_tdc_domain
*list
= NULL
;
4529 /* get the number of domains */
4530 len
+= tdb_unpack( buf
+len
, buflen
-len
, "d", &num_domains
);
4532 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4536 list
= talloc_array( NULL
, struct winbindd_tdc_domain
, num_domains
);
4538 DBG_ERR("unpack_tdc_domains: Failed to talloc() domain list!\n");
4542 for ( i
=0; i
<num_domains
; i
++ ) {
4545 this_len
= tdb_unpack( buf
+len
, buflen
-len
, "fffddd",
4553 if ( this_len
== -1 ) {
4554 DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
4555 TALLOC_FREE( list
);
4560 DBG_DEBUG("unpack_tdc_domains: Unpacking domain %s (%s) "
4561 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4562 domain_name
, dns_name
, sid_string
,
4563 flags
, attribs
, type
);
4565 list
[i
].domain_name
= talloc_strdup( list
, domain_name
);
4566 list
[i
].dns_name
= NULL
;
4567 if (dns_name
[0] != '\0') {
4568 list
[i
].dns_name
= talloc_strdup(list
, dns_name
);
4570 if ( !string_to_sid( &(list
[i
].sid
), sid_string
) ) {
4571 DBG_DEBUG("unpack_tdc_domains: no SID for domain %s\n",
4574 list
[i
].trust_flags
= flags
;
4575 list
[i
].trust_attribs
= attribs
;
4576 list
[i
].trust_type
= type
;
4584 /*********************************************************************
4585 ********************************************************************/
4587 static bool wcache_tdc_store_list( struct winbindd_tdc_domain
*domains
, size_t num_domains
)
4589 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4590 TDB_DATA data
= { NULL
, 0 };
4596 /* See if we were asked to delete the cache entry */
4599 ret
= tdb_delete( wcache
->tdb
, key
);
4603 data
.dsize
= pack_tdc_domains( domains
, num_domains
, &data
.dptr
);
4610 ret
= tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
4613 SAFE_FREE( data
.dptr
);
4614 SAFE_FREE( key
.dptr
);
4616 return ( ret
== 0 );
4619 /*********************************************************************
4620 ********************************************************************/
4622 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain
**domains
, size_t *num_domains
)
4624 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4625 TDB_DATA data
= { NULL
, 0 };
4633 data
= tdb_fetch( wcache
->tdb
, key
);
4635 SAFE_FREE( key
.dptr
);
4640 *num_domains
= unpack_tdc_domains( data
.dptr
, data
.dsize
, domains
);
4642 SAFE_FREE( data
.dptr
);
4650 /*********************************************************************
4651 ********************************************************************/
4653 bool wcache_tdc_add_domain( struct winbindd_domain
*domain
)
4655 struct winbindd_tdc_domain
*dom_list
= NULL
;
4656 size_t num_domains
= 0;
4658 struct dom_sid_buf buf
;
4660 DBG_DEBUG("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4661 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4662 domain
->name
, domain
->alt_name
,
4663 dom_sid_str_buf(&domain
->sid
, &buf
),
4664 domain
->domain_flags
,
4665 domain
->domain_trust_attribs
,
4666 domain
->domain_type
);
4668 if ( !init_wcache() ) {
4672 /* fetch the list */
4674 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4676 /* add the new domain */
4678 if ( !add_wbdomain_to_tdc_array( domain
, &dom_list
, &num_domains
) ) {
4682 /* pack the domain */
4684 if ( !wcache_tdc_store_list( dom_list
, num_domains
) ) {
4692 TALLOC_FREE( dom_list
);
4697 static struct winbindd_tdc_domain
*wcache_tdc_dup_domain(
4698 TALLOC_CTX
*mem_ctx
, const struct winbindd_tdc_domain
*src
)
4700 struct winbindd_tdc_domain
*dst
;
4702 dst
= talloc(mem_ctx
, struct winbindd_tdc_domain
);
4706 dst
->domain_name
= talloc_strdup(dst
, src
->domain_name
);
4707 if (dst
->domain_name
== NULL
) {
4711 dst
->dns_name
= NULL
;
4712 if (src
->dns_name
!= NULL
) {
4713 dst
->dns_name
= talloc_strdup(dst
, src
->dns_name
);
4714 if (dst
->dns_name
== NULL
) {
4719 sid_copy(&dst
->sid
, &src
->sid
);
4720 dst
->trust_flags
= src
->trust_flags
;
4721 dst
->trust_type
= src
->trust_type
;
4722 dst
->trust_attribs
= src
->trust_attribs
;
4729 /*********************************************************************
4730 ********************************************************************/
4732 struct winbindd_tdc_domain
* wcache_tdc_fetch_domain( TALLOC_CTX
*ctx
, const char *name
)
4734 struct winbindd_tdc_domain
*dom_list
= NULL
;
4735 size_t num_domains
= 0;
4737 struct winbindd_tdc_domain
*d
= NULL
;
4739 DBG_DEBUG("wcache_tdc_fetch_domain: Searching for domain %s\n", name
);
4741 if ( !init_wcache() ) {
4745 /* fetch the list */
4747 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4749 for ( i
=0; i
<num_domains
; i
++ ) {
4750 if ( strequal(name
, dom_list
[i
].domain_name
) ||
4751 strequal(name
, dom_list
[i
].dns_name
) )
4753 DBG_DEBUG("wcache_tdc_fetch_domain: Found domain %s\n",
4756 d
= wcache_tdc_dup_domain(ctx
, &dom_list
[i
]);
4761 TALLOC_FREE( dom_list
);
4766 /*********************************************************************
4767 ********************************************************************/
4769 void wcache_tdc_clear( void )
4771 if ( !init_wcache() )
4774 wcache_tdc_store_list( NULL
, 0 );
4779 static bool wcache_ndr_key(TALLOC_CTX
*mem_ctx
, const char *domain_name
,
4780 uint32_t opnum
, const DATA_BLOB
*req
,
4786 key
= talloc_asprintf(mem_ctx
, "NDR/%s/%d/", domain_name
, (int)opnum
);
4790 keylen
= talloc_get_size(key
) - 1;
4792 key
= talloc_realloc(mem_ctx
, key
, char, keylen
+ req
->length
);
4796 memcpy(key
+ keylen
, req
->data
, req
->length
);
4798 pkey
->dptr
= (uint8_t *)key
;
4799 pkey
->dsize
= talloc_get_size(key
);
4803 static bool wcache_opnum_cacheable(uint32_t opnum
)
4806 case NDR_WBINT_LOOKUPSID
:
4807 case NDR_WBINT_LOOKUPSIDS
:
4808 case NDR_WBINT_LOOKUPNAME
:
4809 case NDR_WBINT_SIDS2UNIXIDS
:
4810 case NDR_WBINT_UNIXIDS2SIDS
:
4811 case NDR_WBINT_GETNSSINFO
:
4812 case NDR_WBINT_LOOKUPUSERALIASES
:
4813 case NDR_WBINT_LOOKUPUSERGROUPS
:
4814 case NDR_WBINT_LOOKUPGROUPMEMBERS
:
4815 case NDR_WBINT_QUERYGROUPLIST
:
4816 case NDR_WBINT_QUERYUSERRIDLIST
:
4817 case NDR_WBINT_DSGETDCNAME
:
4818 case NDR_WBINT_LOOKUPRIDS
:
4824 bool wcache_fetch_ndr(TALLOC_CTX
*mem_ctx
, struct winbindd_domain
*domain
,
4825 uint32_t opnum
, const DATA_BLOB
*req
, DATA_BLOB
*resp
)
4830 if (!wcache_opnum_cacheable(opnum
) ||
4831 is_my_own_sam_domain(domain
) ||
4832 is_builtin_domain(domain
)) {
4836 if (wcache
->tdb
== NULL
) {
4840 if (!wcache_ndr_key(talloc_tos(), domain
->name
, opnum
, req
, &key
)) {
4843 data
= tdb_fetch(wcache
->tdb
, key
);
4844 TALLOC_FREE(key
.dptr
);
4846 if (data
.dptr
== NULL
) {
4849 if (data
.dsize
< 12) {
4853 if (is_domain_online(domain
)) {
4854 uint32_t entry_seqnum
, dom_seqnum
, last_check
;
4855 uint64_t entry_timeout
;
4857 if (!wcache_fetch_seqnum(domain
->name
, &dom_seqnum
,
4861 entry_seqnum
= IVAL(data
.dptr
, 0);
4862 if (entry_seqnum
!= dom_seqnum
) {
4863 DBG_DEBUG("Entry has wrong sequence number: %d\n",
4867 entry_timeout
= BVAL(data
.dptr
, 4);
4868 if (time(NULL
) > (time_t)entry_timeout
) {
4869 DBG_DEBUG("Entry has timed out\n");
4874 resp
->data
= (uint8_t *)talloc_memdup(mem_ctx
, data
.dptr
+ 12,
4876 if (resp
->data
== NULL
) {
4877 DBG_DEBUG("talloc failed\n");
4880 resp
->length
= data
.dsize
- 12;
4884 SAFE_FREE(data
.dptr
);
4888 void wcache_store_ndr(struct winbindd_domain
*domain
, uint32_t opnum
,
4889 const DATA_BLOB
*req
, const DATA_BLOB
*resp
)
4892 uint32_t dom_seqnum
, last_check
;
4895 if (!wcache_opnum_cacheable(opnum
) ||
4896 is_my_own_sam_domain(domain
) ||
4897 is_builtin_domain(domain
)) {
4901 if (wcache
->tdb
== NULL
) {
4905 if (!wcache_fetch_seqnum(domain
->name
, &dom_seqnum
, &last_check
)) {
4906 DBG_DEBUG("could not fetch seqnum for domain %s\n",
4911 if (!wcache_ndr_key(talloc_tos(), domain
->name
, opnum
, req
, &key
)) {
4915 timeout
= time(NULL
) + lp_winbind_cache_time();
4917 data
.dsize
= resp
->length
+ 12;
4918 data
.dptr
= talloc_array(key
.dptr
, uint8_t, data
.dsize
);
4919 if (data
.dptr
== NULL
) {
4923 SIVAL(data
.dptr
, 0, dom_seqnum
);
4924 SBVAL(data
.dptr
, 4, timeout
);
4925 memcpy(data
.dptr
+ 12, resp
->data
, resp
->length
);
4927 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
4930 TALLOC_FREE(key
.dptr
);