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"
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods
;
49 extern struct winbindd_methods ads_methods
;
51 extern struct winbindd_methods builtin_passdb_methods
;
52 extern struct winbindd_methods sam_passdb_methods
;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys
[] = {
63 WINBINDD_CACHE_VERSION_KEYSTR
,
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf
)
75 if (kbuf
.dptr
== NULL
|| kbuf
.dsize
== 0) {
78 for (i
= 0; non_centry_keys
[i
] != NULL
; i
++) {
79 size_t namelen
= strlen(non_centry_keys
[i
]);
80 if (kbuf
.dsize
< namelen
) {
83 if (strncmp(non_centry_keys
[i
], (const char *)kbuf
.dptr
, namelen
) == 0) {
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state
;
96 struct winbind_cache
{
102 uint32 sequence_number
;
108 void (*smb_panic_fn
)(const char *const why
) = smb_panic
;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache
*wcache
;
114 /* get the winbind_cache structure */
115 static struct winbind_cache
*get_cache(struct winbindd_domain
*domain
)
117 struct winbind_cache
*ret
= wcache
;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain
->internal
) {
122 domain
->backend
= &builtin_passdb_methods
;
125 if (dom_sid_equal(&domain
->sid
, &global_sid_Builtin
)) {
126 domain
->initialized
= true;
129 if (strequal(domain
->name
, get_global_sam_name()) &&
130 sid_check_is_our_sam(&domain
->sid
)) {
131 domain
->backend
= &sam_passdb_methods
;
134 if ( !domain
->initialized
) {
135 /* We do not need a connection to an RW DC for cache operation */
136 init_dc_connection(domain
, false);
140 OK. listen up becasue I'm only going to say this once.
141 We have the following scenarios to consider
142 (a) trusted AD domains on a Samba DC,
143 (b) trusted AD domains and we are joined to a non-kerberos domain
144 (c) trusted AD domains and we are joined to a kerberos (AD) domain
146 For (a) we can always contact the trusted domain using krb5
147 since we have the domain trust account password
149 For (b) we can only use RPC since we have no way of
150 getting a krb5 ticket in our own domain
152 For (c) we can always use krb5 since we have a kerberos trust
157 if (!domain
->backend
) {
159 struct winbindd_domain
*our_domain
= domain
;
161 /* find our domain first so we can figure out if we
162 are joined to a kerberized domain */
164 if ( !domain
->primary
)
165 our_domain
= find_our_domain();
167 if ((our_domain
->active_directory
|| IS_DC
)
168 && domain
->active_directory
169 && !lp_winbind_rpc_only()) {
170 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain
->name
));
171 domain
->backend
= &ads_methods
;
173 #endif /* HAVE_ADS */
174 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain
->name
));
175 domain
->backend
= &reconnect_methods
;
178 #endif /* HAVE_ADS */
184 ret
= SMB_XMALLOC_P(struct winbind_cache
);
188 wcache_flush_cache();
194 free a centry structure
196 static void centry_free(struct cache_entry
*centry
)
200 SAFE_FREE(centry
->data
);
204 static bool centry_check_bytes(struct cache_entry
*centry
, size_t nbytes
)
206 if (centry
->len
- centry
->ofs
< nbytes
) {
207 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
208 (unsigned int)nbytes
,
209 centry
->len
- centry
->ofs
));
216 pull a uint64_t from a cache entry
218 static uint64_t centry_uint64_t(struct cache_entry
*centry
)
222 if (!centry_check_bytes(centry
, 8)) {
223 smb_panic_fn("centry_uint64_t");
225 ret
= BVAL(centry
->data
, centry
->ofs
);
231 pull a uint32 from a cache entry
233 static uint32
centry_uint32(struct cache_entry
*centry
)
237 if (!centry_check_bytes(centry
, 4)) {
238 smb_panic_fn("centry_uint32");
240 ret
= IVAL(centry
->data
, centry
->ofs
);
246 pull a uint16 from a cache entry
248 static uint16
centry_uint16(struct cache_entry
*centry
)
251 if (!centry_check_bytes(centry
, 2)) {
252 smb_panic_fn("centry_uint16");
254 ret
= SVAL(centry
->data
, centry
->ofs
);
260 pull a uint8 from a cache entry
262 static uint8
centry_uint8(struct cache_entry
*centry
)
265 if (!centry_check_bytes(centry
, 1)) {
266 smb_panic_fn("centry_uint8");
268 ret
= CVAL(centry
->data
, centry
->ofs
);
274 pull a NTTIME from a cache entry
276 static NTTIME
centry_nttime(struct cache_entry
*centry
)
279 if (!centry_check_bytes(centry
, 8)) {
280 smb_panic_fn("centry_nttime");
282 ret
= IVAL(centry
->data
, centry
->ofs
);
284 ret
+= (uint64
)IVAL(centry
->data
, centry
->ofs
) << 32;
290 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
292 static time_t centry_time(struct cache_entry
*centry
)
294 return (time_t)centry_nttime(centry
);
297 /* pull a string from a cache entry, using the supplied
300 static char *centry_string(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
305 len
= centry_uint8(centry
);
308 /* a deliberate NULL string */
312 if (!centry_check_bytes(centry
, (size_t)len
)) {
313 smb_panic_fn("centry_string");
316 ret
= talloc_array(mem_ctx
, char, len
+1);
318 smb_panic_fn("centry_string out of memory\n");
320 memcpy(ret
,centry
->data
+ centry
->ofs
, len
);
326 /* pull a hash16 from a cache entry, using the supplied
329 static char *centry_hash16(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
334 len
= centry_uint8(centry
);
337 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
342 if (!centry_check_bytes(centry
, 16)) {
346 ret
= talloc_array(mem_ctx
, char, 16);
348 smb_panic_fn("centry_hash out of memory\n");
350 memcpy(ret
,centry
->data
+ centry
->ofs
, 16);
355 /* pull a sid from a cache entry, using the supplied
358 static bool centry_sid(struct cache_entry
*centry
, struct dom_sid
*sid
)
363 sid_string
= centry_string(centry
, talloc_tos());
364 if (sid_string
== NULL
) {
367 ret
= string_to_sid(sid
, sid_string
);
368 TALLOC_FREE(sid_string
);
374 pull a NTSTATUS from a cache entry
376 static NTSTATUS
centry_ntstatus(struct cache_entry
*centry
)
380 status
= NT_STATUS(centry_uint32(centry
));
385 /* the server is considered down if it can't give us a sequence number */
386 static bool wcache_server_down(struct winbindd_domain
*domain
)
393 ret
= (domain
->sequence_number
== DOM_SEQUENCE_NONE
);
396 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
401 static bool wcache_fetch_seqnum(const char *domain_name
, uint32_t *seqnum
,
402 uint32_t *last_seq_check
)
407 if (wcache
->tdb
== NULL
) {
408 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
412 key
= talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name
);
414 DEBUG(10, ("talloc failed\n"));
418 data
= tdb_fetch_bystring(wcache
->tdb
, key
);
421 if (data
.dptr
== NULL
) {
422 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
426 if (data
.dsize
!= 8) {
427 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
429 SAFE_FREE(data
.dptr
);
433 *seqnum
= IVAL(data
.dptr
, 0);
434 *last_seq_check
= IVAL(data
.dptr
, 4);
435 SAFE_FREE(data
.dptr
);
440 static NTSTATUS
fetch_cache_seqnum( struct winbindd_domain
*domain
, time_t now
)
442 uint32 last_check
, time_diff
;
444 if (!wcache_fetch_seqnum(domain
->name
, &domain
->sequence_number
,
446 return NT_STATUS_UNSUCCESSFUL
;
448 domain
->last_seq_check
= last_check
;
450 /* have we expired? */
452 time_diff
= now
- domain
->last_seq_check
;
453 if ( time_diff
> lp_winbind_cache_time() ) {
454 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
455 domain
->name
, domain
->sequence_number
,
456 (uint32
)domain
->last_seq_check
));
457 return NT_STATUS_UNSUCCESSFUL
;
460 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
461 domain
->name
, domain
->sequence_number
,
462 (uint32
)domain
->last_seq_check
));
467 bool wcache_store_seqnum(const char *domain_name
, uint32_t seqnum
,
468 time_t last_seq_check
)
474 if (wcache
->tdb
== NULL
) {
475 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
479 key_str
= talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name
);
480 if (key_str
== NULL
) {
481 DEBUG(10, ("talloc_asprintf failed\n"));
485 SIVAL(buf
, 0, seqnum
);
486 SIVAL(buf
, 4, last_seq_check
);
488 ret
= tdb_store_bystring(wcache
->tdb
, key_str
,
489 make_tdb_data(buf
, sizeof(buf
)), TDB_REPLACE
);
490 TALLOC_FREE(key_str
);
492 DEBUG(10, ("tdb_store_bystring failed: %s\n",
493 tdb_errorstr_compat(wcache
->tdb
)));
494 TALLOC_FREE(key_str
);
498 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
499 domain_name
, seqnum
, (unsigned)last_seq_check
));
504 static bool store_cache_seqnum( struct winbindd_domain
*domain
)
506 return wcache_store_seqnum(domain
->name
, domain
->sequence_number
,
507 domain
->last_seq_check
);
511 refresh the domain sequence number. If force is true
512 then always refresh it, no matter how recently we fetched it
515 static void refresh_sequence_number(struct winbindd_domain
*domain
, bool force
)
519 time_t t
= time(NULL
);
520 unsigned cache_time
= lp_winbind_cache_time();
522 if (is_domain_offline(domain
)) {
528 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
529 /* trying to reconnect is expensive, don't do it too often */
530 if (domain
->sequence_number
== DOM_SEQUENCE_NONE
) {
535 time_diff
= t
- domain
->last_seq_check
;
537 /* see if we have to refetch the domain sequence number */
538 if (!force
&& (time_diff
< cache_time
) &&
539 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
540 NT_STATUS_IS_OK(domain
->last_status
)) {
541 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain
->name
));
545 /* try to get the sequence number from the tdb cache first */
546 /* this will update the timestamp as well */
548 status
= fetch_cache_seqnum( domain
, t
);
549 if (NT_STATUS_IS_OK(status
) &&
550 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
551 NT_STATUS_IS_OK(domain
->last_status
)) {
555 /* important! make sure that we know if this is a native
556 mode domain or not. And that we can contact it. */
558 if ( winbindd_can_contact_domain( domain
) ) {
559 status
= domain
->backend
->sequence_number(domain
,
560 &domain
->sequence_number
);
562 /* just use the current time */
563 status
= NT_STATUS_OK
;
564 domain
->sequence_number
= time(NULL
);
568 /* the above call could have set our domain->backend to NULL when
569 * coming from offline to online mode, make sure to reinitialize the
570 * backend - Guenther */
573 if (!NT_STATUS_IS_OK(status
)) {
574 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status
)));
575 domain
->sequence_number
= DOM_SEQUENCE_NONE
;
578 domain
->last_status
= status
;
579 domain
->last_seq_check
= time(NULL
);
581 /* save the new sequence number in the cache */
582 store_cache_seqnum( domain
);
585 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
586 domain
->name
, domain
->sequence_number
));
592 decide if a cache entry has expired
594 static bool centry_expired(struct winbindd_domain
*domain
, const char *keystr
, struct cache_entry
*centry
)
596 /* If we've been told to be offline - stay in that state... */
597 if (lp_winbind_offline_logon() && global_winbindd_offline_state
) {
598 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
599 keystr
, domain
->name
));
603 /* when the domain is offline return the cached entry.
604 * This deals with transient offline states... */
606 if (!domain
->online
) {
607 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
608 keystr
, domain
->name
));
612 /* if the server is OK and our cache entry came from when it was down then
613 the entry is invalid */
614 if ((domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
615 (centry
->sequence_number
== DOM_SEQUENCE_NONE
)) {
616 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
617 keystr
, domain
->name
));
621 /* if the server is down or the cache entry is not older than the
622 current sequence number or it did not timeout then it is OK */
623 if (wcache_server_down(domain
)
624 || ((centry
->sequence_number
== domain
->sequence_number
)
625 && (centry
->timeout
> time(NULL
)))) {
626 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
627 keystr
, domain
->name
));
631 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
632 keystr
, domain
->name
));
638 static struct cache_entry
*wcache_fetch_raw(char *kstr
)
641 struct cache_entry
*centry
;
644 key
= string_tdb_data(kstr
);
645 data
= tdb_fetch_compat(wcache
->tdb
, key
);
651 centry
= SMB_XMALLOC_P(struct cache_entry
);
652 centry
->data
= (unsigned char *)data
.dptr
;
653 centry
->len
= data
.dsize
;
656 if (centry
->len
< 16) {
657 /* huh? corrupt cache? */
658 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
659 "(len < 16)?\n", kstr
));
664 centry
->status
= centry_ntstatus(centry
);
665 centry
->sequence_number
= centry_uint32(centry
);
666 centry
->timeout
= centry_uint64_t(centry
);
671 static bool is_my_own_sam_domain(struct winbindd_domain
*domain
)
673 if (strequal(domain
->name
, get_global_sam_name()) &&
674 sid_check_is_our_sam(&domain
->sid
)) {
681 static bool is_builtin_domain(struct winbindd_domain
*domain
)
683 if (strequal(domain
->name
, "BUILTIN") &&
684 sid_check_is_builtin(&domain
->sid
)) {
692 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
693 number and return status
695 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
696 struct winbindd_domain
*domain
,
697 const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
698 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
699 struct winbindd_domain
*domain
,
700 const char *format
, ...)
704 struct cache_entry
*centry
;
706 if (!winbindd_use_cache() ||
707 is_my_own_sam_domain(domain
) ||
708 is_builtin_domain(domain
)) {
712 refresh_sequence_number(domain
, false);
714 va_start(ap
, format
);
715 smb_xvasprintf(&kstr
, format
, ap
);
718 centry
= wcache_fetch_raw(kstr
);
719 if (centry
== NULL
) {
724 if (centry_expired(domain
, kstr
, centry
)) {
726 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
727 kstr
, domain
->name
));
734 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
735 kstr
, domain
->name
));
741 static void wcache_delete(const char *format
, ...) PRINTF_ATTRIBUTE(1,2);
742 static void wcache_delete(const char *format
, ...)
748 va_start(ap
, format
);
749 smb_xvasprintf(&kstr
, format
, ap
);
752 key
= string_tdb_data(kstr
);
754 tdb_delete(wcache
->tdb
, key
);
759 make sure we have at least len bytes available in a centry
761 static void centry_expand(struct cache_entry
*centry
, uint32 len
)
763 if (centry
->len
- centry
->ofs
>= len
)
766 centry
->data
= SMB_REALLOC_ARRAY(centry
->data
, unsigned char,
769 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry
->len
));
770 smb_panic_fn("out of memory in centry_expand");
775 push a uint64_t into a centry
777 static void centry_put_uint64_t(struct cache_entry
*centry
, uint64_t v
)
779 centry_expand(centry
, 8);
780 SBVAL(centry
->data
, centry
->ofs
, v
);
785 push a uint32 into a centry
787 static void centry_put_uint32(struct cache_entry
*centry
, uint32 v
)
789 centry_expand(centry
, 4);
790 SIVAL(centry
->data
, centry
->ofs
, v
);
795 push a uint16 into a centry
797 static void centry_put_uint16(struct cache_entry
*centry
, uint16 v
)
799 centry_expand(centry
, 2);
800 SSVAL(centry
->data
, centry
->ofs
, v
);
805 push a uint8 into a centry
807 static void centry_put_uint8(struct cache_entry
*centry
, uint8 v
)
809 centry_expand(centry
, 1);
810 SCVAL(centry
->data
, centry
->ofs
, v
);
815 push a string into a centry
817 static void centry_put_string(struct cache_entry
*centry
, const char *s
)
822 /* null strings are marked as len 0xFFFF */
823 centry_put_uint8(centry
, 0xFF);
828 /* can't handle more than 254 char strings. Truncating is probably best */
830 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len
));
833 centry_put_uint8(centry
, len
);
834 centry_expand(centry
, len
);
835 memcpy(centry
->data
+ centry
->ofs
, s
, len
);
840 push a 16 byte hash into a centry - treat as 16 byte string.
842 static void centry_put_hash16(struct cache_entry
*centry
, const uint8 val
[16])
844 centry_put_uint8(centry
, 16);
845 centry_expand(centry
, 16);
846 memcpy(centry
->data
+ centry
->ofs
, val
, 16);
850 static void centry_put_sid(struct cache_entry
*centry
, const struct dom_sid
*sid
)
853 centry_put_string(centry
, sid_to_fstring(sid_string
, sid
));
858 put NTSTATUS into a centry
860 static void centry_put_ntstatus(struct cache_entry
*centry
, NTSTATUS status
)
862 uint32 status_value
= NT_STATUS_V(status
);
863 centry_put_uint32(centry
, status_value
);
868 push a NTTIME into a centry
870 static void centry_put_nttime(struct cache_entry
*centry
, NTTIME nt
)
872 centry_expand(centry
, 8);
873 SIVAL(centry
->data
, centry
->ofs
, nt
& 0xFFFFFFFF);
875 SIVAL(centry
->data
, centry
->ofs
, nt
>> 32);
880 push a time_t into a centry - use a 64 bit size.
881 NTTIME here is being used as a convenient 64-bit size.
883 static void centry_put_time(struct cache_entry
*centry
, time_t t
)
885 NTTIME nt
= (NTTIME
)t
;
886 centry_put_nttime(centry
, nt
);
890 start a centry for output. When finished, call centry_end()
892 static struct cache_entry
*centry_start(struct winbindd_domain
*domain
,
895 struct cache_entry
*centry
;
900 centry
= SMB_XMALLOC_P(struct cache_entry
);
902 centry
->len
= 8192; /* reasonable default */
903 centry
->data
= SMB_XMALLOC_ARRAY(uint8
, centry
->len
);
905 centry
->sequence_number
= domain
->sequence_number
;
906 centry
->timeout
= lp_winbind_cache_time() + time(NULL
);
907 centry_put_ntstatus(centry
, status
);
908 centry_put_uint32(centry
, centry
->sequence_number
);
909 centry_put_uint64_t(centry
, centry
->timeout
);
914 finish a centry and write it to the tdb
916 static void centry_end(struct cache_entry
*centry
, const char *format
, ...) PRINTF_ATTRIBUTE(2,3);
917 static void centry_end(struct cache_entry
*centry
, const char *format
, ...)
923 if (!winbindd_use_cache()) {
927 va_start(ap
, format
);
928 smb_xvasprintf(&kstr
, format
, ap
);
931 key
= string_tdb_data(kstr
);
932 data
.dptr
= centry
->data
;
933 data
.dsize
= centry
->ofs
;
935 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
939 static void wcache_save_name_to_sid(struct winbindd_domain
*domain
,
940 NTSTATUS status
, const char *domain_name
,
941 const char *name
, const struct dom_sid
*sid
,
942 enum lsa_SidType type
)
944 struct cache_entry
*centry
;
947 centry
= centry_start(domain
, status
);
951 if ((domain_name
== NULL
) || (domain_name
[0] == '\0')) {
952 struct winbindd_domain
*mydomain
=
953 find_domain_from_sid_noinit(sid
);
954 if (mydomain
!= NULL
) {
955 domain_name
= mydomain
->name
;
959 centry_put_uint32(centry
, type
);
960 centry_put_sid(centry
, sid
);
961 fstrcpy(uname
, name
);
962 (void)strupper_m(uname
);
963 centry_end(centry
, "NS/%s/%s", domain_name
, uname
);
964 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name
,
965 uname
, sid_string_dbg(sid
), nt_errstr(status
)));
969 static void wcache_save_sid_to_name(struct winbindd_domain
*domain
, NTSTATUS status
,
970 const struct dom_sid
*sid
, const char *domain_name
, const char *name
, enum lsa_SidType type
)
972 struct cache_entry
*centry
;
975 centry
= centry_start(domain
, status
);
979 if ((domain_name
== NULL
) || (domain_name
[0] == '\0')) {
980 struct winbindd_domain
*mydomain
=
981 find_domain_from_sid_noinit(sid
);
982 if (mydomain
!= NULL
) {
983 domain_name
= mydomain
->name
;
987 if (NT_STATUS_IS_OK(status
)) {
988 centry_put_uint32(centry
, type
);
989 centry_put_string(centry
, domain_name
);
990 centry_put_string(centry
, name
);
993 centry_end(centry
, "SN/%s", sid_to_fstring(sid_string
, sid
));
994 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string
,
995 domain_name
, name
, nt_errstr(status
)));
1000 static void wcache_save_user(struct winbindd_domain
*domain
, NTSTATUS status
,
1001 struct wbint_userinfo
*info
)
1003 struct cache_entry
*centry
;
1006 if (is_null_sid(&info
->user_sid
)) {
1010 centry
= centry_start(domain
, status
);
1013 centry_put_string(centry
, info
->acct_name
);
1014 centry_put_string(centry
, info
->full_name
);
1015 centry_put_string(centry
, info
->homedir
);
1016 centry_put_string(centry
, info
->shell
);
1017 centry_put_uint32(centry
, info
->primary_gid
);
1018 centry_put_sid(centry
, &info
->user_sid
);
1019 centry_put_sid(centry
, &info
->group_sid
);
1020 centry_end(centry
, "U/%s", sid_to_fstring(sid_string
,
1022 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string
, info
->acct_name
));
1023 centry_free(centry
);
1026 static void wcache_save_lockout_policy(struct winbindd_domain
*domain
,
1028 struct samr_DomInfo12
*lockout_policy
)
1030 struct cache_entry
*centry
;
1032 centry
= centry_start(domain
, status
);
1036 centry_put_nttime(centry
, lockout_policy
->lockout_duration
);
1037 centry_put_nttime(centry
, lockout_policy
->lockout_window
);
1038 centry_put_uint16(centry
, lockout_policy
->lockout_threshold
);
1040 centry_end(centry
, "LOC_POL/%s", domain
->name
);
1042 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain
->name
));
1044 centry_free(centry
);
1049 static void wcache_save_password_policy(struct winbindd_domain
*domain
,
1051 struct samr_DomInfo1
*policy
)
1053 struct cache_entry
*centry
;
1055 centry
= centry_start(domain
, status
);
1059 centry_put_uint16(centry
, policy
->min_password_length
);
1060 centry_put_uint16(centry
, policy
->password_history_length
);
1061 centry_put_uint32(centry
, policy
->password_properties
);
1062 centry_put_nttime(centry
, policy
->max_password_age
);
1063 centry_put_nttime(centry
, policy
->min_password_age
);
1065 centry_end(centry
, "PWD_POL/%s", domain
->name
);
1067 DEBUG(10,("wcache_save_password_policy: %s\n", domain
->name
));
1069 centry_free(centry
);
1072 /***************************************************************************
1073 ***************************************************************************/
1075 static void wcache_save_username_alias(struct winbindd_domain
*domain
,
1077 const char *name
, const char *alias
)
1079 struct cache_entry
*centry
;
1082 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1085 centry_put_string( centry
, alias
);
1087 fstrcpy(uname
, name
);
1088 (void)strupper_m(uname
);
1089 centry_end(centry
, "NSS/NA/%s", uname
);
1091 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name
, alias
));
1093 centry_free(centry
);
1096 static void wcache_save_alias_username(struct winbindd_domain
*domain
,
1098 const char *alias
, const char *name
)
1100 struct cache_entry
*centry
;
1103 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1106 centry_put_string( centry
, name
);
1108 fstrcpy(uname
, alias
);
1109 (void)strupper_m(uname
);
1110 centry_end(centry
, "NSS/AN/%s", uname
);
1112 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias
, name
));
1114 centry_free(centry
);
1117 /***************************************************************************
1118 ***************************************************************************/
1120 NTSTATUS
resolve_username_to_alias( TALLOC_CTX
*mem_ctx
,
1121 struct winbindd_domain
*domain
,
1122 const char *name
, char **alias
)
1124 struct winbind_cache
*cache
= get_cache(domain
);
1125 struct cache_entry
*centry
= NULL
;
1129 if ( domain
->internal
)
1130 return NT_STATUS_NOT_SUPPORTED
;
1135 upper_name
= talloc_strdup(mem_ctx
, name
);
1136 if (upper_name
== NULL
) {
1137 return NT_STATUS_NO_MEMORY
;
1139 if (!strupper_m(upper_name
)) {
1140 talloc_free(upper_name
);
1141 return NT_STATUS_INVALID_PARAMETER
;
1144 centry
= wcache_fetch(cache
, domain
, "NSS/NA/%s", upper_name
);
1146 talloc_free(upper_name
);
1151 status
= centry
->status
;
1153 if (!NT_STATUS_IS_OK(status
)) {
1154 centry_free(centry
);
1158 *alias
= centry_string( centry
, mem_ctx
);
1160 centry_free(centry
);
1162 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1163 name
, *alias
? *alias
: "(none)"));
1165 return (*alias
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1169 /* If its not in cache and we are offline, then fail */
1171 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1172 DEBUG(8,("resolve_username_to_alias: rejecting query "
1173 "in offline mode\n"));
1174 return NT_STATUS_NOT_FOUND
;
1177 status
= nss_map_to_alias( mem_ctx
, domain
->name
, name
, alias
);
1179 if ( NT_STATUS_IS_OK( status
) ) {
1180 wcache_save_username_alias(domain
, status
, name
, *alias
);
1183 if ( NT_STATUS_EQUAL( status
, NT_STATUS_NONE_MAPPED
) ) {
1184 wcache_save_username_alias(domain
, status
, name
, "(NULL)");
1187 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1188 nt_errstr(status
)));
1190 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1191 set_domain_offline( domain
);
1197 /***************************************************************************
1198 ***************************************************************************/
1200 NTSTATUS
resolve_alias_to_username( TALLOC_CTX
*mem_ctx
,
1201 struct winbindd_domain
*domain
,
1202 const char *alias
, char **name
)
1204 struct winbind_cache
*cache
= get_cache(domain
);
1205 struct cache_entry
*centry
= NULL
;
1209 if ( domain
->internal
)
1210 return NT_STATUS_NOT_SUPPORTED
;
1215 upper_name
= talloc_strdup(mem_ctx
, alias
);
1216 if (upper_name
== NULL
) {
1217 return NT_STATUS_NO_MEMORY
;
1219 if (!strupper_m(upper_name
)) {
1220 talloc_free(upper_name
);
1221 return NT_STATUS_INVALID_PARAMETER
;
1224 centry
= wcache_fetch(cache
, domain
, "NSS/AN/%s", upper_name
);
1226 talloc_free(upper_name
);
1231 status
= centry
->status
;
1233 if (!NT_STATUS_IS_OK(status
)) {
1234 centry_free(centry
);
1238 *name
= centry_string( centry
, mem_ctx
);
1240 centry_free(centry
);
1242 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1243 alias
, *name
? *name
: "(none)"));
1245 return (*name
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1249 /* If its not in cache and we are offline, then fail */
1251 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1252 DEBUG(8,("resolve_alias_to_username: rejecting query "
1253 "in offline mode\n"));
1254 return NT_STATUS_NOT_FOUND
;
1257 /* an alias cannot contain a domain prefix or '@' */
1259 if (strchr(alias
, '\\') || strchr(alias
, '@')) {
1260 DEBUG(10,("resolve_alias_to_username: skipping fully "
1261 "qualified name %s\n", alias
));
1262 return NT_STATUS_OBJECT_NAME_INVALID
;
1265 status
= nss_map_from_alias( mem_ctx
, domain
->name
, alias
, name
);
1267 if ( NT_STATUS_IS_OK( status
) ) {
1268 wcache_save_alias_username( domain
, status
, alias
, *name
);
1271 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
1272 wcache_save_alias_username(domain
, status
, alias
, "(NULL)");
1275 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1276 nt_errstr(status
)));
1278 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1279 set_domain_offline( domain
);
1285 NTSTATUS
wcache_cached_creds_exist(struct winbindd_domain
*domain
, const struct dom_sid
*sid
)
1287 struct winbind_cache
*cache
= get_cache(domain
);
1289 fstring key_str
, tmp
;
1293 return NT_STATUS_INTERNAL_DB_ERROR
;
1296 if (is_null_sid(sid
)) {
1297 return NT_STATUS_INVALID_SID
;
1300 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1301 return NT_STATUS_INVALID_SID
;
1304 fstr_sprintf(key_str
, "CRED/%s", sid_to_fstring(tmp
, sid
));
1306 data
= tdb_fetch_compat(cache
->tdb
, string_tdb_data(key_str
));
1308 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1311 SAFE_FREE(data
.dptr
);
1312 return NT_STATUS_OK
;
1315 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1316 as new salted ones. */
1318 NTSTATUS
wcache_get_creds(struct winbindd_domain
*domain
,
1319 TALLOC_CTX
*mem_ctx
,
1320 const struct dom_sid
*sid
,
1321 const uint8
**cached_nt_pass
,
1322 const uint8
**cached_salt
)
1324 struct winbind_cache
*cache
= get_cache(domain
);
1325 struct cache_entry
*centry
= NULL
;
1331 return NT_STATUS_INTERNAL_DB_ERROR
;
1334 if (is_null_sid(sid
)) {
1335 return NT_STATUS_INVALID_SID
;
1338 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1339 return NT_STATUS_INVALID_SID
;
1342 /* Try and get a salted cred first. If we can't
1343 fall back to an unsalted cred. */
1345 centry
= wcache_fetch(cache
, domain
, "CRED/%s",
1346 sid_to_fstring(tmp
, sid
));
1348 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1349 sid_string_dbg(sid
)));
1350 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1354 * We don't use the time element at this moment,
1355 * but we have to consume it, so that we don't
1356 * neet to change the disk format of the cache.
1358 (void)centry_time(centry
);
1360 /* In the salted case this isn't actually the nt_hash itself,
1361 but the MD5 of the salt + nt_hash. Let the caller
1362 sort this out. It can tell as we only return the cached_salt
1363 if we are returning a salted cred. */
1365 *cached_nt_pass
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1366 if (*cached_nt_pass
== NULL
) {
1369 sid_to_fstring(sidstr
, sid
);
1371 /* Bad (old) cred cache. Delete and pretend we
1373 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1375 wcache_delete("CRED/%s", sidstr
);
1376 centry_free(centry
);
1377 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1380 /* We only have 17 bytes more data in the salted cred case. */
1381 if (centry
->len
- centry
->ofs
== 17) {
1382 *cached_salt
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1384 *cached_salt
= NULL
;
1387 dump_data_pw("cached_nt_pass", *cached_nt_pass
, NT_HASH_LEN
);
1389 dump_data_pw("cached_salt", *cached_salt
, NT_HASH_LEN
);
1392 status
= centry
->status
;
1394 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1395 sid_string_dbg(sid
), nt_errstr(status
) ));
1397 centry_free(centry
);
1401 /* Store creds for a SID - only writes out new salted ones. */
1403 NTSTATUS
wcache_save_creds(struct winbindd_domain
*domain
,
1404 const struct dom_sid
*sid
,
1405 const uint8 nt_pass
[NT_HASH_LEN
])
1407 struct cache_entry
*centry
;
1410 uint8 cred_salt
[NT_HASH_LEN
];
1411 uint8 salted_hash
[NT_HASH_LEN
];
1413 if (is_null_sid(sid
)) {
1414 return NT_STATUS_INVALID_SID
;
1417 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1418 return NT_STATUS_INVALID_SID
;
1421 centry
= centry_start(domain
, NT_STATUS_OK
);
1423 return NT_STATUS_INTERNAL_DB_ERROR
;
1426 dump_data_pw("nt_pass", nt_pass
, NT_HASH_LEN
);
1428 centry_put_time(centry
, time(NULL
));
1430 /* Create a salt and then salt the hash. */
1431 generate_random_buffer(cred_salt
, NT_HASH_LEN
);
1432 E_md5hash(cred_salt
, nt_pass
, salted_hash
);
1434 centry_put_hash16(centry
, salted_hash
);
1435 centry_put_hash16(centry
, cred_salt
);
1436 centry_end(centry
, "CRED/%s", sid_to_fstring(sid_string
, sid
));
1438 DEBUG(10,("wcache_save_creds: %s\n", sid_string
));
1440 centry_free(centry
);
1442 return NT_STATUS_OK
;
1446 /* Query display info. This is the basic user list fn */
1447 static NTSTATUS
query_user_list(struct winbindd_domain
*domain
,
1448 TALLOC_CTX
*mem_ctx
,
1449 uint32
*num_entries
,
1450 struct wbint_userinfo
**info
)
1452 struct winbind_cache
*cache
= get_cache(domain
);
1453 struct cache_entry
*centry
= NULL
;
1455 unsigned int i
, retry
;
1456 bool old_status
= domain
->online
;
1461 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1466 *num_entries
= centry_uint32(centry
);
1468 if (*num_entries
== 0)
1471 (*info
) = talloc_array(mem_ctx
, struct wbint_userinfo
, *num_entries
);
1473 smb_panic_fn("query_user_list out of memory");
1475 for (i
=0; i
<(*num_entries
); i
++) {
1476 (*info
)[i
].acct_name
= centry_string(centry
, mem_ctx
);
1477 (*info
)[i
].full_name
= centry_string(centry
, mem_ctx
);
1478 (*info
)[i
].homedir
= centry_string(centry
, mem_ctx
);
1479 (*info
)[i
].shell
= centry_string(centry
, mem_ctx
);
1480 centry_sid(centry
, &(*info
)[i
].user_sid
);
1481 centry_sid(centry
, &(*info
)[i
].group_sid
);
1485 status
= centry
->status
;
1487 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1488 domain
->name
, nt_errstr(status
) ));
1490 centry_free(centry
);
1497 /* Return status value returned by seq number check */
1499 if (!NT_STATUS_IS_OK(domain
->last_status
))
1500 return domain
->last_status
;
1502 /* Put the query_user_list() in a retry loop. There appears to be
1503 * some bug either with Windows 2000 or Samba's handling of large
1504 * rpc replies. This manifests itself as sudden disconnection
1505 * at a random point in the enumeration of a large (60k) user list.
1506 * The retry loop simply tries the operation again. )-: It's not
1507 * pretty but an acceptable workaround until we work out what the
1508 * real problem is. */
1513 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1516 status
= domain
->backend
->query_user_list(domain
, mem_ctx
, num_entries
, info
);
1517 if (!NT_STATUS_IS_OK(status
)) {
1518 DEBUG(3, ("query_user_list: returned 0x%08x, "
1519 "retrying\n", NT_STATUS_V(status
)));
1521 if (NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1522 DEBUG(3, ("query_user_list: flushing "
1523 "connection cache\n"));
1524 invalidate_cm_connection(domain
);
1526 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1527 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1528 if (!domain
->internal
&& old_status
) {
1529 set_domain_offline(domain
);
1531 /* store partial response. */
1532 if (*num_entries
> 0) {
1534 * humm, what about the status used for cache?
1535 * Should it be NT_STATUS_OK?
1540 * domain is offline now, and there is no user entries,
1541 * try to fetch from cache again.
1543 if (cache
->tdb
&& !domain
->online
&& !domain
->internal
&& old_status
) {
1544 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1545 /* partial response... */
1549 goto do_fetch_cache
;
1556 } while (NT_STATUS_V(status
) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL
) &&
1560 refresh_sequence_number(domain
, false);
1561 if (!NT_STATUS_IS_OK(status
)) {
1564 centry
= centry_start(domain
, status
);
1567 centry_put_uint32(centry
, *num_entries
);
1568 for (i
=0; i
<(*num_entries
); i
++) {
1569 centry_put_string(centry
, (*info
)[i
].acct_name
);
1570 centry_put_string(centry
, (*info
)[i
].full_name
);
1571 centry_put_string(centry
, (*info
)[i
].homedir
);
1572 centry_put_string(centry
, (*info
)[i
].shell
);
1573 centry_put_sid(centry
, &(*info
)[i
].user_sid
);
1574 centry_put_sid(centry
, &(*info
)[i
].group_sid
);
1575 if (domain
->backend
&& domain
->backend
->consistent
) {
1576 /* when the backend is consistent we can pre-prime some mappings */
1577 wcache_save_name_to_sid(domain
, NT_STATUS_OK
,
1579 (*info
)[i
].acct_name
,
1580 &(*info
)[i
].user_sid
,
1582 wcache_save_sid_to_name(domain
, NT_STATUS_OK
,
1583 &(*info
)[i
].user_sid
,
1585 (*info
)[i
].acct_name
,
1587 wcache_save_user(domain
, NT_STATUS_OK
, &(*info
)[i
]);
1590 centry_end(centry
, "UL/%s", domain
->name
);
1591 centry_free(centry
);
1597 /* list all domain groups */
1598 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
1599 TALLOC_CTX
*mem_ctx
,
1600 uint32
*num_entries
,
1601 struct wb_acct_info
**info
)
1603 struct winbind_cache
*cache
= get_cache(domain
);
1604 struct cache_entry
*centry
= NULL
;
1609 old_status
= domain
->online
;
1613 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1618 *num_entries
= centry_uint32(centry
);
1620 if (*num_entries
== 0)
1623 (*info
) = talloc_array(mem_ctx
, struct wb_acct_info
, *num_entries
);
1625 smb_panic_fn("enum_dom_groups out of memory");
1627 for (i
=0; i
<(*num_entries
); i
++) {
1628 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1629 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1630 (*info
)[i
].rid
= centry_uint32(centry
);
1634 status
= centry
->status
;
1636 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1637 domain
->name
, nt_errstr(status
) ));
1639 centry_free(centry
);
1646 /* Return status value returned by seq number check */
1648 if (!NT_STATUS_IS_OK(domain
->last_status
))
1649 return domain
->last_status
;
1651 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1654 status
= domain
->backend
->enum_dom_groups(domain
, mem_ctx
, num_entries
, info
);
1656 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1657 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1658 if (!domain
->internal
&& old_status
) {
1659 set_domain_offline(domain
);
1663 !domain
->internal
&&
1665 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1667 goto do_fetch_cache
;
1672 refresh_sequence_number(domain
, false);
1673 if (!NT_STATUS_IS_OK(status
)) {
1676 centry
= centry_start(domain
, status
);
1679 centry_put_uint32(centry
, *num_entries
);
1680 for (i
=0; i
<(*num_entries
); i
++) {
1681 centry_put_string(centry
, (*info
)[i
].acct_name
);
1682 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1683 centry_put_uint32(centry
, (*info
)[i
].rid
);
1685 centry_end(centry
, "GL/%s/domain", domain
->name
);
1686 centry_free(centry
);
1692 /* list all domain groups */
1693 static NTSTATUS
enum_local_groups(struct winbindd_domain
*domain
,
1694 TALLOC_CTX
*mem_ctx
,
1695 uint32
*num_entries
,
1696 struct wb_acct_info
**info
)
1698 struct winbind_cache
*cache
= get_cache(domain
);
1699 struct cache_entry
*centry
= NULL
;
1704 old_status
= domain
->online
;
1708 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1713 *num_entries
= centry_uint32(centry
);
1715 if (*num_entries
== 0)
1718 (*info
) = talloc_array(mem_ctx
, struct wb_acct_info
, *num_entries
);
1720 smb_panic_fn("enum_dom_groups out of memory");
1722 for (i
=0; i
<(*num_entries
); i
++) {
1723 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1724 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1725 (*info
)[i
].rid
= centry_uint32(centry
);
1730 /* If we are returning cached data and the domain controller
1731 is down then we don't know whether the data is up to date
1732 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1735 if (wcache_server_down(domain
)) {
1736 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1737 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1739 status
= centry
->status
;
1741 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1742 domain
->name
, nt_errstr(status
) ));
1744 centry_free(centry
);
1751 /* Return status value returned by seq number check */
1753 if (!NT_STATUS_IS_OK(domain
->last_status
))
1754 return domain
->last_status
;
1756 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1759 status
= domain
->backend
->enum_local_groups(domain
, mem_ctx
, num_entries
, info
);
1761 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1762 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1763 if (!domain
->internal
&& old_status
) {
1764 set_domain_offline(domain
);
1767 !domain
->internal
&&
1770 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1772 goto do_fetch_cache
;
1777 refresh_sequence_number(domain
, false);
1778 if (!NT_STATUS_IS_OK(status
)) {
1781 centry
= centry_start(domain
, status
);
1784 centry_put_uint32(centry
, *num_entries
);
1785 for (i
=0; i
<(*num_entries
); i
++) {
1786 centry_put_string(centry
, (*info
)[i
].acct_name
);
1787 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1788 centry_put_uint32(centry
, (*info
)[i
].rid
);
1790 centry_end(centry
, "GL/%s/local", domain
->name
);
1791 centry_free(centry
);
1797 NTSTATUS
wcache_name_to_sid(struct winbindd_domain
*domain
,
1798 const char *domain_name
,
1800 struct dom_sid
*sid
,
1801 enum lsa_SidType
*type
)
1803 struct winbind_cache
*cache
= get_cache(domain
);
1804 struct cache_entry
*centry
;
1808 if (cache
->tdb
== NULL
) {
1809 return NT_STATUS_NOT_FOUND
;
1812 uname
= talloc_strdup_upper(talloc_tos(), name
);
1813 if (uname
== NULL
) {
1814 return NT_STATUS_NO_MEMORY
;
1817 if ((domain_name
== NULL
) || (domain_name
[0] == '\0')) {
1818 domain_name
= domain
->name
;
1821 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
1823 if (centry
== NULL
) {
1824 return NT_STATUS_NOT_FOUND
;
1827 status
= centry
->status
;
1828 if (NT_STATUS_IS_OK(status
)) {
1829 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1830 centry_sid(centry
, sid
);
1833 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1834 "%s\n", domain
->name
, nt_errstr(status
) ));
1836 centry_free(centry
);
1840 /* convert a single name to a sid in a domain */
1841 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
1842 TALLOC_CTX
*mem_ctx
,
1843 const char *domain_name
,
1846 struct dom_sid
*sid
,
1847 enum lsa_SidType
*type
)
1852 old_status
= domain
->online
;
1854 status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
1855 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
1861 /* If the seq number check indicated that there is a problem
1862 * with this DC, then return that status... except for
1863 * access_denied. This is special because the dc may be in
1864 * "restrict anonymous = 1" mode, in which case it will deny
1865 * most unauthenticated operations, but *will* allow the LSA
1866 * name-to-sid that we try as a fallback. */
1868 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1869 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1870 return domain
->last_status
;
1872 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1875 status
= domain
->backend
->name_to_sid(domain
, mem_ctx
, domain_name
,
1876 name
, flags
, sid
, type
);
1878 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1879 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1880 if (!domain
->internal
&& old_status
) {
1881 set_domain_offline(domain
);
1883 if (!domain
->internal
&&
1886 NTSTATUS cache_status
;
1887 cache_status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
1888 return cache_status
;
1892 refresh_sequence_number(domain
, false);
1894 if (domain
->online
&&
1895 (NT_STATUS_IS_OK(status
) || NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))) {
1896 wcache_save_name_to_sid(domain
, status
, domain_name
, name
, sid
, *type
);
1898 /* Only save the reverse mapping if this was not a UPN */
1899 if (!strchr(name
, '@')) {
1900 if (!strupper_m(discard_const_p(char, domain_name
))) {
1901 return NT_STATUS_INVALID_PARAMETER
;
1903 (void)strlower_m(discard_const_p(char, name
));
1904 wcache_save_sid_to_name(domain
, status
, sid
, domain_name
, name
, *type
);
1911 static NTSTATUS
wcache_sid_to_name(struct winbindd_domain
*domain
,
1912 const struct dom_sid
*sid
,
1913 TALLOC_CTX
*mem_ctx
,
1916 enum lsa_SidType
*type
)
1918 struct winbind_cache
*cache
= get_cache(domain
);
1919 struct cache_entry
*centry
;
1923 if (cache
->tdb
== NULL
) {
1924 return NT_STATUS_NOT_FOUND
;
1927 sid_string
= sid_string_tos(sid
);
1928 if (sid_string
== NULL
) {
1929 return NT_STATUS_NO_MEMORY
;
1932 centry
= wcache_fetch(cache
, domain
, "SN/%s", sid_string
);
1933 TALLOC_FREE(sid_string
);
1934 if (centry
== NULL
) {
1935 return NT_STATUS_NOT_FOUND
;
1938 if (NT_STATUS_IS_OK(centry
->status
)) {
1939 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1940 *domain_name
= centry_string(centry
, mem_ctx
);
1941 *name
= centry_string(centry
, mem_ctx
);
1944 status
= centry
->status
;
1945 centry_free(centry
);
1947 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1948 "%s\n", domain
->name
, nt_errstr(status
) ));
1953 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1955 static NTSTATUS
sid_to_name(struct winbindd_domain
*domain
,
1956 TALLOC_CTX
*mem_ctx
,
1957 const struct dom_sid
*sid
,
1960 enum lsa_SidType
*type
)
1965 old_status
= domain
->online
;
1966 status
= wcache_sid_to_name(domain
, sid
, mem_ctx
, domain_name
, name
,
1968 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
1973 *domain_name
= NULL
;
1975 /* If the seq number check indicated that there is a problem
1976 * with this DC, then return that status... except for
1977 * access_denied. This is special because the dc may be in
1978 * "restrict anonymous = 1" mode, in which case it will deny
1979 * most unauthenticated operations, but *will* allow the LSA
1980 * sid-to-name that we try as a fallback. */
1982 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1983 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1984 return domain
->last_status
;
1986 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1989 status
= domain
->backend
->sid_to_name(domain
, mem_ctx
, sid
, domain_name
, name
, type
);
1991 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
1992 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
1993 if (!domain
->internal
&& old_status
) {
1994 set_domain_offline(domain
);
1996 if (!domain
->internal
&&
1999 NTSTATUS cache_status
;
2000 cache_status
= wcache_sid_to_name(domain
, sid
, mem_ctx
,
2001 domain_name
, name
, type
);
2002 return cache_status
;
2006 refresh_sequence_number(domain
, false);
2007 if (!NT_STATUS_IS_OK(status
)) {
2010 wcache_save_sid_to_name(domain
, status
, sid
, *domain_name
, *name
, *type
);
2012 /* We can't save the name to sid mapping here, as with sid history a
2013 * later name2sid would give the wrong sid. */
2018 static NTSTATUS
rids_to_names(struct winbindd_domain
*domain
,
2019 TALLOC_CTX
*mem_ctx
,
2020 const struct dom_sid
*domain_sid
,
2025 enum lsa_SidType
**types
)
2027 struct winbind_cache
*cache
= get_cache(domain
);
2029 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
2034 old_status
= domain
->online
;
2035 *domain_name
= NULL
;
2043 if (num_rids
== 0) {
2044 return NT_STATUS_OK
;
2047 *names
= talloc_array(mem_ctx
, char *, num_rids
);
2048 *types
= talloc_array(mem_ctx
, enum lsa_SidType
, num_rids
);
2050 if ((*names
== NULL
) || (*types
== NULL
)) {
2051 result
= NT_STATUS_NO_MEMORY
;
2055 have_mapped
= have_unmapped
= false;
2057 for (i
=0; i
<num_rids
; i
++) {
2059 struct cache_entry
*centry
;
2062 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2063 result
= NT_STATUS_INTERNAL_ERROR
;
2067 centry
= wcache_fetch(cache
, domain
, "SN/%s",
2068 sid_to_fstring(tmp
, &sid
));
2073 (*types
)[i
] = SID_NAME_UNKNOWN
;
2074 (*names
)[i
] = talloc_strdup(*names
, "");
2076 if (NT_STATUS_IS_OK(centry
->status
)) {
2079 (*types
)[i
] = (enum lsa_SidType
)centry_uint32(centry
);
2081 dom
= centry_string(centry
, mem_ctx
);
2082 if (*domain_name
== NULL
) {
2088 (*names
)[i
] = centry_string(centry
, *names
);
2090 } else if (NT_STATUS_EQUAL(centry
->status
, NT_STATUS_NONE_MAPPED
)
2091 || NT_STATUS_EQUAL(centry
->status
, STATUS_SOME_UNMAPPED
)) {
2092 have_unmapped
= true;
2095 /* something's definitely wrong */
2096 result
= centry
->status
;
2097 centry_free(centry
);
2101 centry_free(centry
);
2105 return NT_STATUS_NONE_MAPPED
;
2107 if (!have_unmapped
) {
2108 return NT_STATUS_OK
;
2110 return STATUS_SOME_UNMAPPED
;
2114 TALLOC_FREE(*names
);
2115 TALLOC_FREE(*types
);
2117 result
= domain
->backend
->rids_to_names(domain
, mem_ctx
, domain_sid
,
2118 rids
, num_rids
, domain_name
,
2121 if (NT_STATUS_EQUAL(result
, NT_STATUS_IO_TIMEOUT
) ||
2122 NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2123 if (!domain
->internal
&& old_status
) {
2124 set_domain_offline(domain
);
2127 !domain
->internal
&&
2130 have_mapped
= have_unmapped
= false;
2132 *names
= talloc_array(mem_ctx
, char *, num_rids
);
2133 if (*names
== NULL
) {
2134 result
= NT_STATUS_NO_MEMORY
;
2138 *types
= talloc_array(mem_ctx
, enum lsa_SidType
,
2140 if (*types
== NULL
) {
2141 result
= NT_STATUS_NO_MEMORY
;
2145 for (i
=0; i
<num_rids
; i
++) {
2147 struct cache_entry
*centry
;
2150 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2151 result
= NT_STATUS_INTERNAL_ERROR
;
2155 centry
= wcache_fetch(cache
, domain
, "SN/%s",
2156 sid_to_fstring(tmp
, &sid
));
2158 (*types
)[i
] = SID_NAME_UNKNOWN
;
2159 (*names
)[i
] = talloc_strdup(*names
, "");
2163 (*types
)[i
] = SID_NAME_UNKNOWN
;
2164 (*names
)[i
] = talloc_strdup(*names
, "");
2166 if (NT_STATUS_IS_OK(centry
->status
)) {
2169 (*types
)[i
] = (enum lsa_SidType
)centry_uint32(centry
);
2171 dom
= centry_string(centry
, mem_ctx
);
2172 if (*domain_name
== NULL
) {
2178 (*names
)[i
] = centry_string(centry
, *names
);
2180 } else if (NT_STATUS_EQUAL(centry
->status
, NT_STATUS_NONE_MAPPED
)) {
2181 have_unmapped
= true;
2184 /* something's definitely wrong */
2185 result
= centry
->status
;
2186 centry_free(centry
);
2190 centry_free(centry
);
2194 return NT_STATUS_NONE_MAPPED
;
2196 if (!have_unmapped
) {
2197 return NT_STATUS_OK
;
2199 return STATUS_SOME_UNMAPPED
;
2203 None of the queried rids has been found so save all negative entries
2205 if (NT_STATUS_EQUAL(result
, NT_STATUS_NONE_MAPPED
)) {
2206 for (i
= 0; i
< num_rids
; i
++) {
2208 const char *name
= "";
2209 const enum lsa_SidType type
= SID_NAME_UNKNOWN
;
2210 NTSTATUS status
= NT_STATUS_NONE_MAPPED
;
2212 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2213 return NT_STATUS_INTERNAL_ERROR
;
2216 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
2224 Some or all of the queried rids have been found.
2226 if (!NT_STATUS_IS_OK(result
) &&
2227 !NT_STATUS_EQUAL(result
, STATUS_SOME_UNMAPPED
)) {
2231 refresh_sequence_number(domain
, false);
2233 for (i
=0; i
<num_rids
; i
++) {
2237 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
2238 result
= NT_STATUS_INTERNAL_ERROR
;
2242 status
= (*types
)[i
] == SID_NAME_UNKNOWN
?
2243 NT_STATUS_NONE_MAPPED
: NT_STATUS_OK
;
2245 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
2246 (*names
)[i
], (*types
)[i
]);
2252 TALLOC_FREE(*names
);
2253 TALLOC_FREE(*types
);
2257 NTSTATUS
wcache_query_user(struct winbindd_domain
*domain
,
2258 TALLOC_CTX
*mem_ctx
,
2259 const struct dom_sid
*user_sid
,
2260 struct wbint_userinfo
*info
)
2262 struct winbind_cache
*cache
= get_cache(domain
);
2263 struct cache_entry
*centry
= NULL
;
2267 if (cache
->tdb
== NULL
) {
2268 return NT_STATUS_NOT_FOUND
;
2271 sid_string
= sid_string_tos(user_sid
);
2272 if (sid_string
== NULL
) {
2273 return NT_STATUS_NO_MEMORY
;
2276 centry
= wcache_fetch(cache
, domain
, "U/%s", sid_string
);
2277 TALLOC_FREE(sid_string
);
2278 if (centry
== NULL
) {
2279 return NT_STATUS_NOT_FOUND
;
2283 * If we have an access denied cache entry and a cached info3
2284 * in the samlogon cache then do a query. This will force the
2285 * rpc back end to return the info3 data.
2288 if (NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
) &&
2289 netsamlogon_cache_have(user_sid
)) {
2290 DEBUG(10, ("query_user: cached access denied and have cached "
2292 domain
->last_status
= NT_STATUS_OK
;
2293 centry_free(centry
);
2294 return NT_STATUS_NOT_FOUND
;
2297 /* if status is not ok then this is a negative hit
2298 and the rest of the data doesn't matter */
2299 status
= centry
->status
;
2300 if (NT_STATUS_IS_OK(status
)) {
2301 info
->acct_name
= centry_string(centry
, mem_ctx
);
2302 info
->full_name
= centry_string(centry
, mem_ctx
);
2303 info
->homedir
= centry_string(centry
, mem_ctx
);
2304 info
->shell
= centry_string(centry
, mem_ctx
);
2305 info
->primary_gid
= centry_uint32(centry
);
2306 centry_sid(centry
, &info
->user_sid
);
2307 centry_sid(centry
, &info
->group_sid
);
2310 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2311 "%s\n", domain
->name
, nt_errstr(status
) ));
2313 centry_free(centry
);
2319 * @brief Query a fullname from the username cache (for further gecos processing)
2321 * @param domain A pointer to the winbindd_domain struct.
2322 * @param mem_ctx The talloc context.
2323 * @param user_sid The user sid.
2324 * @param full_name A pointer to the full_name string.
2326 * @return NTSTATUS code
2328 NTSTATUS
wcache_query_user_fullname(struct winbindd_domain
*domain
,
2329 TALLOC_CTX
*mem_ctx
,
2330 const struct dom_sid
*user_sid
,
2331 const char **full_name
)
2334 struct wbint_userinfo info
;
2336 status
= wcache_query_user(domain
, mem_ctx
, user_sid
, &info
);
2337 if (!NT_STATUS_IS_OK(status
)) {
2341 if (info
.full_name
!= NULL
) {
2342 *full_name
= talloc_strdup(mem_ctx
, info
.full_name
);
2343 if (*full_name
== NULL
) {
2344 return NT_STATUS_NO_MEMORY
;
2348 return NT_STATUS_OK
;
2351 /* Lookup user information from a rid */
2352 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
2353 TALLOC_CTX
*mem_ctx
,
2354 const struct dom_sid
*user_sid
,
2355 struct wbint_userinfo
*info
)
2360 old_status
= domain
->online
;
2361 status
= wcache_query_user(domain
, mem_ctx
, user_sid
, info
);
2362 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2368 /* Return status value returned by seq number check */
2370 if (!NT_STATUS_IS_OK(domain
->last_status
))
2371 return domain
->last_status
;
2373 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2376 status
= domain
->backend
->query_user(domain
, mem_ctx
, user_sid
, info
);
2378 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2379 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2380 if (!domain
->internal
&& old_status
) {
2381 set_domain_offline(domain
);
2383 if (!domain
->internal
&&
2386 NTSTATUS cache_status
;
2387 cache_status
= wcache_query_user(domain
, mem_ctx
, user_sid
, info
);
2388 return cache_status
;
2392 refresh_sequence_number(domain
, false);
2393 if (!NT_STATUS_IS_OK(status
)) {
2396 wcache_save_user(domain
, status
, info
);
2401 NTSTATUS
wcache_lookup_usergroups(struct winbindd_domain
*domain
,
2402 TALLOC_CTX
*mem_ctx
,
2403 const struct dom_sid
*user_sid
,
2404 uint32_t *pnum_sids
,
2405 struct dom_sid
**psids
)
2407 struct winbind_cache
*cache
= get_cache(domain
);
2408 struct cache_entry
*centry
= NULL
;
2410 uint32_t i
, num_sids
;
2411 struct dom_sid
*sids
;
2414 if (cache
->tdb
== NULL
) {
2415 return NT_STATUS_NOT_FOUND
;
2418 centry
= wcache_fetch(cache
, domain
, "UG/%s",
2419 sid_to_fstring(sid_string
, user_sid
));
2420 if (centry
== NULL
) {
2421 return NT_STATUS_NOT_FOUND
;
2424 /* If we have an access denied cache entry and a cached info3 in the
2425 samlogon cache then do a query. This will force the rpc back end
2426 to return the info3 data. */
2428 if (NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)
2429 && netsamlogon_cache_have(user_sid
)) {
2430 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2432 domain
->last_status
= NT_STATUS_OK
;
2433 centry_free(centry
);
2434 return NT_STATUS_NOT_FOUND
;
2437 num_sids
= centry_uint32(centry
);
2438 sids
= talloc_array(mem_ctx
, struct dom_sid
, num_sids
);
2440 centry_free(centry
);
2441 return NT_STATUS_NO_MEMORY
;
2444 for (i
=0; i
<num_sids
; i
++) {
2445 centry_sid(centry
, &sids
[i
]);
2448 status
= centry
->status
;
2450 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2451 "status: %s\n", domain
->name
, nt_errstr(status
)));
2453 centry_free(centry
);
2455 *pnum_sids
= num_sids
;
2460 /* Lookup groups a user is a member of. */
2461 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
2462 TALLOC_CTX
*mem_ctx
,
2463 const struct dom_sid
*user_sid
,
2464 uint32
*num_groups
, struct dom_sid
**user_gids
)
2466 struct cache_entry
*centry
= NULL
;
2472 old_status
= domain
->online
;
2473 status
= wcache_lookup_usergroups(domain
, mem_ctx
, user_sid
,
2474 num_groups
, user_gids
);
2475 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2480 (*user_gids
) = NULL
;
2482 /* Return status value returned by seq number check */
2484 if (!NT_STATUS_IS_OK(domain
->last_status
))
2485 return domain
->last_status
;
2487 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2490 status
= domain
->backend
->lookup_usergroups(domain
, mem_ctx
, user_sid
, num_groups
, user_gids
);
2492 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2493 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2494 if (!domain
->internal
&& old_status
) {
2495 set_domain_offline(domain
);
2497 if (!domain
->internal
&&
2500 NTSTATUS cache_status
;
2501 cache_status
= wcache_lookup_usergroups(domain
, mem_ctx
, user_sid
,
2502 num_groups
, user_gids
);
2503 return cache_status
;
2506 if ( NT_STATUS_EQUAL(status
, NT_STATUS_SYNCHRONIZATION_REQUIRED
) )
2510 refresh_sequence_number(domain
, false);
2511 if (!NT_STATUS_IS_OK(status
)) {
2514 centry
= centry_start(domain
, status
);
2518 centry_put_uint32(centry
, *num_groups
);
2519 for (i
=0; i
<(*num_groups
); i
++) {
2520 centry_put_sid(centry
, &(*user_gids
)[i
]);
2523 centry_end(centry
, "UG/%s", sid_to_fstring(sid_string
, user_sid
));
2524 centry_free(centry
);
2530 static char *wcache_make_sidlist(TALLOC_CTX
*mem_ctx
, uint32_t num_sids
,
2531 const struct dom_sid
*sids
)
2536 sidlist
= talloc_strdup(mem_ctx
, "");
2537 if (sidlist
== NULL
) {
2540 for (i
=0; i
<num_sids
; i
++) {
2542 sidlist
= talloc_asprintf_append_buffer(
2543 sidlist
, "/%s", sid_to_fstring(tmp
, &sids
[i
]));
2544 if (sidlist
== NULL
) {
2551 NTSTATUS
wcache_lookup_useraliases(struct winbindd_domain
*domain
,
2552 TALLOC_CTX
*mem_ctx
, uint32_t num_sids
,
2553 const struct dom_sid
*sids
,
2554 uint32_t *pnum_aliases
, uint32_t **paliases
)
2556 struct winbind_cache
*cache
= get_cache(domain
);
2557 struct cache_entry
*centry
= NULL
;
2558 uint32_t num_aliases
;
2564 if (cache
->tdb
== NULL
) {
2565 return NT_STATUS_NOT_FOUND
;
2568 if (num_sids
== 0) {
2571 return NT_STATUS_OK
;
2574 /* We need to cache indexed by the whole list of SIDs, the aliases
2575 * resulting might come from any of the SIDs. */
2577 sidlist
= wcache_make_sidlist(talloc_tos(), num_sids
, sids
);
2578 if (sidlist
== NULL
) {
2579 return NT_STATUS_NO_MEMORY
;
2582 centry
= wcache_fetch(cache
, domain
, "UA%s", sidlist
);
2583 TALLOC_FREE(sidlist
);
2584 if (centry
== NULL
) {
2585 return NT_STATUS_NOT_FOUND
;
2588 num_aliases
= centry_uint32(centry
);
2589 aliases
= talloc_array(mem_ctx
, uint32_t, num_aliases
);
2590 if (aliases
== NULL
) {
2591 centry_free(centry
);
2592 return NT_STATUS_NO_MEMORY
;
2595 for (i
=0; i
<num_aliases
; i
++) {
2596 aliases
[i
] = centry_uint32(centry
);
2599 status
= centry
->status
;
2601 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2602 "status %s\n", domain
->name
, nt_errstr(status
)));
2604 centry_free(centry
);
2606 *pnum_aliases
= num_aliases
;
2607 *paliases
= aliases
;
2612 static NTSTATUS
lookup_useraliases(struct winbindd_domain
*domain
,
2613 TALLOC_CTX
*mem_ctx
,
2614 uint32 num_sids
, const struct dom_sid
*sids
,
2615 uint32
*num_aliases
, uint32
**alias_rids
)
2617 struct cache_entry
*centry
= NULL
;
2623 old_status
= domain
->online
;
2624 status
= wcache_lookup_useraliases(domain
, mem_ctx
, num_sids
, sids
,
2625 num_aliases
, alias_rids
);
2626 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2631 (*alias_rids
) = NULL
;
2633 if (!NT_STATUS_IS_OK(domain
->last_status
))
2634 return domain
->last_status
;
2636 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2637 "for domain %s\n", domain
->name
));
2639 sidlist
= wcache_make_sidlist(talloc_tos(), num_sids
, sids
);
2640 if (sidlist
== NULL
) {
2641 return NT_STATUS_NO_MEMORY
;
2644 status
= domain
->backend
->lookup_useraliases(domain
, mem_ctx
,
2646 num_aliases
, alias_rids
);
2648 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2649 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2650 if (!domain
->internal
&& old_status
) {
2651 set_domain_offline(domain
);
2653 if (!domain
->internal
&&
2656 NTSTATUS cache_status
;
2657 cache_status
= wcache_lookup_useraliases(domain
, mem_ctx
, num_sids
,
2658 sids
, num_aliases
, alias_rids
);
2659 return cache_status
;
2663 refresh_sequence_number(domain
, false);
2664 if (!NT_STATUS_IS_OK(status
)) {
2667 centry
= centry_start(domain
, status
);
2670 centry_put_uint32(centry
, *num_aliases
);
2671 for (i
=0; i
<(*num_aliases
); i
++)
2672 centry_put_uint32(centry
, (*alias_rids
)[i
]);
2673 centry_end(centry
, "UA%s", sidlist
);
2674 centry_free(centry
);
2680 NTSTATUS
wcache_lookup_groupmem(struct winbindd_domain
*domain
,
2681 TALLOC_CTX
*mem_ctx
,
2682 const struct dom_sid
*group_sid
,
2683 uint32_t *num_names
,
2684 struct dom_sid
**sid_mem
, char ***names
,
2685 uint32_t **name_types
)
2687 struct winbind_cache
*cache
= get_cache(domain
);
2688 struct cache_entry
*centry
= NULL
;
2693 if (cache
->tdb
== NULL
) {
2694 return NT_STATUS_NOT_FOUND
;
2697 sid_string
= sid_string_tos(group_sid
);
2698 if (sid_string
== NULL
) {
2699 return NT_STATUS_NO_MEMORY
;
2702 centry
= wcache_fetch(cache
, domain
, "GM/%s", sid_string
);
2703 TALLOC_FREE(sid_string
);
2704 if (centry
== NULL
) {
2705 return NT_STATUS_NOT_FOUND
;
2712 *num_names
= centry_uint32(centry
);
2713 if (*num_names
== 0) {
2714 centry_free(centry
);
2715 return NT_STATUS_OK
;
2718 *sid_mem
= talloc_array(mem_ctx
, struct dom_sid
, *num_names
);
2719 *names
= talloc_array(mem_ctx
, char *, *num_names
);
2720 *name_types
= talloc_array(mem_ctx
, uint32
, *num_names
);
2722 if ((*sid_mem
== NULL
) || (*names
== NULL
) || (*name_types
== NULL
)) {
2723 TALLOC_FREE(*sid_mem
);
2724 TALLOC_FREE(*names
);
2725 TALLOC_FREE(*name_types
);
2726 centry_free(centry
);
2727 return NT_STATUS_NO_MEMORY
;
2730 for (i
=0; i
<(*num_names
); i
++) {
2731 centry_sid(centry
, &(*sid_mem
)[i
]);
2732 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2733 (*name_types
)[i
] = centry_uint32(centry
);
2736 status
= centry
->status
;
2738 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2739 "status: %s\n", domain
->name
, nt_errstr(status
)));
2741 centry_free(centry
);
2745 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
2746 TALLOC_CTX
*mem_ctx
,
2747 const struct dom_sid
*group_sid
,
2748 enum lsa_SidType type
,
2750 struct dom_sid
**sid_mem
, char ***names
,
2751 uint32
**name_types
)
2753 struct cache_entry
*centry
= NULL
;
2759 old_status
= domain
->online
;
2760 status
= wcache_lookup_groupmem(domain
, mem_ctx
, group_sid
, num_names
,
2761 sid_mem
, names
, name_types
);
2762 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
2769 (*name_types
) = NULL
;
2771 /* Return status value returned by seq number check */
2773 if (!NT_STATUS_IS_OK(domain
->last_status
))
2774 return domain
->last_status
;
2776 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2779 status
= domain
->backend
->lookup_groupmem(domain
, mem_ctx
, group_sid
,
2781 sid_mem
, names
, name_types
);
2783 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2784 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2785 if (!domain
->internal
&& old_status
) {
2786 set_domain_offline(domain
);
2788 if (!domain
->internal
&&
2791 NTSTATUS cache_status
;
2792 cache_status
= wcache_lookup_groupmem(domain
, mem_ctx
, group_sid
,
2793 num_names
, sid_mem
, names
,
2795 return cache_status
;
2799 refresh_sequence_number(domain
, false);
2800 if (!NT_STATUS_IS_OK(status
)) {
2803 centry
= centry_start(domain
, status
);
2806 centry_put_uint32(centry
, *num_names
);
2807 for (i
=0; i
<(*num_names
); i
++) {
2808 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2809 centry_put_string(centry
, (*names
)[i
]);
2810 centry_put_uint32(centry
, (*name_types
)[i
]);
2812 centry_end(centry
, "GM/%s", sid_to_fstring(sid_string
, group_sid
));
2813 centry_free(centry
);
2819 /* find the sequence number for a domain */
2820 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
2822 refresh_sequence_number(domain
, false);
2824 *seq
= domain
->sequence_number
;
2826 return NT_STATUS_OK
;
2829 /* enumerate trusted domains
2830 * (we need to have the list of trustdoms in the cache when we go offline) -
2832 static NTSTATUS
trusted_domains(struct winbindd_domain
*domain
,
2833 TALLOC_CTX
*mem_ctx
,
2834 struct netr_DomainTrustList
*trusts
)
2837 struct winbind_cache
*cache
;
2838 struct winbindd_tdc_domain
*dom_list
= NULL
;
2839 size_t num_domains
= 0;
2840 bool retval
= false;
2844 old_status
= domain
->online
;
2846 trusts
->array
= NULL
;
2848 cache
= get_cache(domain
);
2849 if (!cache
|| !cache
->tdb
) {
2853 if (domain
->online
) {
2857 retval
= wcache_tdc_fetch_list(&dom_list
, &num_domains
);
2858 if (!retval
|| !num_domains
|| !dom_list
) {
2859 TALLOC_FREE(dom_list
);
2864 trusts
->array
= talloc_zero_array(mem_ctx
, struct netr_DomainTrust
, num_domains
);
2865 if (!trusts
->array
) {
2866 TALLOC_FREE(dom_list
);
2867 return NT_STATUS_NO_MEMORY
;
2870 for (i
= 0; i
< num_domains
; i
++) {
2871 struct netr_DomainTrust
*trust
;
2872 struct dom_sid
*sid
;
2873 struct winbindd_domain
*dom
;
2875 dom
= find_domain_from_name_noinit(dom_list
[i
].domain_name
);
2876 if (dom
&& dom
->internal
) {
2880 trust
= &trusts
->array
[trusts
->count
];
2881 trust
->netbios_name
= talloc_strdup(trusts
->array
, dom_list
[i
].domain_name
);
2882 trust
->dns_name
= talloc_strdup(trusts
->array
, dom_list
[i
].dns_name
);
2883 sid
= talloc(trusts
->array
, struct dom_sid
);
2884 if (!trust
->netbios_name
|| !trust
->dns_name
||
2886 TALLOC_FREE(dom_list
);
2887 TALLOC_FREE(trusts
->array
);
2888 return NT_STATUS_NO_MEMORY
;
2891 trust
->trust_flags
= dom_list
[i
].trust_flags
;
2892 trust
->trust_attributes
= dom_list
[i
].trust_attribs
;
2893 trust
->trust_type
= dom_list
[i
].trust_type
;
2894 sid_copy(sid
, &dom_list
[i
].sid
);
2899 TALLOC_FREE(dom_list
);
2900 return NT_STATUS_OK
;
2903 /* Return status value returned by seq number check */
2905 if (!NT_STATUS_IS_OK(domain
->last_status
))
2906 return domain
->last_status
;
2908 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2911 status
= domain
->backend
->trusted_domains(domain
, mem_ctx
, trusts
);
2913 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2914 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2915 if (!domain
->internal
&& old_status
) {
2916 set_domain_offline(domain
);
2918 if (!domain
->internal
&&
2921 retval
= wcache_tdc_fetch_list(&dom_list
, &num_domains
);
2922 if (retval
&& num_domains
&& dom_list
) {
2923 TALLOC_FREE(trusts
->array
);
2925 goto do_fetch_cache
;
2929 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2930 * so that the generic centry handling still applies correctly -
2933 if (!NT_STATUS_IS_ERR(status
)) {
2934 status
= NT_STATUS_OK
;
2939 /* get lockout policy */
2940 static NTSTATUS
lockout_policy(struct winbindd_domain
*domain
,
2941 TALLOC_CTX
*mem_ctx
,
2942 struct samr_DomInfo12
*policy
)
2944 struct winbind_cache
*cache
= get_cache(domain
);
2945 struct cache_entry
*centry
= NULL
;
2949 old_status
= domain
->online
;
2953 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2959 policy
->lockout_duration
= centry_nttime(centry
);
2960 policy
->lockout_window
= centry_nttime(centry
);
2961 policy
->lockout_threshold
= centry_uint16(centry
);
2963 status
= centry
->status
;
2965 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2966 domain
->name
, nt_errstr(status
) ));
2968 centry_free(centry
);
2972 ZERO_STRUCTP(policy
);
2974 /* Return status value returned by seq number check */
2976 if (!NT_STATUS_IS_OK(domain
->last_status
))
2977 return domain
->last_status
;
2979 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2982 status
= domain
->backend
->lockout_policy(domain
, mem_ctx
, policy
);
2984 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
2985 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
2986 if (!domain
->internal
&& old_status
) {
2987 set_domain_offline(domain
);
2990 !domain
->internal
&&
2993 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2995 goto do_fetch_cache
;
3000 refresh_sequence_number(domain
, false);
3001 if (!NT_STATUS_IS_OK(status
)) {
3004 wcache_save_lockout_policy(domain
, status
, policy
);
3009 /* get password policy */
3010 static NTSTATUS
password_policy(struct winbindd_domain
*domain
,
3011 TALLOC_CTX
*mem_ctx
,
3012 struct samr_DomInfo1
*policy
)
3014 struct winbind_cache
*cache
= get_cache(domain
);
3015 struct cache_entry
*centry
= NULL
;
3019 old_status
= domain
->online
;
3023 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
3029 policy
->min_password_length
= centry_uint16(centry
);
3030 policy
->password_history_length
= centry_uint16(centry
);
3031 policy
->password_properties
= centry_uint32(centry
);
3032 policy
->max_password_age
= centry_nttime(centry
);
3033 policy
->min_password_age
= centry_nttime(centry
);
3035 status
= centry
->status
;
3037 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3038 domain
->name
, nt_errstr(status
) ));
3040 centry_free(centry
);
3044 ZERO_STRUCTP(policy
);
3046 /* Return status value returned by seq number check */
3048 if (!NT_STATUS_IS_OK(domain
->last_status
))
3049 return domain
->last_status
;
3051 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3054 status
= domain
->backend
->password_policy(domain
, mem_ctx
, policy
);
3056 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
) ||
3057 NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
)) {
3058 if (!domain
->internal
&& old_status
) {
3059 set_domain_offline(domain
);
3062 !domain
->internal
&&
3065 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
3067 goto do_fetch_cache
;
3072 refresh_sequence_number(domain
, false);
3073 if (!NT_STATUS_IS_OK(status
)) {
3076 wcache_save_password_policy(domain
, status
, policy
);
3082 /* Invalidate cached user and group lists coherently */
3084 static int traverse_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3087 if (strncmp((const char *)kbuf
.dptr
, "UL/", 3) == 0 ||
3088 strncmp((const char *)kbuf
.dptr
, "GL/", 3) == 0)
3089 tdb_delete(the_tdb
, kbuf
);
3094 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3096 void wcache_invalidate_samlogon(struct winbindd_domain
*domain
,
3097 const struct dom_sid
*sid
)
3099 fstring key_str
, sid_string
;
3100 struct winbind_cache
*cache
;
3102 /* dont clear cached U/SID and UG/SID entries when we want to logon
3105 if (lp_winbind_offline_logon()) {
3112 cache
= get_cache(domain
);
3118 /* Clear U/SID cache entry */
3119 fstr_sprintf(key_str
, "U/%s", sid_to_fstring(sid_string
, sid
));
3120 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
3121 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3123 /* Clear UG/SID cache entry */
3124 fstr_sprintf(key_str
, "UG/%s", sid_to_fstring(sid_string
, sid
));
3125 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
3126 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3128 /* Samba/winbindd never needs this. */
3129 netsamlogon_clear_cached_user(sid
);
3132 bool wcache_invalidate_cache(void)
3134 struct winbindd_domain
*domain
;
3136 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
3137 struct winbind_cache
*cache
= get_cache(domain
);
3139 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3140 "entries for %s\n", domain
->name
));
3143 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
3152 bool wcache_invalidate_cache_noinit(void)
3154 struct winbindd_domain
*domain
;
3156 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
3157 struct winbind_cache
*cache
;
3159 /* Skip uninitialized domains. */
3160 if (!domain
->initialized
&& !domain
->internal
) {
3164 cache
= get_cache(domain
);
3166 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3167 "entries for %s\n", domain
->name
));
3170 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
3172 * Flushing cache has nothing to with domains.
3173 * return here if we successfully flushed once.
3174 * To avoid unnecessary traversing the cache.
3185 bool init_wcache(void)
3189 if (wcache
== NULL
) {
3190 wcache
= SMB_XMALLOC_P(struct winbind_cache
);
3191 ZERO_STRUCTP(wcache
);
3194 if (wcache
->tdb
!= NULL
)
3197 db_path
= state_path("winbindd_cache.tdb");
3198 if (db_path
== NULL
) {
3202 /* when working offline we must not clear the cache on restart */
3203 wcache
->tdb
= tdb_open_log(db_path
,
3204 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3205 TDB_INCOMPATIBLE_HASH
|
3206 (lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
)),
3207 O_RDWR
|O_CREAT
, 0600);
3208 TALLOC_FREE(db_path
);
3209 if (wcache
->tdb
== NULL
) {
3210 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3217 /************************************************************************
3218 This is called by the parent to initialize the cache file.
3219 We don't need sophisticated locking here as we know we're the
3221 ************************************************************************/
3223 bool initialize_winbindd_cache(void)
3225 bool cache_bad
= true;
3228 if (!init_wcache()) {
3229 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3233 /* Check version number. */
3234 if (tdb_fetch_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers
) &&
3235 vers
== WINBINDD_CACHE_VERSION
) {
3242 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3243 "and re-creating with version number %d\n",
3244 WINBINDD_CACHE_VERSION
));
3246 tdb_close(wcache
->tdb
);
3249 db_path
= state_path("winbindd_cache.tdb");
3250 if (db_path
== NULL
) {
3254 if (unlink(db_path
) == -1) {
3255 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3258 TALLOC_FREE(db_path
);
3261 TALLOC_FREE(db_path
);
3262 if (!init_wcache()) {
3263 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3264 "init_wcache failed.\n"));
3268 /* Write the version. */
3269 if (!tdb_store_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
)) {
3270 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3271 tdb_errorstr_compat(wcache
->tdb
) ));
3276 tdb_close(wcache
->tdb
);
3281 void close_winbindd_cache(void)
3287 tdb_close(wcache
->tdb
);
3292 bool lookup_cached_sid(TALLOC_CTX
*mem_ctx
, const struct dom_sid
*sid
,
3293 char **domain_name
, char **name
,
3294 enum lsa_SidType
*type
)
3296 struct winbindd_domain
*domain
;
3299 domain
= find_lookup_domain_from_sid(sid
);
3300 if (domain
== NULL
) {
3303 status
= wcache_sid_to_name(domain
, sid
, mem_ctx
, domain_name
, name
,
3305 return NT_STATUS_IS_OK(status
);
3308 bool lookup_cached_name(const char *domain_name
,
3310 struct dom_sid
*sid
,
3311 enum lsa_SidType
*type
)
3313 struct winbindd_domain
*domain
;
3315 bool original_online_state
;
3317 domain
= find_lookup_domain_from_name(domain_name
);
3318 if (domain
== NULL
) {
3322 /* If we are doing a cached logon, temporarily set the domain
3323 offline so the cache won't expire the entry */
3325 original_online_state
= domain
->online
;
3326 domain
->online
= false;
3327 status
= wcache_name_to_sid(domain
, domain_name
, name
, sid
, type
);
3328 domain
->online
= original_online_state
;
3330 return NT_STATUS_IS_OK(status
);
3333 void cache_name2sid(struct winbindd_domain
*domain
,
3334 const char *domain_name
, const char *name
,
3335 enum lsa_SidType type
, const struct dom_sid
*sid
)
3337 refresh_sequence_number(domain
, false);
3338 wcache_save_name_to_sid(domain
, NT_STATUS_OK
, domain_name
, name
,
3343 * The original idea that this cache only contains centries has
3344 * been blurred - now other stuff gets put in here. Ensure we
3345 * ignore these things on cleanup.
3348 static int traverse_fn_cleanup(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
,
3349 TDB_DATA dbuf
, void *state
)
3351 struct cache_entry
*centry
;
3353 if (is_non_centry_key(kbuf
)) {
3357 centry
= wcache_fetch_raw((char *)kbuf
.dptr
);
3362 if (!NT_STATUS_IS_OK(centry
->status
)) {
3363 DEBUG(10,("deleting centry %s\n", (const char *)kbuf
.dptr
));
3364 tdb_delete(the_tdb
, kbuf
);
3367 centry_free(centry
);
3371 /* flush the cache */
3372 void wcache_flush_cache(void)
3379 tdb_close(wcache
->tdb
);
3382 if (!winbindd_use_cache()) {
3386 db_path
= state_path("winbindd_cache.tdb");
3387 if (db_path
== NULL
) {
3391 /* when working offline we must not clear the cache on restart */
3392 wcache
->tdb
= tdb_open_log(db_path
,
3393 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3394 TDB_INCOMPATIBLE_HASH
|
3395 (lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
)),
3396 O_RDWR
|O_CREAT
, 0600);
3397 TALLOC_FREE(db_path
);
3399 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3403 tdb_traverse(wcache
->tdb
, traverse_fn_cleanup
, NULL
);
3405 DEBUG(10,("wcache_flush_cache success\n"));
3408 /* Count cached creds */
3410 static int traverse_fn_cached_creds(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3413 int *cred_count
= (int*)state
;
3415 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
3421 NTSTATUS
wcache_count_cached_creds(struct winbindd_domain
*domain
, int *count
)
3423 struct winbind_cache
*cache
= get_cache(domain
);
3428 return NT_STATUS_INTERNAL_DB_ERROR
;
3431 tdb_traverse(cache
->tdb
, traverse_fn_cached_creds
, (void *)count
);
3433 return NT_STATUS_OK
;
3437 struct cred_list
*prev
, *next
;
3442 static struct cred_list
*wcache_cred_list
;
3444 static int traverse_fn_get_credlist(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
3447 struct cred_list
*cred
;
3449 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
3451 cred
= SMB_MALLOC_P(struct cred_list
);
3453 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3459 /* save a copy of the key */
3461 fstrcpy(cred
->name
, (const char *)kbuf
.dptr
);
3462 DLIST_ADD(wcache_cred_list
, cred
);
3468 NTSTATUS
wcache_remove_oldest_cached_creds(struct winbindd_domain
*domain
, const struct dom_sid
*sid
)
3470 struct winbind_cache
*cache
= get_cache(domain
);
3473 struct cred_list
*cred
, *oldest
= NULL
;
3476 return NT_STATUS_INTERNAL_DB_ERROR
;
3479 /* we possibly already have an entry */
3480 if (sid
&& NT_STATUS_IS_OK(wcache_cached_creds_exist(domain
, sid
))) {
3482 fstring key_str
, tmp
;
3484 DEBUG(11,("we already have an entry, deleting that\n"));
3486 fstr_sprintf(key_str
, "CRED/%s", sid_to_fstring(tmp
, sid
));
3488 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
3490 return NT_STATUS_OK
;
3493 ret
= tdb_traverse(cache
->tdb
, traverse_fn_get_credlist
, NULL
);
3495 return NT_STATUS_OK
;
3496 } else if ((ret
< 0) || (wcache_cred_list
== NULL
)) {
3497 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
3500 ZERO_STRUCTP(oldest
);
3502 for (cred
= wcache_cred_list
; cred
; cred
= cred
->next
) {
3507 data
= tdb_fetch_compat(cache
->tdb
, string_tdb_data(cred
->name
));
3509 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3511 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
3515 t
= IVAL(data
.dptr
, 0);
3516 SAFE_FREE(data
.dptr
);
3519 oldest
= SMB_MALLOC_P(struct cred_list
);
3520 if (oldest
== NULL
) {
3521 status
= NT_STATUS_NO_MEMORY
;
3525 fstrcpy(oldest
->name
, cred
->name
);
3526 oldest
->created
= t
;
3530 if (t
< oldest
->created
) {
3531 fstrcpy(oldest
->name
, cred
->name
);
3532 oldest
->created
= t
;
3536 if (tdb_delete(cache
->tdb
, string_tdb_data(oldest
->name
)) == 0) {
3537 status
= NT_STATUS_OK
;
3539 status
= NT_STATUS_UNSUCCESSFUL
;
3542 SAFE_FREE(wcache_cred_list
);
3548 /* Change the global online/offline state. */
3549 bool set_global_winbindd_state_offline(void)
3553 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3555 /* Only go offline if someone has created
3556 the key "WINBINDD_OFFLINE" in the cache tdb. */
3558 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
3559 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3563 if (!lp_winbind_offline_logon()) {
3564 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3568 if (global_winbindd_offline_state
) {
3569 /* Already offline. */
3573 data
= tdb_fetch_bystring( wcache
->tdb
, "WINBINDD_OFFLINE" );
3575 if (!data
.dptr
|| data
.dsize
!= 4) {
3576 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3577 SAFE_FREE(data
.dptr
);
3580 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3581 global_winbindd_offline_state
= true;
3582 SAFE_FREE(data
.dptr
);
3587 void set_global_winbindd_state_online(void)
3589 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3591 if (!lp_winbind_offline_logon()) {
3592 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3596 if (!global_winbindd_offline_state
) {
3597 /* Already online. */
3600 global_winbindd_offline_state
= false;
3606 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3607 tdb_delete_bystring(wcache
->tdb
, "WINBINDD_OFFLINE");
3610 bool get_global_winbindd_state_offline(void)
3612 return global_winbindd_offline_state
;
3615 /***********************************************************************
3616 Validate functions for all possible cache tdb keys.
3617 ***********************************************************************/
3619 static struct cache_entry
*create_centry_validate(const char *kstr
, TDB_DATA data
,
3620 struct tdb_validation_status
*state
)
3622 struct cache_entry
*centry
;
3624 centry
= SMB_XMALLOC_P(struct cache_entry
);
3625 centry
->data
= (unsigned char *)smb_memdup(data
.dptr
, data
.dsize
);
3626 if (!centry
->data
) {
3630 centry
->len
= data
.dsize
;
3633 if (centry
->len
< 16) {
3634 /* huh? corrupt cache? */
3635 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3636 "(len < 16) ?\n", kstr
));
3637 centry_free(centry
);
3638 state
->bad_entry
= true;
3639 state
->success
= false;
3643 centry
->status
= NT_STATUS(centry_uint32(centry
));
3644 centry
->sequence_number
= centry_uint32(centry
);
3645 centry
->timeout
= centry_uint64_t(centry
);
3649 static int validate_seqnum(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3650 struct tdb_validation_status
*state
)
3652 if (dbuf
.dsize
!= 8) {
3653 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3654 keystr
, (unsigned int)dbuf
.dsize
));
3655 state
->bad_entry
= true;
3661 static int validate_ns(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3662 struct tdb_validation_status
*state
)
3664 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3669 (void)centry_uint32(centry
);
3670 if (NT_STATUS_IS_OK(centry
->status
)) {
3672 (void)centry_sid(centry
, &sid
);
3675 centry_free(centry
);
3677 if (!(state
->success
)) {
3680 DEBUG(10,("validate_ns: %s ok\n", keystr
));
3684 static int validate_sn(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3685 struct tdb_validation_status
*state
)
3687 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3692 if (NT_STATUS_IS_OK(centry
->status
)) {
3693 (void)centry_uint32(centry
);
3694 (void)centry_string(centry
, mem_ctx
);
3695 (void)centry_string(centry
, mem_ctx
);
3698 centry_free(centry
);
3700 if (!(state
->success
)) {
3703 DEBUG(10,("validate_sn: %s ok\n", keystr
));
3707 static int validate_u(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3708 struct tdb_validation_status
*state
)
3710 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3717 (void)centry_string(centry
, mem_ctx
);
3718 (void)centry_string(centry
, mem_ctx
);
3719 (void)centry_string(centry
, mem_ctx
);
3720 (void)centry_string(centry
, mem_ctx
);
3721 (void)centry_uint32(centry
);
3722 (void)centry_sid(centry
, &sid
);
3723 (void)centry_sid(centry
, &sid
);
3725 centry_free(centry
);
3727 if (!(state
->success
)) {
3730 DEBUG(10,("validate_u: %s ok\n", keystr
));
3734 static int validate_loc_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3735 struct tdb_validation_status
*state
)
3737 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3743 (void)centry_nttime(centry
);
3744 (void)centry_nttime(centry
);
3745 (void)centry_uint16(centry
);
3747 centry_free(centry
);
3749 if (!(state
->success
)) {
3752 DEBUG(10,("validate_loc_pol: %s ok\n", keystr
));
3756 static int validate_pwd_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3757 struct tdb_validation_status
*state
)
3759 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3765 (void)centry_uint16(centry
);
3766 (void)centry_uint16(centry
);
3767 (void)centry_uint32(centry
);
3768 (void)centry_nttime(centry
);
3769 (void)centry_nttime(centry
);
3771 centry_free(centry
);
3773 if (!(state
->success
)) {
3776 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr
));
3780 static int validate_cred(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3781 struct tdb_validation_status
*state
)
3783 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3789 (void)centry_time(centry
);
3790 (void)centry_hash16(centry
, mem_ctx
);
3792 /* We only have 17 bytes more data in the salted cred case. */
3793 if (centry
->len
- centry
->ofs
== 17) {
3794 (void)centry_hash16(centry
, mem_ctx
);
3797 centry_free(centry
);
3799 if (!(state
->success
)) {
3802 DEBUG(10,("validate_cred: %s ok\n", keystr
));
3806 static int validate_ul(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3807 struct tdb_validation_status
*state
)
3809 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3810 int32 num_entries
, i
;
3816 num_entries
= (int32
)centry_uint32(centry
);
3818 for (i
=0; i
< num_entries
; i
++) {
3820 (void)centry_string(centry
, mem_ctx
);
3821 (void)centry_string(centry
, mem_ctx
);
3822 (void)centry_string(centry
, mem_ctx
);
3823 (void)centry_string(centry
, mem_ctx
);
3824 (void)centry_sid(centry
, &sid
);
3825 (void)centry_sid(centry
, &sid
);
3828 centry_free(centry
);
3830 if (!(state
->success
)) {
3833 DEBUG(10,("validate_ul: %s ok\n", keystr
));
3837 static int validate_gl(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3838 struct tdb_validation_status
*state
)
3840 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3841 int32 num_entries
, i
;
3847 num_entries
= centry_uint32(centry
);
3849 for (i
=0; i
< num_entries
; i
++) {
3850 (void)centry_string(centry
, mem_ctx
);
3851 (void)centry_string(centry
, mem_ctx
);
3852 (void)centry_uint32(centry
);
3855 centry_free(centry
);
3857 if (!(state
->success
)) {
3860 DEBUG(10,("validate_gl: %s ok\n", keystr
));
3864 static int validate_ug(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3865 struct tdb_validation_status
*state
)
3867 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3868 int32 num_groups
, i
;
3874 num_groups
= centry_uint32(centry
);
3876 for (i
=0; i
< num_groups
; i
++) {
3878 centry_sid(centry
, &sid
);
3881 centry_free(centry
);
3883 if (!(state
->success
)) {
3886 DEBUG(10,("validate_ug: %s ok\n", keystr
));
3890 static int validate_ua(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3891 struct tdb_validation_status
*state
)
3893 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3894 int32 num_aliases
, i
;
3900 num_aliases
= centry_uint32(centry
);
3902 for (i
=0; i
< num_aliases
; i
++) {
3903 (void)centry_uint32(centry
);
3906 centry_free(centry
);
3908 if (!(state
->success
)) {
3911 DEBUG(10,("validate_ua: %s ok\n", keystr
));
3915 static int validate_gm(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3916 struct tdb_validation_status
*state
)
3918 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3925 num_names
= centry_uint32(centry
);
3927 for (i
=0; i
< num_names
; i
++) {
3929 centry_sid(centry
, &sid
);
3930 (void)centry_string(centry
, mem_ctx
);
3931 (void)centry_uint32(centry
);
3934 centry_free(centry
);
3936 if (!(state
->success
)) {
3939 DEBUG(10,("validate_gm: %s ok\n", keystr
));
3943 static int validate_dr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3944 struct tdb_validation_status
*state
)
3946 /* Can't say anything about this other than must be nonzero. */
3947 if (dbuf
.dsize
== 0) {
3948 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3950 state
->bad_entry
= true;
3951 state
->success
= false;
3955 DEBUG(10,("validate_dr: %s ok\n", keystr
));
3959 static int validate_de(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3960 struct tdb_validation_status
*state
)
3962 /* Can't say anything about this other than must be nonzero. */
3963 if (dbuf
.dsize
== 0) {
3964 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3966 state
->bad_entry
= true;
3967 state
->success
= false;
3971 DEBUG(10,("validate_de: %s ok\n", keystr
));
3975 static int validate_pwinfo(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3976 TDB_DATA dbuf
, struct tdb_validation_status
*state
)
3978 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3984 (void)centry_string(centry
, mem_ctx
);
3985 (void)centry_string(centry
, mem_ctx
);
3986 (void)centry_string(centry
, mem_ctx
);
3987 (void)centry_uint32(centry
);
3989 centry_free(centry
);
3991 if (!(state
->success
)) {
3994 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
3998 static int validate_nss_an(TALLOC_CTX
*mem_ctx
, const char *keystr
,
4000 struct tdb_validation_status
*state
)
4002 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
4008 (void)centry_string( centry
, mem_ctx
);
4010 centry_free(centry
);
4012 if (!(state
->success
)) {
4015 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
4019 static int validate_nss_na(TALLOC_CTX
*mem_ctx
, const char *keystr
,
4021 struct tdb_validation_status
*state
)
4023 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
4029 (void)centry_string( centry
, mem_ctx
);
4031 centry_free(centry
);
4033 if (!(state
->success
)) {
4036 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
4040 static int validate_trustdomcache(TALLOC_CTX
*mem_ctx
, const char *keystr
,
4042 struct tdb_validation_status
*state
)
4044 if (dbuf
.dsize
== 0) {
4045 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
4046 "key %s (len ==0) ?\n", keystr
));
4047 state
->bad_entry
= true;
4048 state
->success
= false;
4052 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr
));
4053 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
4057 static int validate_offline(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4058 struct tdb_validation_status
*state
)
4060 if (dbuf
.dsize
!= 4) {
4061 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4062 keystr
, (unsigned int)dbuf
.dsize
));
4063 state
->bad_entry
= true;
4064 state
->success
= false;
4067 DEBUG(10,("validate_offline: %s ok\n", keystr
));
4071 static int validate_ndr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4072 struct tdb_validation_status
*state
)
4075 * Ignore validation for now. The proper way to do this is with a
4076 * checksum. Just pure parsing does not really catch much.
4081 static int validate_cache_version(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
4082 struct tdb_validation_status
*state
)
4084 if (dbuf
.dsize
!= 4) {
4085 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4086 "key %s (len %u != 4) ?\n",
4087 keystr
, (unsigned int)dbuf
.dsize
));
4088 state
->bad_entry
= true;
4089 state
->success
= false;
4093 DEBUG(10, ("validate_cache_version: %s ok\n", keystr
));
4097 /***********************************************************************
4098 A list of all possible cache tdb keys with associated validation
4100 ***********************************************************************/
4102 struct key_val_struct
{
4103 const char *keyname
;
4104 int (*validate_data_fn
)(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
, struct tdb_validation_status
* state
);
4106 {"SEQNUM/", validate_seqnum
},
4107 {"NS/", validate_ns
},
4108 {"SN/", validate_sn
},
4110 {"LOC_POL/", validate_loc_pol
},
4111 {"PWD_POL/", validate_pwd_pol
},
4112 {"CRED/", validate_cred
},
4113 {"UL/", validate_ul
},
4114 {"GL/", validate_gl
},
4115 {"UG/", validate_ug
},
4116 {"UA", validate_ua
},
4117 {"GM/", validate_gm
},
4118 {"DR/", validate_dr
},
4119 {"DE/", validate_de
},
4120 {"NSS/PWINFO/", validate_pwinfo
},
4121 {"TRUSTDOMCACHE/", validate_trustdomcache
},
4122 {"NSS/NA/", validate_nss_na
},
4123 {"NSS/AN/", validate_nss_an
},
4124 {"WINBINDD_OFFLINE", validate_offline
},
4125 {"NDR/", validate_ndr
},
4126 {WINBINDD_CACHE_VERSION_KEYSTR
, validate_cache_version
},
4130 /***********************************************************************
4131 Function to look at every entry in the tdb and validate it as far as
4133 ***********************************************************************/
4135 static int cache_traverse_validate_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
4138 unsigned int max_key_len
= 1024;
4139 struct tdb_validation_status
*v_state
= (struct tdb_validation_status
*)state
;
4141 /* Paranoia check. */
4142 if (strncmp("UA/", (const char *)kbuf
.dptr
, 3) == 0 ||
4143 strncmp("NDR/", (const char *)kbuf
.dptr
, 4) == 0) {
4144 max_key_len
= 1024 * 1024;
4146 if (kbuf
.dsize
> max_key_len
) {
4147 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4149 (unsigned int)kbuf
.dsize
, (unsigned int)max_key_len
));
4153 for (i
= 0; key_val
[i
].keyname
; i
++) {
4154 size_t namelen
= strlen(key_val
[i
].keyname
);
4155 if (kbuf
.dsize
>= namelen
&& (
4156 strncmp(key_val
[i
].keyname
, (const char *)kbuf
.dptr
, namelen
)) == 0) {
4157 TALLOC_CTX
*mem_ctx
;
4161 keystr
= SMB_MALLOC_ARRAY(char, kbuf
.dsize
+1);
4165 memcpy(keystr
, kbuf
.dptr
, kbuf
.dsize
);
4166 keystr
[kbuf
.dsize
] = '\0';
4168 mem_ctx
= talloc_init("validate_ctx");
4174 ret
= key_val
[i
].validate_data_fn(mem_ctx
, keystr
, dbuf
,
4178 talloc_destroy(mem_ctx
);
4183 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4184 dump_data(0, (uint8
*)kbuf
.dptr
, kbuf
.dsize
);
4185 DEBUG(0,("data :\n"));
4186 dump_data(0, (uint8
*)dbuf
.dptr
, dbuf
.dsize
);
4187 v_state
->unknown_key
= true;
4188 v_state
->success
= false;
4189 return 1; /* terminate. */
4192 static void validate_panic(const char *const why
)
4194 DEBUG(0,("validating cache: would panic %s\n", why
));
4195 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4199 static int wbcache_update_centry_fn(TDB_CONTEXT
*tdb
,
4207 if (is_non_centry_key(key
)) {
4211 if (data
.dptr
== NULL
|| data
.dsize
== 0) {
4212 if (tdb_delete(tdb
, key
) < 0) {
4213 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4219 /* add timeout to blob (uint64_t) */
4220 blob
.dsize
= data
.dsize
+ 8;
4222 blob
.dptr
= SMB_XMALLOC_ARRAY(uint8_t, blob
.dsize
);
4223 if (blob
.dptr
== NULL
) {
4226 memset(blob
.dptr
, 0, blob
.dsize
);
4228 /* copy status and seqnum */
4229 memcpy(blob
.dptr
, data
.dptr
, 8);
4232 ctimeout
= lp_winbind_cache_time() + time(NULL
);
4233 SBVAL(blob
.dptr
, 8, ctimeout
);
4236 memcpy(blob
.dptr
+ 16, data
.dptr
+ 8, data
.dsize
- 8);
4238 if (tdb_store(tdb
, key
, blob
, TDB_REPLACE
) < 0) {
4239 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4241 SAFE_FREE(blob
.dptr
);
4245 SAFE_FREE(blob
.dptr
);
4249 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT
*tdb
)
4253 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4255 rc
= tdb_traverse(tdb
, wbcache_update_centry_fn
, NULL
);
4263 /***********************************************************************
4264 Try and validate every entry in the winbindd cache. If we fail here,
4265 delete the cache tdb and return non-zero.
4266 ***********************************************************************/
4268 int winbindd_validate_cache(void)
4271 char *tdb_path
= NULL
;
4272 TDB_CONTEXT
*tdb
= NULL
;
4276 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4277 smb_panic_fn
= validate_panic
;
4279 tdb_path
= state_path("winbindd_cache.tdb");
4280 if (tdb_path
== NULL
) {
4284 tdb
= tdb_open_log(tdb_path
,
4285 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
4286 TDB_INCOMPATIBLE_HASH
|
4287 ( lp_winbind_offline_logon()
4289 : TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
4293 DEBUG(0, ("winbindd_validate_cache: "
4294 "error opening/initializing tdb\n"));
4298 /* Version check and upgrade code. */
4299 if (!tdb_fetch_uint32(tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers_id
)) {
4300 DEBUG(10, ("Fresh database\n"));
4301 tdb_store_uint32(tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
);
4302 vers_id
= WINBINDD_CACHE_VERSION
;
4305 if (vers_id
!= WINBINDD_CACHE_VERSION
) {
4306 if (vers_id
== WINBINDD_CACHE_VER1
) {
4307 ok
= wbcache_upgrade_v1_to_v2(tdb
);
4309 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4314 tdb_store_uint32(tdb
,
4315 WINBINDD_CACHE_VERSION_KEYSTR
,
4316 WINBINDD_CACHE_VERSION
);
4317 vers_id
= WINBINDD_CACHE_VER2
;
4323 ret
= tdb_validate_and_backup(tdb_path
, cache_traverse_validate_fn
);
4326 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4327 DEBUGADD(10, ("removing tdb %s.\n", tdb_path
));
4332 TALLOC_FREE(tdb_path
);
4333 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4334 smb_panic_fn
= smb_panic
;
4338 /***********************************************************************
4339 Try and validate every entry in the winbindd cache.
4340 ***********************************************************************/
4342 int winbindd_validate_cache_nobackup(void)
4347 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4348 smb_panic_fn
= validate_panic
;
4350 tdb_path
= state_path("winbindd_cache.tdb");
4351 if (tdb_path
== NULL
) {
4352 goto err_panic_restore
;
4355 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
4356 ret
= tdb_validate_open(tdb_path
, cache_traverse_validate_fn
);
4358 ret
= tdb_validate(wcache
->tdb
, cache_traverse_validate_fn
);
4362 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4366 TALLOC_FREE(tdb_path
);
4368 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4370 smb_panic_fn
= smb_panic
;
4374 bool winbindd_cache_validate_and_initialize(void)
4376 close_winbindd_cache();
4378 if (lp_winbind_offline_logon()) {
4379 if (winbindd_validate_cache() < 0) {
4380 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4381 "could be restored.\n"));
4385 return initialize_winbindd_cache();
4388 /*********************************************************************
4389 ********************************************************************/
4391 static bool add_wbdomain_to_tdc_array( struct winbindd_domain
*new_dom
,
4392 struct winbindd_tdc_domain
**domains
,
4393 size_t *num_domains
)
4395 struct winbindd_tdc_domain
*list
= NULL
;
4398 bool set_only
= false;
4400 /* don't allow duplicates */
4405 for ( i
=0; i
< (*num_domains
); i
++ ) {
4406 if ( strequal( new_dom
->name
, list
[i
].domain_name
) ) {
4407 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4418 list
= talloc_array( NULL
, struct winbindd_tdc_domain
, 1 );
4421 list
= talloc_realloc( *domains
, *domains
,
4422 struct winbindd_tdc_domain
,
4427 ZERO_STRUCT( list
[idx
] );
4433 list
[idx
].domain_name
= talloc_strdup(list
, new_dom
->name
);
4434 if (list
[idx
].domain_name
== NULL
) {
4437 if (new_dom
->alt_name
!= NULL
) {
4438 list
[idx
].dns_name
= talloc_strdup(list
, new_dom
->alt_name
);
4439 if (list
[idx
].dns_name
== NULL
) {
4444 if ( !is_null_sid( &new_dom
->sid
) ) {
4445 sid_copy( &list
[idx
].sid
, &new_dom
->sid
);
4447 sid_copy(&list
[idx
].sid
, &global_sid_NULL
);
4450 if ( new_dom
->domain_flags
!= 0x0 )
4451 list
[idx
].trust_flags
= new_dom
->domain_flags
;
4453 if ( new_dom
->domain_type
!= 0x0 )
4454 list
[idx
].trust_type
= new_dom
->domain_type
;
4456 if ( new_dom
->domain_trust_attribs
!= 0x0 )
4457 list
[idx
].trust_attribs
= new_dom
->domain_trust_attribs
;
4461 *num_domains
= idx
+ 1;
4467 /*********************************************************************
4468 ********************************************************************/
4470 static TDB_DATA
make_tdc_key( const char *domain_name
)
4472 char *keystr
= NULL
;
4473 TDB_DATA key
= { NULL
, 0 };
4475 if ( !domain_name
) {
4476 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4480 if (asprintf( &keystr
, "TRUSTDOMCACHE/%s", domain_name
) == -1) {
4483 key
= string_term_tdb_data(keystr
);
4488 /*********************************************************************
4489 ********************************************************************/
4491 static int pack_tdc_domains( struct winbindd_tdc_domain
*domains
,
4493 unsigned char **buf
)
4495 unsigned char *buffer
= NULL
;
4500 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4508 /* Store the number of array items first */
4509 len
+= tdb_pack( buffer
+len
, buflen
-len
, "d",
4512 /* now pack each domain trust record */
4513 for ( i
=0; i
<num_domains
; i
++ ) {
4518 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4519 domains
[i
].domain_name
,
4520 domains
[i
].dns_name
? domains
[i
].dns_name
: "UNKNOWN" ));
4523 len
+= tdb_pack( buffer
+len
, buflen
-len
, "fffddd",
4524 domains
[i
].domain_name
,
4525 domains
[i
].dns_name
? domains
[i
].dns_name
: "",
4526 sid_to_fstring(tmp
, &domains
[i
].sid
),
4527 domains
[i
].trust_flags
,
4528 domains
[i
].trust_attribs
,
4529 domains
[i
].trust_type
);
4532 if ( buflen
< len
) {
4534 if ( (buffer
= SMB_MALLOC_ARRAY(unsigned char, len
)) == NULL
) {
4535 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4549 /*********************************************************************
4550 ********************************************************************/
4552 static size_t unpack_tdc_domains( unsigned char *buf
, int buflen
,
4553 struct winbindd_tdc_domain
**domains
)
4555 fstring domain_name
, dns_name
, sid_string
;
4556 uint32 type
, attribs
, flags
;
4560 struct winbindd_tdc_domain
*list
= NULL
;
4562 /* get the number of domains */
4563 len
+= tdb_unpack( buf
+len
, buflen
-len
, "d", &num_domains
);
4565 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4569 list
= talloc_array( NULL
, struct winbindd_tdc_domain
, num_domains
);
4571 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4575 for ( i
=0; i
<num_domains
; i
++ ) {
4578 this_len
= tdb_unpack( buf
+len
, buflen
-len
, "fffddd",
4586 if ( this_len
== -1 ) {
4587 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4588 TALLOC_FREE( list
);
4593 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4594 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4595 domain_name
, dns_name
, sid_string
,
4596 flags
, attribs
, type
));
4598 list
[i
].domain_name
= talloc_strdup( list
, domain_name
);
4599 list
[i
].dns_name
= NULL
;
4600 if (dns_name
[0] != '\0') {
4601 list
[i
].dns_name
= talloc_strdup(list
, dns_name
);
4603 if ( !string_to_sid( &(list
[i
].sid
), sid_string
) ) {
4604 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4607 list
[i
].trust_flags
= flags
;
4608 list
[i
].trust_attribs
= attribs
;
4609 list
[i
].trust_type
= type
;
4617 /*********************************************************************
4618 ********************************************************************/
4620 static bool wcache_tdc_store_list( struct winbindd_tdc_domain
*domains
, size_t num_domains
)
4622 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4623 TDB_DATA data
= { NULL
, 0 };
4629 /* See if we were asked to delete the cache entry */
4632 ret
= tdb_delete( wcache
->tdb
, key
);
4636 data
.dsize
= pack_tdc_domains( domains
, num_domains
, &data
.dptr
);
4643 ret
= tdb_store( wcache
->tdb
, key
, data
, 0 );
4646 SAFE_FREE( data
.dptr
);
4647 SAFE_FREE( key
.dptr
);
4649 return ( ret
== 0 );
4652 /*********************************************************************
4653 ********************************************************************/
4655 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain
**domains
, size_t *num_domains
)
4657 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4658 TDB_DATA data
= { NULL
, 0 };
4666 data
= tdb_fetch_compat( wcache
->tdb
, key
);
4668 SAFE_FREE( key
.dptr
);
4673 *num_domains
= unpack_tdc_domains( data
.dptr
, data
.dsize
, domains
);
4675 SAFE_FREE( data
.dptr
);
4683 /*********************************************************************
4684 ********************************************************************/
4686 bool wcache_tdc_add_domain( struct winbindd_domain
*domain
)
4688 struct winbindd_tdc_domain
*dom_list
= NULL
;
4689 size_t num_domains
= 0;
4692 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4693 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4694 domain
->name
, domain
->alt_name
,
4695 sid_string_dbg(&domain
->sid
),
4696 domain
->domain_flags
,
4697 domain
->domain_trust_attribs
,
4698 domain
->domain_type
));
4700 if ( !init_wcache() ) {
4704 /* fetch the list */
4706 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4708 /* add the new domain */
4710 if ( !add_wbdomain_to_tdc_array( domain
, &dom_list
, &num_domains
) ) {
4714 /* pack the domain */
4716 if ( !wcache_tdc_store_list( dom_list
, num_domains
) ) {
4724 TALLOC_FREE( dom_list
);
4729 static struct winbindd_tdc_domain
*wcache_tdc_dup_domain(
4730 TALLOC_CTX
*mem_ctx
, const struct winbindd_tdc_domain
*src
)
4732 struct winbindd_tdc_domain
*dst
;
4734 dst
= talloc(mem_ctx
, struct winbindd_tdc_domain
);
4738 dst
->domain_name
= talloc_strdup(dst
, src
->domain_name
);
4739 if (dst
->domain_name
== NULL
) {
4743 dst
->dns_name
= NULL
;
4744 if (src
->dns_name
!= NULL
) {
4745 dst
->dns_name
= talloc_strdup(dst
, src
->dns_name
);
4746 if (dst
->dns_name
== NULL
) {
4751 sid_copy(&dst
->sid
, &src
->sid
);
4752 dst
->trust_flags
= src
->trust_flags
;
4753 dst
->trust_type
= src
->trust_type
;
4754 dst
->trust_attribs
= src
->trust_attribs
;
4761 /*********************************************************************
4762 ********************************************************************/
4764 struct winbindd_tdc_domain
* wcache_tdc_fetch_domain( TALLOC_CTX
*ctx
, const char *name
)
4766 struct winbindd_tdc_domain
*dom_list
= NULL
;
4767 size_t num_domains
= 0;
4769 struct winbindd_tdc_domain
*d
= NULL
;
4771 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name
));
4773 if ( !init_wcache() ) {
4777 /* fetch the list */
4779 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4781 for ( i
=0; i
<num_domains
; i
++ ) {
4782 if ( strequal(name
, dom_list
[i
].domain_name
) ||
4783 strequal(name
, dom_list
[i
].dns_name
) )
4785 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4788 d
= wcache_tdc_dup_domain(ctx
, &dom_list
[i
]);
4793 TALLOC_FREE( dom_list
);
4798 /*********************************************************************
4799 ********************************************************************/
4801 struct winbindd_tdc_domain
*
4802 wcache_tdc_fetch_domainbysid(TALLOC_CTX
*ctx
,
4803 const struct dom_sid
*sid
)
4805 struct winbindd_tdc_domain
*dom_list
= NULL
;
4806 size_t num_domains
= 0;
4808 struct winbindd_tdc_domain
*d
= NULL
;
4810 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4811 sid_string_dbg(sid
)));
4813 if (!init_wcache()) {
4817 /* fetch the list */
4819 wcache_tdc_fetch_list(&dom_list
, &num_domains
);
4821 for (i
= 0; i
<num_domains
; i
++) {
4822 if (dom_sid_equal(sid
, &(dom_list
[i
].sid
))) {
4823 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4824 "Found domain %s for SID %s\n",
4825 dom_list
[i
].domain_name
,
4826 sid_string_dbg(sid
)));
4828 d
= wcache_tdc_dup_domain(ctx
, &dom_list
[i
]);
4833 TALLOC_FREE(dom_list
);
4839 /*********************************************************************
4840 ********************************************************************/
4842 void wcache_tdc_clear( void )
4844 if ( !init_wcache() )
4847 wcache_tdc_store_list( NULL
, 0 );
4853 /*********************************************************************
4854 ********************************************************************/
4856 static void wcache_save_user_pwinfo(struct winbindd_domain
*domain
,
4858 const struct dom_sid
*user_sid
,
4859 const char *homedir
,
4864 struct cache_entry
*centry
;
4867 if ( (centry
= centry_start(domain
, status
)) == NULL
)
4870 centry_put_string( centry
, homedir
);
4871 centry_put_string( centry
, shell
);
4872 centry_put_string( centry
, gecos
);
4873 centry_put_uint32( centry
, gid
);
4875 centry_end(centry
, "NSS/PWINFO/%s", sid_to_fstring(tmp
, user_sid
) );
4877 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid
) ));
4879 centry_free(centry
);
4884 NTSTATUS
nss_get_info_cached( struct winbindd_domain
*domain
,
4885 const struct dom_sid
*user_sid
,
4887 const char **homedir
, const char **shell
,
4888 const char **gecos
, gid_t
*p_gid
)
4890 struct winbind_cache
*cache
= get_cache(domain
);
4891 struct cache_entry
*centry
= NULL
;
4898 centry
= wcache_fetch(cache
, domain
, "NSS/PWINFO/%s",
4899 sid_to_fstring(tmp
, user_sid
));
4904 *homedir
= centry_string( centry
, ctx
);
4905 *shell
= centry_string( centry
, ctx
);
4906 *gecos
= centry_string( centry
, ctx
);
4907 *p_gid
= centry_uint32( centry
);
4909 centry_free(centry
);
4911 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4912 sid_string_dbg(user_sid
)));
4914 return NT_STATUS_OK
;
4918 nt_status
= nss_get_info( domain
->name
, user_sid
, ctx
,
4919 homedir
, shell
, gecos
, p_gid
);
4921 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status
)));
4923 if ( NT_STATUS_IS_OK(nt_status
) ) {
4924 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir
));
4925 DEBUGADD(10, ("\tshell = '%s'\n", *shell
));
4926 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos
));
4927 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid
));
4929 wcache_save_user_pwinfo( domain
, nt_status
, user_sid
,
4930 *homedir
, *shell
, *gecos
, *p_gid
);
4933 if ( NT_STATUS_EQUAL( nt_status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
4934 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4936 set_domain_offline( domain
);
4944 /* the cache backend methods are exposed via this structure */
4945 struct winbindd_methods cache_methods
= {
4963 static bool wcache_ndr_key(TALLOC_CTX
*mem_ctx
, const char *domain_name
,
4964 uint32_t opnum
, const DATA_BLOB
*req
,
4970 key
= talloc_asprintf(mem_ctx
, "NDR/%s/%d/", domain_name
, (int)opnum
);
4974 keylen
= talloc_get_size(key
) - 1;
4976 key
= talloc_realloc(mem_ctx
, key
, char, keylen
+ req
->length
);
4980 memcpy(key
+ keylen
, req
->data
, req
->length
);
4982 pkey
->dptr
= (uint8_t *)key
;
4983 pkey
->dsize
= talloc_get_size(key
);
4987 static bool wcache_opnum_cacheable(uint32_t opnum
)
4990 case NDR_WBINT_PING
:
4991 case NDR_WBINT_QUERYSEQUENCENUMBER
:
4992 case NDR_WBINT_ALLOCATEUID
:
4993 case NDR_WBINT_ALLOCATEGID
:
4994 case NDR_WBINT_CHECKMACHINEACCOUNT
:
4995 case NDR_WBINT_CHANGEMACHINEACCOUNT
:
4996 case NDR_WBINT_PINGDC
:
5002 bool wcache_fetch_ndr(TALLOC_CTX
*mem_ctx
, struct winbindd_domain
*domain
,
5003 uint32_t opnum
, const DATA_BLOB
*req
, DATA_BLOB
*resp
)
5008 if (!wcache_opnum_cacheable(opnum
) ||
5009 is_my_own_sam_domain(domain
) ||
5010 is_builtin_domain(domain
)) {
5014 if (wcache
->tdb
== NULL
) {
5018 if (!wcache_ndr_key(talloc_tos(), domain
->name
, opnum
, req
, &key
)) {
5021 data
= tdb_fetch_compat(wcache
->tdb
, key
);
5022 TALLOC_FREE(key
.dptr
);
5024 if (data
.dptr
== NULL
) {
5027 if (data
.dsize
< 12) {
5031 if (!is_domain_offline(domain
)) {
5032 uint32_t entry_seqnum
, dom_seqnum
, last_check
;
5033 uint64_t entry_timeout
;
5035 if (!wcache_fetch_seqnum(domain
->name
, &dom_seqnum
,
5039 entry_seqnum
= IVAL(data
.dptr
, 0);
5040 if (entry_seqnum
!= dom_seqnum
) {
5041 DEBUG(10, ("Entry has wrong sequence number: %d\n",
5042 (int)entry_seqnum
));
5045 entry_timeout
= BVAL(data
.dptr
, 4);
5046 if (time(NULL
) > entry_timeout
) {
5047 DEBUG(10, ("Entry has timed out\n"));
5052 resp
->data
= (uint8_t *)talloc_memdup(mem_ctx
, data
.dptr
+ 12,
5054 if (resp
->data
== NULL
) {
5055 DEBUG(10, ("talloc failed\n"));
5058 resp
->length
= data
.dsize
- 12;
5062 SAFE_FREE(data
.dptr
);
5066 void wcache_store_ndr(struct winbindd_domain
*domain
, uint32_t opnum
,
5067 const DATA_BLOB
*req
, const DATA_BLOB
*resp
)
5070 uint32_t dom_seqnum
, last_check
;
5073 if (!wcache_opnum_cacheable(opnum
) ||
5074 is_my_own_sam_domain(domain
) ||
5075 is_builtin_domain(domain
)) {
5079 if (wcache
->tdb
== NULL
) {
5083 if (!wcache_fetch_seqnum(domain
->name
, &dom_seqnum
, &last_check
)) {
5084 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5089 if (!wcache_ndr_key(talloc_tos(), domain
->name
, opnum
, req
, &key
)) {
5093 timeout
= time(NULL
) + lp_winbind_cache_time();
5095 data
.dsize
= resp
->length
+ 12;
5096 data
.dptr
= talloc_array(key
.dptr
, uint8_t, data
.dsize
);
5097 if (data
.dptr
== NULL
) {
5101 SIVAL(data
.dptr
, 0, dom_seqnum
);
5102 SBVAL(data
.dptr
, 4, timeout
);
5103 memcpy(data
.dptr
+ 12, resp
->data
, resp
->length
);
5105 tdb_store(wcache
->tdb
, key
, data
, 0);
5108 TALLOC_FREE(key
.dptr
);