winbind: Fix bug 9854 -- NULL pointer dereference
[Samba.git] / source3 / winbindd / winbindd_cache.c
blobf631aea5da3d41c493c2920f3d62565a97f69341
1 /*
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/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
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;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
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[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
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 {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
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;
123 domain->initialized = True;
126 if (strequal(domain->name, get_global_sam_name()) &&
127 sid_check_is_our_sam(&domain->sid)) {
128 domain->backend = &sam_passdb_methods;
129 domain->initialized = True;
132 if ( !domain->initialized ) {
133 init_dc_connection( domain );
137 OK. listen up becasue I'm only going to say this once.
138 We have the following scenarios to consider
139 (a) trusted AD domains on a Samba DC,
140 (b) trusted AD domains and we are joined to a non-kerberos domain
141 (c) trusted AD domains and we are joined to a kerberos (AD) domain
143 For (a) we can always contact the trusted domain using krb5
144 since we have the domain trust account password
146 For (b) we can only use RPC since we have no way of
147 getting a krb5 ticket in our own domain
149 For (c) we can always use krb5 since we have a kerberos trust
151 --jerry
154 if (!domain->backend) {
155 #ifdef HAVE_ADS
156 struct winbindd_domain *our_domain = domain;
158 /* find our domain first so we can figure out if we
159 are joined to a kerberized domain */
161 if ( !domain->primary )
162 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only()) {
167 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168 domain->backend = &ads_methods;
169 } else {
170 #endif /* HAVE_ADS */
171 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172 domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
175 #endif /* HAVE_ADS */
178 if (ret)
179 return ret;
181 ret = SMB_XMALLOC_P(struct winbind_cache);
182 ZERO_STRUCTP(ret);
184 wcache = ret;
185 wcache_flush_cache();
187 return ret;
191 free a centry structure
193 static void centry_free(struct cache_entry *centry)
195 if (!centry)
196 return;
197 SAFE_FREE(centry->data);
198 free(centry);
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
203 if (centry->len - centry->ofs < nbytes) {
204 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
205 (unsigned int)nbytes,
206 centry->len - centry->ofs));
207 return false;
209 return true;
213 pull a uint64_t from a cache entry
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
217 uint64_t ret;
219 if (!centry_check_bytes(centry, 8)) {
220 smb_panic_fn("centry_uint64_t");
222 ret = BVAL(centry->data, centry->ofs);
223 centry->ofs += 8;
224 return ret;
228 pull a uint32 from a cache entry
230 static uint32 centry_uint32(struct cache_entry *centry)
232 uint32 ret;
234 if (!centry_check_bytes(centry, 4)) {
235 smb_panic_fn("centry_uint32");
237 ret = IVAL(centry->data, centry->ofs);
238 centry->ofs += 4;
239 return ret;
243 pull a uint16 from a cache entry
245 static uint16 centry_uint16(struct cache_entry *centry)
247 uint16 ret;
248 if (!centry_check_bytes(centry, 2)) {
249 smb_panic_fn("centry_uint16");
251 ret = SVAL(centry->data, centry->ofs);
252 centry->ofs += 2;
253 return ret;
257 pull a uint8 from a cache entry
259 static uint8 centry_uint8(struct cache_entry *centry)
261 uint8 ret;
262 if (!centry_check_bytes(centry, 1)) {
263 smb_panic_fn("centry_uint8");
265 ret = CVAL(centry->data, centry->ofs);
266 centry->ofs += 1;
267 return ret;
271 pull a NTTIME from a cache entry
273 static NTTIME centry_nttime(struct cache_entry *centry)
275 NTTIME ret;
276 if (!centry_check_bytes(centry, 8)) {
277 smb_panic_fn("centry_nttime");
279 ret = IVAL(centry->data, centry->ofs);
280 centry->ofs += 4;
281 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282 centry->ofs += 4;
283 return ret;
287 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
289 static time_t centry_time(struct cache_entry *centry)
291 return (time_t)centry_nttime(centry);
294 /* pull a string from a cache entry, using the supplied
295 talloc context
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 uint32 len;
300 char *ret;
302 len = centry_uint8(centry);
304 if (len == 0xFF) {
305 /* a deliberate NULL string */
306 return NULL;
309 if (!centry_check_bytes(centry, (size_t)len)) {
310 smb_panic_fn("centry_string");
313 ret = talloc_array(mem_ctx, char, len+1);
314 if (!ret) {
315 smb_panic_fn("centry_string out of memory\n");
317 memcpy(ret,centry->data + centry->ofs, len);
318 ret[len] = 0;
319 centry->ofs += len;
320 return ret;
323 /* pull a hash16 from a cache entry, using the supplied
324 talloc context
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 uint32 len;
329 char *ret;
331 len = centry_uint8(centry);
333 if (len != 16) {
334 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 len ));
336 return NULL;
339 if (!centry_check_bytes(centry, 16)) {
340 return NULL;
343 ret = talloc_array(mem_ctx, char, 16);
344 if (!ret) {
345 smb_panic_fn("centry_hash out of memory\n");
347 memcpy(ret,centry->data + centry->ofs, 16);
348 centry->ofs += 16;
349 return ret;
352 /* pull a sid from a cache entry, using the supplied
353 talloc context
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 char *sid_string;
358 bool ret;
360 sid_string = centry_string(centry, talloc_tos());
361 if (sid_string == NULL) {
362 return false;
364 ret = string_to_sid(sid, sid_string);
365 TALLOC_FREE(sid_string);
366 return ret;
371 pull a NTSTATUS from a cache entry
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 NTSTATUS status;
377 status = NT_STATUS(centry_uint32(centry));
378 return status;
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
385 bool ret;
387 if (!wcache->tdb)
388 return false;
390 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
392 if (ret)
393 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
394 domain->name ));
395 return ret;
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399 uint32_t *last_seq_check)
401 char *key;
402 TDB_DATA data;
404 if (wcache->tdb == NULL) {
405 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 return false;
409 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410 if (key == NULL) {
411 DEBUG(10, ("talloc failed\n"));
412 return false;
415 data = tdb_fetch_bystring(wcache->tdb, key);
416 TALLOC_FREE(key);
418 if (data.dptr == NULL) {
419 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 domain_name));
421 return false;
423 if (data.dsize != 8) {
424 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425 (int)data.dsize));
426 SAFE_FREE(data.dptr);
427 return false;
430 *seqnum = IVAL(data.dptr, 0);
431 *last_seq_check = IVAL(data.dptr, 4);
432 SAFE_FREE(data.dptr);
434 return true;
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
439 uint32 last_check, time_diff;
441 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442 &last_check)) {
443 return NT_STATUS_UNSUCCESSFUL;
445 domain->last_seq_check = last_check;
447 /* have we expired? */
449 time_diff = now - domain->last_seq_check;
450 if ( time_diff > lp_winbind_cache_time() ) {
451 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452 domain->name, domain->sequence_number,
453 (uint32)domain->last_seq_check));
454 return NT_STATUS_UNSUCCESSFUL;
457 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
458 domain->name, domain->sequence_number,
459 (uint32)domain->last_seq_check));
461 return NT_STATUS_OK;
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465 time_t last_seq_check)
467 char *key_str;
468 uint8_t buf[8];
469 int ret;
471 if (wcache->tdb == NULL) {
472 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 return false;
476 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477 if (key_str == NULL) {
478 DEBUG(10, ("talloc_asprintf failed\n"));
479 return false;
482 SIVAL(buf, 0, seqnum);
483 SIVAL(buf, 4, last_seq_check);
485 ret = tdb_store_bystring(wcache->tdb, key_str,
486 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487 TALLOC_FREE(key_str);
488 if (ret != 0) {
489 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490 tdb_errorstr_compat(wcache->tdb)));
491 TALLOC_FREE(key_str);
492 return false;
495 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496 domain_name, seqnum, (unsigned)last_seq_check));
498 return true;
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
503 return wcache_store_seqnum(domain->name, domain->sequence_number,
504 domain->last_seq_check);
508 refresh the domain sequence number. If force is true
509 then always refresh it, no matter how recently we fetched it
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 NTSTATUS status;
515 unsigned time_diff;
516 time_t t = time(NULL);
517 unsigned cache_time = lp_winbind_cache_time();
519 if (is_domain_offline(domain)) {
520 return;
523 get_cache( domain );
525 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
526 /* trying to reconnect is expensive, don't do it too often */
527 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528 cache_time *= 8;
530 #endif
532 time_diff = t - domain->last_seq_check;
534 /* see if we have to refetch the domain sequence number */
535 if (!force && (time_diff < cache_time) &&
536 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 NT_STATUS_IS_OK(domain->last_status)) {
538 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 goto done;
542 /* try to get the sequence number from the tdb cache first */
543 /* this will update the timestamp as well */
545 status = fetch_cache_seqnum( domain, t );
546 if (NT_STATUS_IS_OK(status) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 goto done;
552 /* important! make sure that we know if this is a native
553 mode domain or not. And that we can contact it. */
555 if ( winbindd_can_contact_domain( domain ) ) {
556 status = domain->backend->sequence_number(domain,
557 &domain->sequence_number);
558 } else {
559 /* just use the current time */
560 status = NT_STATUS_OK;
561 domain->sequence_number = time(NULL);
565 /* the above call could have set our domain->backend to NULL when
566 * coming from offline to online mode, make sure to reinitialize the
567 * backend - Guenther */
568 get_cache( domain );
570 if (!NT_STATUS_IS_OK(status)) {
571 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572 domain->sequence_number = DOM_SEQUENCE_NONE;
575 domain->last_status = status;
576 domain->last_seq_check = time(NULL);
578 /* save the new sequence number in the cache */
579 store_cache_seqnum( domain );
581 done:
582 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
583 domain->name, domain->sequence_number));
585 return;
589 decide if a cache entry has expired
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
593 /* If we've been told to be offline - stay in that state... */
594 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596 keystr, domain->name ));
597 return false;
600 /* when the domain is offline return the cached entry.
601 * This deals with transient offline states... */
603 if (!domain->online) {
604 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605 keystr, domain->name ));
606 return false;
609 /* if the server is OK and our cache entry came from when it was down then
610 the entry is invalid */
611 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
612 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614 keystr, domain->name ));
615 return true;
618 /* if the server is down or the cache entry is not older than the
619 current sequence number or it did not timeout then it is OK */
620 if (wcache_server_down(domain)
621 || ((centry->sequence_number == domain->sequence_number)
622 && (centry->timeout > time(NULL)))) {
623 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624 keystr, domain->name ));
625 return false;
628 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629 keystr, domain->name ));
631 /* it's expired */
632 return true;
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
637 TDB_DATA data;
638 struct cache_entry *centry;
639 TDB_DATA key;
641 key = string_tdb_data(kstr);
642 data = tdb_fetch_compat(wcache->tdb, key);
643 if (!data.dptr) {
644 /* a cache miss */
645 return NULL;
648 centry = SMB_XMALLOC_P(struct cache_entry);
649 centry->data = (unsigned char *)data.dptr;
650 centry->len = data.dsize;
651 centry->ofs = 0;
653 if (centry->len < 16) {
654 /* huh? corrupt cache? */
655 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656 "(len < 16)?\n", kstr));
657 centry_free(centry);
658 return NULL;
661 centry->status = centry_ntstatus(centry);
662 centry->sequence_number = centry_uint32(centry);
663 centry->timeout = centry_uint64_t(centry);
665 return centry;
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
670 if (strequal(domain->name, get_global_sam_name()) &&
671 sid_check_is_our_sam(&domain->sid)) {
672 return true;
675 return false;
678 static bool is_builtin_domain(struct winbindd_domain *domain)
680 if (strequal(domain->name, "BUILTIN") &&
681 sid_check_is_builtin(&domain->sid)) {
682 return true;
685 return false;
689 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690 number and return status
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...)
699 va_list ap;
700 char *kstr;
701 struct cache_entry *centry;
703 if (!winbindd_use_cache() ||
704 is_my_own_sam_domain(domain) ||
705 is_builtin_domain(domain)) {
706 return NULL;
709 refresh_sequence_number(domain, false);
711 va_start(ap, format);
712 smb_xvasprintf(&kstr, format, ap);
713 va_end(ap);
715 centry = wcache_fetch_raw(kstr);
716 if (centry == NULL) {
717 free(kstr);
718 return NULL;
721 if (centry_expired(domain, kstr, centry)) {
723 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724 kstr, domain->name ));
726 centry_free(centry);
727 free(kstr);
728 return NULL;
731 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732 kstr, domain->name ));
734 free(kstr);
735 return centry;
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
741 va_list ap;
742 char *kstr;
743 TDB_DATA key;
745 va_start(ap, format);
746 smb_xvasprintf(&kstr, format, ap);
747 va_end(ap);
749 key = string_tdb_data(kstr);
751 tdb_delete(wcache->tdb, key);
752 free(kstr);
756 make sure we have at least len bytes available in a centry
758 static void centry_expand(struct cache_entry *centry, uint32 len)
760 if (centry->len - centry->ofs >= len)
761 return;
762 centry->len *= 2;
763 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764 centry->len);
765 if (!centry->data) {
766 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767 smb_panic_fn("out of memory in centry_expand");
772 push a uint64_t into a centry
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
776 centry_expand(centry, 8);
777 SBVAL(centry->data, centry->ofs, v);
778 centry->ofs += 8;
782 push a uint32 into a centry
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
786 centry_expand(centry, 4);
787 SIVAL(centry->data, centry->ofs, v);
788 centry->ofs += 4;
792 push a uint16 into a centry
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
796 centry_expand(centry, 2);
797 SSVAL(centry->data, centry->ofs, v);
798 centry->ofs += 2;
802 push a uint8 into a centry
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
806 centry_expand(centry, 1);
807 SCVAL(centry->data, centry->ofs, v);
808 centry->ofs += 1;
812 push a string into a centry
814 static void centry_put_string(struct cache_entry *centry, const char *s)
816 int len;
818 if (!s) {
819 /* null strings are marked as len 0xFFFF */
820 centry_put_uint8(centry, 0xFF);
821 return;
824 len = strlen(s);
825 /* can't handle more than 254 char strings. Truncating is probably best */
826 if (len > 254) {
827 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828 len = 254;
830 centry_put_uint8(centry, len);
831 centry_expand(centry, len);
832 memcpy(centry->data + centry->ofs, s, len);
833 centry->ofs += len;
837 push a 16 byte hash into a centry - treat as 16 byte string.
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
841 centry_put_uint8(centry, 16);
842 centry_expand(centry, 16);
843 memcpy(centry->data + centry->ofs, val, 16);
844 centry->ofs += 16;
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
849 fstring sid_string;
850 centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 put NTSTATUS into a centry
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
859 uint32 status_value = NT_STATUS_V(status);
860 centry_put_uint32(centry, status_value);
865 push a NTTIME into a centry
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
869 centry_expand(centry, 8);
870 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871 centry->ofs += 4;
872 SIVAL(centry->data, centry->ofs, nt >> 32);
873 centry->ofs += 4;
877 push a time_t into a centry - use a 64 bit size.
878 NTTIME here is being used as a convenient 64-bit size.
880 static void centry_put_time(struct cache_entry *centry, time_t t)
882 NTTIME nt = (NTTIME)t;
883 centry_put_nttime(centry, nt);
887 start a centry for output. When finished, call centry_end()
889 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
891 struct cache_entry *centry;
893 if (!wcache->tdb)
894 return NULL;
896 centry = SMB_XMALLOC_P(struct cache_entry);
898 centry->len = 8192; /* reasonable default */
899 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900 centry->ofs = 0;
901 centry->sequence_number = domain->sequence_number;
902 centry->timeout = lp_winbind_cache_time() + time(NULL);
903 centry_put_ntstatus(centry, status);
904 centry_put_uint32(centry, centry->sequence_number);
905 centry_put_uint64_t(centry, centry->timeout);
906 return centry;
910 finish a centry and write it to the tdb
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
915 va_list ap;
916 char *kstr;
917 TDB_DATA key, data;
919 if (!winbindd_use_cache()) {
920 return;
923 va_start(ap, format);
924 smb_xvasprintf(&kstr, format, ap);
925 va_end(ap);
927 key = string_tdb_data(kstr);
928 data.dptr = centry->data;
929 data.dsize = centry->ofs;
931 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 free(kstr);
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
936 NTSTATUS status, const char *domain_name,
937 const char *name, const struct dom_sid *sid,
938 enum lsa_SidType type)
940 struct cache_entry *centry;
941 fstring uname;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
947 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
948 struct winbindd_domain *mydomain =
949 find_domain_from_sid_noinit(sid);
950 if (mydomain != NULL) {
951 domain_name = mydomain->name;
955 centry_put_uint32(centry, type);
956 centry_put_sid(centry, sid);
957 fstrcpy(uname, name);
958 (void)strupper_m(uname);
959 centry_end(centry, "NS/%s/%s", domain_name, uname);
960 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
961 uname, sid_string_dbg(sid), nt_errstr(status)));
962 centry_free(centry);
965 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
966 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
968 struct cache_entry *centry;
969 fstring sid_string;
971 centry = centry_start(domain, status);
972 if (!centry)
973 return;
975 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
976 struct winbindd_domain *mydomain =
977 find_domain_from_sid_noinit(sid);
978 if (mydomain != NULL) {
979 domain_name = mydomain->name;
983 if (NT_STATUS_IS_OK(status)) {
984 centry_put_uint32(centry, type);
985 centry_put_string(centry, domain_name);
986 centry_put_string(centry, name);
989 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
990 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
991 domain_name, name, nt_errstr(status)));
992 centry_free(centry);
996 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
997 struct wbint_userinfo *info)
999 struct cache_entry *centry;
1000 fstring sid_string;
1002 if (is_null_sid(&info->user_sid)) {
1003 return;
1006 centry = centry_start(domain, status);
1007 if (!centry)
1008 return;
1009 centry_put_string(centry, info->acct_name);
1010 centry_put_string(centry, info->full_name);
1011 centry_put_string(centry, info->homedir);
1012 centry_put_string(centry, info->shell);
1013 centry_put_uint32(centry, info->primary_gid);
1014 centry_put_sid(centry, &info->user_sid);
1015 centry_put_sid(centry, &info->group_sid);
1016 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1017 &info->user_sid));
1018 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1019 centry_free(centry);
1022 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1023 NTSTATUS status,
1024 struct samr_DomInfo12 *lockout_policy)
1026 struct cache_entry *centry;
1028 centry = centry_start(domain, status);
1029 if (!centry)
1030 return;
1032 centry_put_nttime(centry, lockout_policy->lockout_duration);
1033 centry_put_nttime(centry, lockout_policy->lockout_window);
1034 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1036 centry_end(centry, "LOC_POL/%s", domain->name);
1038 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1040 centry_free(centry);
1045 static void wcache_save_password_policy(struct winbindd_domain *domain,
1046 NTSTATUS status,
1047 struct samr_DomInfo1 *policy)
1049 struct cache_entry *centry;
1051 centry = centry_start(domain, status);
1052 if (!centry)
1053 return;
1055 centry_put_uint16(centry, policy->min_password_length);
1056 centry_put_uint16(centry, policy->password_history_length);
1057 centry_put_uint32(centry, policy->password_properties);
1058 centry_put_nttime(centry, policy->max_password_age);
1059 centry_put_nttime(centry, policy->min_password_age);
1061 centry_end(centry, "PWD_POL/%s", domain->name);
1063 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1065 centry_free(centry);
1068 /***************************************************************************
1069 ***************************************************************************/
1071 static void wcache_save_username_alias(struct winbindd_domain *domain,
1072 NTSTATUS status,
1073 const char *name, const char *alias)
1075 struct cache_entry *centry;
1076 fstring uname;
1078 if ( (centry = centry_start(domain, status)) == NULL )
1079 return;
1081 centry_put_string( centry, alias );
1083 fstrcpy(uname, name);
1084 (void)strupper_m(uname);
1085 centry_end(centry, "NSS/NA/%s", uname);
1087 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1089 centry_free(centry);
1092 static void wcache_save_alias_username(struct winbindd_domain *domain,
1093 NTSTATUS status,
1094 const char *alias, const char *name)
1096 struct cache_entry *centry;
1097 fstring uname;
1099 if ( (centry = centry_start(domain, status)) == NULL )
1100 return;
1102 centry_put_string( centry, name );
1104 fstrcpy(uname, alias);
1105 (void)strupper_m(uname);
1106 centry_end(centry, "NSS/AN/%s", uname);
1108 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1110 centry_free(centry);
1113 /***************************************************************************
1114 ***************************************************************************/
1116 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1117 struct winbindd_domain *domain,
1118 const char *name, char **alias )
1120 struct winbind_cache *cache = get_cache(domain);
1121 struct cache_entry *centry = NULL;
1122 NTSTATUS status;
1123 char *upper_name;
1125 if ( domain->internal )
1126 return NT_STATUS_NOT_SUPPORTED;
1128 if (!cache->tdb)
1129 goto do_query;
1131 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1132 return NT_STATUS_NO_MEMORY;
1133 if (!strupper_m(upper_name)) {
1134 SAFE_FREE(name);
1135 return NT_STATUS_INVALID_PARAMETER;
1138 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1140 SAFE_FREE( upper_name );
1142 if (!centry)
1143 goto do_query;
1145 status = centry->status;
1147 if (!NT_STATUS_IS_OK(status)) {
1148 centry_free(centry);
1149 return status;
1152 *alias = centry_string( centry, mem_ctx );
1154 centry_free(centry);
1156 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1157 name, *alias ? *alias : "(none)"));
1159 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1161 do_query:
1163 /* If its not in cache and we are offline, then fail */
1165 if ( get_global_winbindd_state_offline() || !domain->online ) {
1166 DEBUG(8,("resolve_username_to_alias: rejecting query "
1167 "in offline mode\n"));
1168 return NT_STATUS_NOT_FOUND;
1171 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1173 if ( NT_STATUS_IS_OK( status ) ) {
1174 wcache_save_username_alias(domain, status, name, *alias);
1177 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1178 wcache_save_username_alias(domain, status, name, "(NULL)");
1181 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1182 nt_errstr(status)));
1184 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1185 set_domain_offline( domain );
1188 return status;
1191 /***************************************************************************
1192 ***************************************************************************/
1194 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1195 struct winbindd_domain *domain,
1196 const char *alias, char **name )
1198 struct winbind_cache *cache = get_cache(domain);
1199 struct cache_entry *centry = NULL;
1200 NTSTATUS status;
1201 char *upper_name;
1203 if ( domain->internal )
1204 return NT_STATUS_NOT_SUPPORTED;
1206 if (!cache->tdb)
1207 goto do_query;
1209 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1210 return NT_STATUS_NO_MEMORY;
1211 if (!strupper_m(upper_name)) {
1212 SAFE_FREE(alias);
1213 return NT_STATUS_INVALID_PARAMETER;
1216 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1218 SAFE_FREE( upper_name );
1220 if (!centry)
1221 goto do_query;
1223 status = centry->status;
1225 if (!NT_STATUS_IS_OK(status)) {
1226 centry_free(centry);
1227 return status;
1230 *name = centry_string( centry, mem_ctx );
1232 centry_free(centry);
1234 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1235 alias, *name ? *name : "(none)"));
1237 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1239 do_query:
1241 /* If its not in cache and we are offline, then fail */
1243 if ( get_global_winbindd_state_offline() || !domain->online ) {
1244 DEBUG(8,("resolve_alias_to_username: rejecting query "
1245 "in offline mode\n"));
1246 return NT_STATUS_NOT_FOUND;
1249 /* an alias cannot contain a domain prefix or '@' */
1251 if (strchr(alias, '\\') || strchr(alias, '@')) {
1252 DEBUG(10,("resolve_alias_to_username: skipping fully "
1253 "qualified name %s\n", alias));
1254 return NT_STATUS_OBJECT_NAME_INVALID;
1257 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1259 if ( NT_STATUS_IS_OK( status ) ) {
1260 wcache_save_alias_username( domain, status, alias, *name );
1263 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1264 wcache_save_alias_username(domain, status, alias, "(NULL)");
1267 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1268 nt_errstr(status)));
1270 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1271 set_domain_offline( domain );
1274 return status;
1277 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1279 struct winbind_cache *cache = get_cache(domain);
1280 TDB_DATA data;
1281 fstring key_str, tmp;
1282 uint32 rid;
1284 if (!cache->tdb) {
1285 return NT_STATUS_INTERNAL_DB_ERROR;
1288 if (is_null_sid(sid)) {
1289 return NT_STATUS_INVALID_SID;
1292 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1293 return NT_STATUS_INVALID_SID;
1296 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1298 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1299 if (!data.dptr) {
1300 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1303 SAFE_FREE(data.dptr);
1304 return NT_STATUS_OK;
1307 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1308 as new salted ones. */
1310 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1311 TALLOC_CTX *mem_ctx,
1312 const struct dom_sid *sid,
1313 const uint8 **cached_nt_pass,
1314 const uint8 **cached_salt)
1316 struct winbind_cache *cache = get_cache(domain);
1317 struct cache_entry *centry = NULL;
1318 NTSTATUS status;
1319 uint32 rid;
1320 fstring tmp;
1322 if (!cache->tdb) {
1323 return NT_STATUS_INTERNAL_DB_ERROR;
1326 if (is_null_sid(sid)) {
1327 return NT_STATUS_INVALID_SID;
1330 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1331 return NT_STATUS_INVALID_SID;
1334 /* Try and get a salted cred first. If we can't
1335 fall back to an unsalted cred. */
1337 centry = wcache_fetch(cache, domain, "CRED/%s",
1338 sid_to_fstring(tmp, sid));
1339 if (!centry) {
1340 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1341 sid_string_dbg(sid)));
1342 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1346 * We don't use the time element at this moment,
1347 * but we have to consume it, so that we don't
1348 * neet to change the disk format of the cache.
1350 (void)centry_time(centry);
1352 /* In the salted case this isn't actually the nt_hash itself,
1353 but the MD5 of the salt + nt_hash. Let the caller
1354 sort this out. It can tell as we only return the cached_salt
1355 if we are returning a salted cred. */
1357 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1358 if (*cached_nt_pass == NULL) {
1359 fstring sidstr;
1361 sid_to_fstring(sidstr, sid);
1363 /* Bad (old) cred cache. Delete and pretend we
1364 don't have it. */
1365 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1366 sidstr));
1367 wcache_delete("CRED/%s", sidstr);
1368 centry_free(centry);
1369 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1372 /* We only have 17 bytes more data in the salted cred case. */
1373 if (centry->len - centry->ofs == 17) {
1374 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1375 } else {
1376 *cached_salt = NULL;
1379 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1380 if (*cached_salt) {
1381 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1384 status = centry->status;
1386 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1387 sid_string_dbg(sid), nt_errstr(status) ));
1389 centry_free(centry);
1390 return status;
1393 /* Store creds for a SID - only writes out new salted ones. */
1395 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1396 const struct dom_sid *sid,
1397 const uint8 nt_pass[NT_HASH_LEN])
1399 struct cache_entry *centry;
1400 fstring sid_string;
1401 uint32 rid;
1402 uint8 cred_salt[NT_HASH_LEN];
1403 uint8 salted_hash[NT_HASH_LEN];
1405 if (is_null_sid(sid)) {
1406 return NT_STATUS_INVALID_SID;
1409 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1410 return NT_STATUS_INVALID_SID;
1413 centry = centry_start(domain, NT_STATUS_OK);
1414 if (!centry) {
1415 return NT_STATUS_INTERNAL_DB_ERROR;
1418 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1420 centry_put_time(centry, time(NULL));
1422 /* Create a salt and then salt the hash. */
1423 generate_random_buffer(cred_salt, NT_HASH_LEN);
1424 E_md5hash(cred_salt, nt_pass, salted_hash);
1426 centry_put_hash16(centry, salted_hash);
1427 centry_put_hash16(centry, cred_salt);
1428 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1430 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1432 centry_free(centry);
1434 return NT_STATUS_OK;
1438 /* Query display info. This is the basic user list fn */
1439 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1440 TALLOC_CTX *mem_ctx,
1441 uint32 *num_entries,
1442 struct wbint_userinfo **info)
1444 struct winbind_cache *cache = get_cache(domain);
1445 struct cache_entry *centry = NULL;
1446 NTSTATUS status;
1447 unsigned int i, retry;
1448 bool old_status = domain->online;
1450 if (!cache->tdb)
1451 goto do_query;
1453 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1454 if (!centry)
1455 goto do_query;
1457 do_fetch_cache:
1458 *num_entries = centry_uint32(centry);
1460 if (*num_entries == 0)
1461 goto do_cached;
1463 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1464 if (! (*info)) {
1465 smb_panic_fn("query_user_list out of memory");
1467 for (i=0; i<(*num_entries); i++) {
1468 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1469 (*info)[i].full_name = centry_string(centry, mem_ctx);
1470 (*info)[i].homedir = centry_string(centry, mem_ctx);
1471 (*info)[i].shell = centry_string(centry, mem_ctx);
1472 centry_sid(centry, &(*info)[i].user_sid);
1473 centry_sid(centry, &(*info)[i].group_sid);
1476 do_cached:
1477 status = centry->status;
1479 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1480 domain->name, nt_errstr(status) ));
1482 centry_free(centry);
1483 return status;
1485 do_query:
1486 *num_entries = 0;
1487 *info = NULL;
1489 /* Return status value returned by seq number check */
1491 if (!NT_STATUS_IS_OK(domain->last_status))
1492 return domain->last_status;
1494 /* Put the query_user_list() in a retry loop. There appears to be
1495 * some bug either with Windows 2000 or Samba's handling of large
1496 * rpc replies. This manifests itself as sudden disconnection
1497 * at a random point in the enumeration of a large (60k) user list.
1498 * The retry loop simply tries the operation again. )-: It's not
1499 * pretty but an acceptable workaround until we work out what the
1500 * real problem is. */
1502 retry = 0;
1503 do {
1505 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1506 domain->name ));
1508 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1509 if (!NT_STATUS_IS_OK(status)) {
1510 DEBUG(3, ("query_user_list: returned 0x%08x, "
1511 "retrying\n", NT_STATUS_V(status)));
1513 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1514 DEBUG(3, ("query_user_list: flushing "
1515 "connection cache\n"));
1516 invalidate_cm_connection(&domain->conn);
1518 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1519 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1520 if (!domain->internal && old_status) {
1521 set_domain_offline(domain);
1523 /* store partial response. */
1524 if (*num_entries > 0) {
1526 * humm, what about the status used for cache?
1527 * Should it be NT_STATUS_OK?
1529 break;
1532 * domain is offline now, and there is no user entries,
1533 * try to fetch from cache again.
1535 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1536 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1537 /* partial response... */
1538 if (!centry) {
1539 goto skip_save;
1540 } else {
1541 goto do_fetch_cache;
1543 } else {
1544 goto skip_save;
1548 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1549 (retry++ < 5));
1551 /* and save it */
1552 refresh_sequence_number(domain, false);
1553 if (!NT_STATUS_IS_OK(status)) {
1554 return status;
1556 centry = centry_start(domain, status);
1557 if (!centry)
1558 goto skip_save;
1559 centry_put_uint32(centry, *num_entries);
1560 for (i=0; i<(*num_entries); i++) {
1561 centry_put_string(centry, (*info)[i].acct_name);
1562 centry_put_string(centry, (*info)[i].full_name);
1563 centry_put_string(centry, (*info)[i].homedir);
1564 centry_put_string(centry, (*info)[i].shell);
1565 centry_put_sid(centry, &(*info)[i].user_sid);
1566 centry_put_sid(centry, &(*info)[i].group_sid);
1567 if (domain->backend && domain->backend->consistent) {
1568 /* when the backend is consistent we can pre-prime some mappings */
1569 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1570 domain->name,
1571 (*info)[i].acct_name,
1572 &(*info)[i].user_sid,
1573 SID_NAME_USER);
1574 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1575 &(*info)[i].user_sid,
1576 domain->name,
1577 (*info)[i].acct_name,
1578 SID_NAME_USER);
1579 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1582 centry_end(centry, "UL/%s", domain->name);
1583 centry_free(centry);
1585 skip_save:
1586 return status;
1589 /* list all domain groups */
1590 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1591 TALLOC_CTX *mem_ctx,
1592 uint32 *num_entries,
1593 struct wb_acct_info **info)
1595 struct winbind_cache *cache = get_cache(domain);
1596 struct cache_entry *centry = NULL;
1597 NTSTATUS status;
1598 unsigned int i;
1599 bool old_status;
1601 old_status = domain->online;
1602 if (!cache->tdb)
1603 goto do_query;
1605 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1606 if (!centry)
1607 goto do_query;
1609 do_fetch_cache:
1610 *num_entries = centry_uint32(centry);
1612 if (*num_entries == 0)
1613 goto do_cached;
1615 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1616 if (! (*info)) {
1617 smb_panic_fn("enum_dom_groups out of memory");
1619 for (i=0; i<(*num_entries); i++) {
1620 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1621 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1622 (*info)[i].rid = centry_uint32(centry);
1625 do_cached:
1626 status = centry->status;
1628 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1629 domain->name, nt_errstr(status) ));
1631 centry_free(centry);
1632 return status;
1634 do_query:
1635 *num_entries = 0;
1636 *info = NULL;
1638 /* Return status value returned by seq number check */
1640 if (!NT_STATUS_IS_OK(domain->last_status))
1641 return domain->last_status;
1643 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1644 domain->name ));
1646 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1648 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1649 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1650 if (!domain->internal && old_status) {
1651 set_domain_offline(domain);
1653 if (cache->tdb &&
1654 !domain->online &&
1655 !domain->internal &&
1656 old_status) {
1657 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1658 if (centry) {
1659 goto do_fetch_cache;
1663 /* and save it */
1664 refresh_sequence_number(domain, false);
1665 if (!NT_STATUS_IS_OK(status)) {
1666 return status;
1668 centry = centry_start(domain, status);
1669 if (!centry)
1670 goto skip_save;
1671 centry_put_uint32(centry, *num_entries);
1672 for (i=0; i<(*num_entries); i++) {
1673 centry_put_string(centry, (*info)[i].acct_name);
1674 centry_put_string(centry, (*info)[i].acct_desc);
1675 centry_put_uint32(centry, (*info)[i].rid);
1677 centry_end(centry, "GL/%s/domain", domain->name);
1678 centry_free(centry);
1680 skip_save:
1681 return status;
1684 /* list all domain groups */
1685 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1686 TALLOC_CTX *mem_ctx,
1687 uint32 *num_entries,
1688 struct wb_acct_info **info)
1690 struct winbind_cache *cache = get_cache(domain);
1691 struct cache_entry *centry = NULL;
1692 NTSTATUS status;
1693 unsigned int i;
1694 bool old_status;
1696 old_status = domain->online;
1697 if (!cache->tdb)
1698 goto do_query;
1700 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1701 if (!centry)
1702 goto do_query;
1704 do_fetch_cache:
1705 *num_entries = centry_uint32(centry);
1707 if (*num_entries == 0)
1708 goto do_cached;
1710 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1711 if (! (*info)) {
1712 smb_panic_fn("enum_dom_groups out of memory");
1714 for (i=0; i<(*num_entries); i++) {
1715 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1716 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1717 (*info)[i].rid = centry_uint32(centry);
1720 do_cached:
1722 /* If we are returning cached data and the domain controller
1723 is down then we don't know whether the data is up to date
1724 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1725 indicate this. */
1727 if (wcache_server_down(domain)) {
1728 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1729 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1730 } else
1731 status = centry->status;
1733 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1734 domain->name, nt_errstr(status) ));
1736 centry_free(centry);
1737 return status;
1739 do_query:
1740 *num_entries = 0;
1741 *info = NULL;
1743 /* Return status value returned by seq number check */
1745 if (!NT_STATUS_IS_OK(domain->last_status))
1746 return domain->last_status;
1748 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1749 domain->name ));
1751 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1753 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1754 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1755 if (!domain->internal && old_status) {
1756 set_domain_offline(domain);
1758 if (cache->tdb &&
1759 !domain->internal &&
1760 !domain->online &&
1761 old_status) {
1762 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1763 if (centry) {
1764 goto do_fetch_cache;
1768 /* and save it */
1769 refresh_sequence_number(domain, false);
1770 if (!NT_STATUS_IS_OK(status)) {
1771 return status;
1773 centry = centry_start(domain, status);
1774 if (!centry)
1775 goto skip_save;
1776 centry_put_uint32(centry, *num_entries);
1777 for (i=0; i<(*num_entries); i++) {
1778 centry_put_string(centry, (*info)[i].acct_name);
1779 centry_put_string(centry, (*info)[i].acct_desc);
1780 centry_put_uint32(centry, (*info)[i].rid);
1782 centry_end(centry, "GL/%s/local", domain->name);
1783 centry_free(centry);
1785 skip_save:
1786 return status;
1789 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1790 const char *domain_name,
1791 const char *name,
1792 struct dom_sid *sid,
1793 enum lsa_SidType *type)
1795 struct winbind_cache *cache = get_cache(domain);
1796 struct cache_entry *centry;
1797 NTSTATUS status;
1798 char *uname;
1800 if (cache->tdb == NULL) {
1801 return NT_STATUS_NOT_FOUND;
1804 uname = talloc_strdup_upper(talloc_tos(), name);
1805 if (uname == NULL) {
1806 return NT_STATUS_NO_MEMORY;
1809 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1810 domain_name = domain->name;
1813 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1814 TALLOC_FREE(uname);
1815 if (centry == NULL) {
1816 return NT_STATUS_NOT_FOUND;
1819 status = centry->status;
1820 if (NT_STATUS_IS_OK(status)) {
1821 *type = (enum lsa_SidType)centry_uint32(centry);
1822 centry_sid(centry, sid);
1825 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1826 "%s\n", domain->name, nt_errstr(status) ));
1828 centry_free(centry);
1829 return status;
1832 /* convert a single name to a sid in a domain */
1833 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1834 TALLOC_CTX *mem_ctx,
1835 const char *domain_name,
1836 const char *name,
1837 uint32_t flags,
1838 struct dom_sid *sid,
1839 enum lsa_SidType *type)
1841 NTSTATUS status;
1842 bool old_status;
1844 old_status = domain->online;
1846 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1847 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1848 return status;
1851 ZERO_STRUCTP(sid);
1853 /* If the seq number check indicated that there is a problem
1854 * with this DC, then return that status... except for
1855 * access_denied. This is special because the dc may be in
1856 * "restrict anonymous = 1" mode, in which case it will deny
1857 * most unauthenticated operations, but *will* allow the LSA
1858 * name-to-sid that we try as a fallback. */
1860 if (!(NT_STATUS_IS_OK(domain->last_status)
1861 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1862 return domain->last_status;
1864 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1865 domain->name ));
1867 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1868 name, flags, sid, type);
1870 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1871 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1872 if (!domain->internal && old_status) {
1873 set_domain_offline(domain);
1875 if (!domain->internal &&
1876 !domain->online &&
1877 old_status) {
1878 NTSTATUS cache_status;
1879 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1880 return cache_status;
1883 /* and save it */
1884 refresh_sequence_number(domain, false);
1886 if (domain->online &&
1887 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1888 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1890 /* Only save the reverse mapping if this was not a UPN */
1891 if (!strchr(name, '@')) {
1892 if (!strupper_m(discard_const_p(char, domain_name))) {
1893 return NT_STATUS_INVALID_PARAMETER;
1895 (void)strlower_m(discard_const_p(char, name));
1896 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1900 return status;
1903 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1904 const struct dom_sid *sid,
1905 TALLOC_CTX *mem_ctx,
1906 char **domain_name,
1907 char **name,
1908 enum lsa_SidType *type)
1910 struct winbind_cache *cache = get_cache(domain);
1911 struct cache_entry *centry;
1912 char *sid_string;
1913 NTSTATUS status;
1915 if (cache->tdb == NULL) {
1916 return NT_STATUS_NOT_FOUND;
1919 sid_string = sid_string_tos(sid);
1920 if (sid_string == NULL) {
1921 return NT_STATUS_NO_MEMORY;
1924 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1925 TALLOC_FREE(sid_string);
1926 if (centry == NULL) {
1927 return NT_STATUS_NOT_FOUND;
1930 if (NT_STATUS_IS_OK(centry->status)) {
1931 *type = (enum lsa_SidType)centry_uint32(centry);
1932 *domain_name = centry_string(centry, mem_ctx);
1933 *name = centry_string(centry, mem_ctx);
1936 status = centry->status;
1937 centry_free(centry);
1939 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1940 "%s\n", domain->name, nt_errstr(status) ));
1942 return status;
1945 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1946 given */
1947 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1948 TALLOC_CTX *mem_ctx,
1949 const struct dom_sid *sid,
1950 char **domain_name,
1951 char **name,
1952 enum lsa_SidType *type)
1954 NTSTATUS status;
1955 bool old_status;
1957 old_status = domain->online;
1958 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1959 type);
1960 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1961 return status;
1964 *name = NULL;
1965 *domain_name = NULL;
1967 /* If the seq number check indicated that there is a problem
1968 * with this DC, then return that status... except for
1969 * access_denied. This is special because the dc may be in
1970 * "restrict anonymous = 1" mode, in which case it will deny
1971 * most unauthenticated operations, but *will* allow the LSA
1972 * sid-to-name that we try as a fallback. */
1974 if (!(NT_STATUS_IS_OK(domain->last_status)
1975 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1976 return domain->last_status;
1978 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1979 domain->name ));
1981 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1983 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1984 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1985 if (!domain->internal && old_status) {
1986 set_domain_offline(domain);
1988 if (!domain->internal &&
1989 !domain->online &&
1990 old_status) {
1991 NTSTATUS cache_status;
1992 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1993 domain_name, name, type);
1994 return cache_status;
1997 /* and save it */
1998 refresh_sequence_number(domain, false);
1999 if (!NT_STATUS_IS_OK(status)) {
2000 return status;
2002 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2004 /* We can't save the name to sid mapping here, as with sid history a
2005 * later name2sid would give the wrong sid. */
2007 return status;
2010 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2011 TALLOC_CTX *mem_ctx,
2012 const struct dom_sid *domain_sid,
2013 uint32 *rids,
2014 size_t num_rids,
2015 char **domain_name,
2016 char ***names,
2017 enum lsa_SidType **types)
2019 struct winbind_cache *cache = get_cache(domain);
2020 size_t i;
2021 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2022 bool have_mapped;
2023 bool have_unmapped;
2024 bool old_status;
2026 old_status = domain->online;
2027 *domain_name = NULL;
2028 *names = NULL;
2029 *types = NULL;
2031 if (!cache->tdb) {
2032 goto do_query;
2035 if (num_rids == 0) {
2036 return NT_STATUS_OK;
2039 *names = talloc_array(mem_ctx, char *, num_rids);
2040 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2042 if ((*names == NULL) || (*types == NULL)) {
2043 result = NT_STATUS_NO_MEMORY;
2044 goto error;
2047 have_mapped = have_unmapped = false;
2049 for (i=0; i<num_rids; i++) {
2050 struct dom_sid sid;
2051 struct cache_entry *centry;
2052 fstring tmp;
2054 if (!sid_compose(&sid, domain_sid, rids[i])) {
2055 result = NT_STATUS_INTERNAL_ERROR;
2056 goto error;
2059 centry = wcache_fetch(cache, domain, "SN/%s",
2060 sid_to_fstring(tmp, &sid));
2061 if (!centry) {
2062 goto do_query;
2065 (*types)[i] = SID_NAME_UNKNOWN;
2066 (*names)[i] = talloc_strdup(*names, "");
2068 if (NT_STATUS_IS_OK(centry->status)) {
2069 char *dom;
2070 have_mapped = true;
2071 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2073 dom = centry_string(centry, mem_ctx);
2074 if (*domain_name == NULL) {
2075 *domain_name = dom;
2076 } else {
2077 talloc_free(dom);
2080 (*names)[i] = centry_string(centry, *names);
2082 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2083 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2084 have_unmapped = true;
2086 } else {
2087 /* something's definitely wrong */
2088 result = centry->status;
2089 goto error;
2092 centry_free(centry);
2095 if (!have_mapped) {
2096 return NT_STATUS_NONE_MAPPED;
2098 if (!have_unmapped) {
2099 return NT_STATUS_OK;
2101 return STATUS_SOME_UNMAPPED;
2103 do_query:
2105 TALLOC_FREE(*names);
2106 TALLOC_FREE(*types);
2108 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2109 rids, num_rids, domain_name,
2110 names, types);
2112 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2113 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2114 if (!domain->internal && old_status) {
2115 set_domain_offline(domain);
2117 if (cache->tdb &&
2118 !domain->internal &&
2119 !domain->online &&
2120 old_status) {
2121 have_mapped = have_unmapped = false;
2123 for (i=0; i<num_rids; i++) {
2124 struct dom_sid sid;
2125 struct cache_entry *centry;
2126 fstring tmp;
2128 if (!sid_compose(&sid, domain_sid, rids[i])) {
2129 result = NT_STATUS_INTERNAL_ERROR;
2130 goto error;
2133 centry = wcache_fetch(cache, domain, "SN/%s",
2134 sid_to_fstring(tmp, &sid));
2135 if (!centry) {
2136 (*types)[i] = SID_NAME_UNKNOWN;
2137 (*names)[i] = talloc_strdup(*names, "");
2138 continue;
2141 (*types)[i] = SID_NAME_UNKNOWN;
2142 (*names)[i] = talloc_strdup(*names, "");
2144 if (NT_STATUS_IS_OK(centry->status)) {
2145 char *dom;
2146 have_mapped = true;
2147 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2149 dom = centry_string(centry, mem_ctx);
2150 if (*domain_name == NULL) {
2151 *domain_name = dom;
2152 } else {
2153 talloc_free(dom);
2156 (*names)[i] = centry_string(centry, *names);
2158 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2159 have_unmapped = true;
2161 } else {
2162 /* something's definitely wrong */
2163 result = centry->status;
2164 centry_free(centry);
2165 goto error;
2168 centry_free(centry);
2171 if (!have_mapped) {
2172 return NT_STATUS_NONE_MAPPED;
2174 if (!have_unmapped) {
2175 return NT_STATUS_OK;
2177 return STATUS_SOME_UNMAPPED;
2181 None of the queried rids has been found so save all negative entries
2183 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2184 for (i = 0; i < num_rids; i++) {
2185 struct dom_sid sid;
2186 const char *name = "";
2187 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2188 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2190 if (!sid_compose(&sid, domain_sid, rids[i])) {
2191 return NT_STATUS_INTERNAL_ERROR;
2194 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2195 name, type);
2198 return result;
2202 Some or all of the queried rids have been found.
2204 if (!NT_STATUS_IS_OK(result) &&
2205 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2206 return result;
2209 refresh_sequence_number(domain, false);
2211 for (i=0; i<num_rids; i++) {
2212 struct dom_sid sid;
2213 NTSTATUS status;
2215 if (!sid_compose(&sid, domain_sid, rids[i])) {
2216 result = NT_STATUS_INTERNAL_ERROR;
2217 goto error;
2220 status = (*types)[i] == SID_NAME_UNKNOWN ?
2221 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2223 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2224 (*names)[i], (*types)[i]);
2227 return result;
2229 error:
2230 TALLOC_FREE(*names);
2231 TALLOC_FREE(*types);
2232 return result;
2235 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2236 TALLOC_CTX *mem_ctx,
2237 const struct dom_sid *user_sid,
2238 struct wbint_userinfo *info)
2240 struct winbind_cache *cache = get_cache(domain);
2241 struct cache_entry *centry = NULL;
2242 NTSTATUS status;
2243 char *sid_string;
2245 if (cache->tdb == NULL) {
2246 return NT_STATUS_NOT_FOUND;
2249 sid_string = sid_string_tos(user_sid);
2250 if (sid_string == NULL) {
2251 return NT_STATUS_NO_MEMORY;
2254 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2255 TALLOC_FREE(sid_string);
2256 if (centry == NULL) {
2257 return NT_STATUS_NOT_FOUND;
2261 * If we have an access denied cache entry and a cached info3
2262 * in the samlogon cache then do a query. This will force the
2263 * rpc back end to return the info3 data.
2266 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2267 netsamlogon_cache_have(user_sid)) {
2268 DEBUG(10, ("query_user: cached access denied and have cached "
2269 "info3\n"));
2270 domain->last_status = NT_STATUS_OK;
2271 centry_free(centry);
2272 return NT_STATUS_NOT_FOUND;
2275 /* if status is not ok then this is a negative hit
2276 and the rest of the data doesn't matter */
2277 status = centry->status;
2278 if (NT_STATUS_IS_OK(status)) {
2279 info->acct_name = centry_string(centry, mem_ctx);
2280 info->full_name = centry_string(centry, mem_ctx);
2281 info->homedir = centry_string(centry, mem_ctx);
2282 info->shell = centry_string(centry, mem_ctx);
2283 info->primary_gid = centry_uint32(centry);
2284 centry_sid(centry, &info->user_sid);
2285 centry_sid(centry, &info->group_sid);
2288 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2289 "%s\n", domain->name, nt_errstr(status) ));
2291 centry_free(centry);
2292 return status;
2295 /* Lookup user information from a rid */
2296 static NTSTATUS query_user(struct winbindd_domain *domain,
2297 TALLOC_CTX *mem_ctx,
2298 const struct dom_sid *user_sid,
2299 struct wbint_userinfo *info)
2301 NTSTATUS status;
2302 bool old_status;
2304 old_status = domain->online;
2305 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2306 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2307 return status;
2310 ZERO_STRUCTP(info);
2312 /* Return status value returned by seq number check */
2314 if (!NT_STATUS_IS_OK(domain->last_status))
2315 return domain->last_status;
2317 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2318 domain->name ));
2320 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2322 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2323 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2324 if (!domain->internal && old_status) {
2325 set_domain_offline(domain);
2327 if (!domain->internal &&
2328 !domain->online &&
2329 old_status) {
2330 NTSTATUS cache_status;
2331 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2332 return cache_status;
2335 /* and save it */
2336 refresh_sequence_number(domain, false);
2337 if (!NT_STATUS_IS_OK(status)) {
2338 return status;
2340 wcache_save_user(domain, status, info);
2342 return status;
2345 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2346 TALLOC_CTX *mem_ctx,
2347 const struct dom_sid *user_sid,
2348 uint32_t *pnum_sids,
2349 struct dom_sid **psids)
2351 struct winbind_cache *cache = get_cache(domain);
2352 struct cache_entry *centry = NULL;
2353 NTSTATUS status;
2354 uint32_t i, num_sids;
2355 struct dom_sid *sids;
2356 fstring sid_string;
2358 if (cache->tdb == NULL) {
2359 return NT_STATUS_NOT_FOUND;
2362 centry = wcache_fetch(cache, domain, "UG/%s",
2363 sid_to_fstring(sid_string, user_sid));
2364 if (centry == NULL) {
2365 return NT_STATUS_NOT_FOUND;
2368 /* If we have an access denied cache entry and a cached info3 in the
2369 samlogon cache then do a query. This will force the rpc back end
2370 to return the info3 data. */
2372 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2373 && netsamlogon_cache_have(user_sid)) {
2374 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2375 "cached info3\n"));
2376 domain->last_status = NT_STATUS_OK;
2377 centry_free(centry);
2378 return NT_STATUS_NOT_FOUND;
2381 num_sids = centry_uint32(centry);
2382 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2383 if (sids == NULL) {
2384 centry_free(centry);
2385 return NT_STATUS_NO_MEMORY;
2388 for (i=0; i<num_sids; i++) {
2389 centry_sid(centry, &sids[i]);
2392 status = centry->status;
2394 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2395 "status: %s\n", domain->name, nt_errstr(status)));
2397 centry_free(centry);
2399 *pnum_sids = num_sids;
2400 *psids = sids;
2401 return status;
2404 /* Lookup groups a user is a member of. */
2405 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2406 TALLOC_CTX *mem_ctx,
2407 const struct dom_sid *user_sid,
2408 uint32 *num_groups, struct dom_sid **user_gids)
2410 struct cache_entry *centry = NULL;
2411 NTSTATUS status;
2412 unsigned int i;
2413 fstring sid_string;
2414 bool old_status;
2416 old_status = domain->online;
2417 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2418 num_groups, user_gids);
2419 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2420 return status;
2423 (*num_groups) = 0;
2424 (*user_gids) = NULL;
2426 /* Return status value returned by seq number check */
2428 if (!NT_STATUS_IS_OK(domain->last_status))
2429 return domain->last_status;
2431 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2432 domain->name ));
2434 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2436 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2437 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2438 if (!domain->internal && old_status) {
2439 set_domain_offline(domain);
2441 if (!domain->internal &&
2442 !domain->online &&
2443 old_status) {
2444 NTSTATUS cache_status;
2445 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2446 num_groups, user_gids);
2447 return cache_status;
2450 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2451 goto skip_save;
2453 /* and save it */
2454 refresh_sequence_number(domain, false);
2455 if (!NT_STATUS_IS_OK(status)) {
2456 return status;
2458 centry = centry_start(domain, status);
2459 if (!centry)
2460 goto skip_save;
2462 centry_put_uint32(centry, *num_groups);
2463 for (i=0; i<(*num_groups); i++) {
2464 centry_put_sid(centry, &(*user_gids)[i]);
2467 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2468 centry_free(centry);
2470 skip_save:
2471 return status;
2474 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2475 const struct dom_sid *sids)
2477 uint32_t i;
2478 char *sidlist;
2480 sidlist = talloc_strdup(mem_ctx, "");
2481 if (sidlist == NULL) {
2482 return NULL;
2484 for (i=0; i<num_sids; i++) {
2485 fstring tmp;
2486 sidlist = talloc_asprintf_append_buffer(
2487 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2488 if (sidlist == NULL) {
2489 return NULL;
2492 return sidlist;
2495 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2496 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2497 const struct dom_sid *sids,
2498 uint32_t *pnum_aliases, uint32_t **paliases)
2500 struct winbind_cache *cache = get_cache(domain);
2501 struct cache_entry *centry = NULL;
2502 uint32_t num_aliases;
2503 uint32_t *aliases;
2504 NTSTATUS status;
2505 char *sidlist;
2506 int i;
2508 if (cache->tdb == NULL) {
2509 return NT_STATUS_NOT_FOUND;
2512 if (num_sids == 0) {
2513 *pnum_aliases = 0;
2514 *paliases = NULL;
2515 return NT_STATUS_OK;
2518 /* We need to cache indexed by the whole list of SIDs, the aliases
2519 * resulting might come from any of the SIDs. */
2521 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2522 if (sidlist == NULL) {
2523 return NT_STATUS_NO_MEMORY;
2526 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2527 TALLOC_FREE(sidlist);
2528 if (centry == NULL) {
2529 return NT_STATUS_NOT_FOUND;
2532 num_aliases = centry_uint32(centry);
2533 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2534 if (aliases == NULL) {
2535 centry_free(centry);
2536 return NT_STATUS_NO_MEMORY;
2539 for (i=0; i<num_aliases; i++) {
2540 aliases[i] = centry_uint32(centry);
2543 status = centry->status;
2545 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2546 "status %s\n", domain->name, nt_errstr(status)));
2548 centry_free(centry);
2550 *pnum_aliases = num_aliases;
2551 *paliases = aliases;
2553 return status;
2556 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2557 TALLOC_CTX *mem_ctx,
2558 uint32 num_sids, const struct dom_sid *sids,
2559 uint32 *num_aliases, uint32 **alias_rids)
2561 struct cache_entry *centry = NULL;
2562 NTSTATUS status;
2563 char *sidlist;
2564 int i;
2565 bool old_status;
2567 old_status = domain->online;
2568 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2569 num_aliases, alias_rids);
2570 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2571 return status;
2574 (*num_aliases) = 0;
2575 (*alias_rids) = NULL;
2577 if (!NT_STATUS_IS_OK(domain->last_status))
2578 return domain->last_status;
2580 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2581 "for domain %s\n", domain->name ));
2583 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2584 if (sidlist == NULL) {
2585 return NT_STATUS_NO_MEMORY;
2588 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2589 num_sids, sids,
2590 num_aliases, alias_rids);
2592 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2593 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2594 if (!domain->internal && old_status) {
2595 set_domain_offline(domain);
2597 if (!domain->internal &&
2598 !domain->online &&
2599 old_status) {
2600 NTSTATUS cache_status;
2601 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2602 sids, num_aliases, alias_rids);
2603 return cache_status;
2606 /* and save it */
2607 refresh_sequence_number(domain, false);
2608 if (!NT_STATUS_IS_OK(status)) {
2609 return status;
2611 centry = centry_start(domain, status);
2612 if (!centry)
2613 goto skip_save;
2614 centry_put_uint32(centry, *num_aliases);
2615 for (i=0; i<(*num_aliases); i++)
2616 centry_put_uint32(centry, (*alias_rids)[i]);
2617 centry_end(centry, "UA%s", sidlist);
2618 centry_free(centry);
2620 skip_save:
2621 return status;
2624 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2625 TALLOC_CTX *mem_ctx,
2626 const struct dom_sid *group_sid,
2627 uint32_t *num_names,
2628 struct dom_sid **sid_mem, char ***names,
2629 uint32_t **name_types)
2631 struct winbind_cache *cache = get_cache(domain);
2632 struct cache_entry *centry = NULL;
2633 NTSTATUS status;
2634 unsigned int i;
2635 char *sid_string;
2637 if (cache->tdb == NULL) {
2638 return NT_STATUS_NOT_FOUND;
2641 sid_string = sid_string_tos(group_sid);
2642 if (sid_string == NULL) {
2643 return NT_STATUS_NO_MEMORY;
2646 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2647 TALLOC_FREE(sid_string);
2648 if (centry == NULL) {
2649 return NT_STATUS_NOT_FOUND;
2652 *sid_mem = NULL;
2653 *names = NULL;
2654 *name_types = NULL;
2656 *num_names = centry_uint32(centry);
2657 if (*num_names == 0) {
2658 centry_free(centry);
2659 return NT_STATUS_OK;
2662 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2663 *names = talloc_array(mem_ctx, char *, *num_names);
2664 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2666 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2667 TALLOC_FREE(*sid_mem);
2668 TALLOC_FREE(*names);
2669 TALLOC_FREE(*name_types);
2670 centry_free(centry);
2671 return NT_STATUS_NO_MEMORY;
2674 for (i=0; i<(*num_names); i++) {
2675 centry_sid(centry, &(*sid_mem)[i]);
2676 (*names)[i] = centry_string(centry, mem_ctx);
2677 (*name_types)[i] = centry_uint32(centry);
2680 status = centry->status;
2682 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2683 "status: %s\n", domain->name, nt_errstr(status)));
2685 centry_free(centry);
2686 return status;
2689 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2690 TALLOC_CTX *mem_ctx,
2691 const struct dom_sid *group_sid,
2692 enum lsa_SidType type,
2693 uint32 *num_names,
2694 struct dom_sid **sid_mem, char ***names,
2695 uint32 **name_types)
2697 struct cache_entry *centry = NULL;
2698 NTSTATUS status;
2699 unsigned int i;
2700 fstring sid_string;
2701 bool old_status;
2703 old_status = domain->online;
2704 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2705 sid_mem, names, name_types);
2706 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2707 return status;
2710 (*num_names) = 0;
2711 (*sid_mem) = NULL;
2712 (*names) = NULL;
2713 (*name_types) = NULL;
2715 /* Return status value returned by seq number check */
2717 if (!NT_STATUS_IS_OK(domain->last_status))
2718 return domain->last_status;
2720 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2721 domain->name ));
2723 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2724 type, num_names,
2725 sid_mem, names, name_types);
2727 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2728 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2729 if (!domain->internal && old_status) {
2730 set_domain_offline(domain);
2732 if (!domain->internal &&
2733 !domain->online &&
2734 old_status) {
2735 NTSTATUS cache_status;
2736 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2737 num_names, sid_mem, names,
2738 name_types);
2739 return cache_status;
2742 /* and save it */
2743 refresh_sequence_number(domain, false);
2744 if (!NT_STATUS_IS_OK(status)) {
2745 return status;
2747 centry = centry_start(domain, status);
2748 if (!centry)
2749 goto skip_save;
2750 centry_put_uint32(centry, *num_names);
2751 for (i=0; i<(*num_names); i++) {
2752 centry_put_sid(centry, &(*sid_mem)[i]);
2753 centry_put_string(centry, (*names)[i]);
2754 centry_put_uint32(centry, (*name_types)[i]);
2756 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2757 centry_free(centry);
2759 skip_save:
2760 return status;
2763 /* find the sequence number for a domain */
2764 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2766 refresh_sequence_number(domain, false);
2768 *seq = domain->sequence_number;
2770 return NT_STATUS_OK;
2773 /* enumerate trusted domains
2774 * (we need to have the list of trustdoms in the cache when we go offline) -
2775 * Guenther */
2776 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2777 TALLOC_CTX *mem_ctx,
2778 struct netr_DomainTrustList *trusts)
2780 NTSTATUS status;
2781 struct winbind_cache *cache;
2782 struct winbindd_tdc_domain *dom_list = NULL;
2783 size_t num_domains = 0;
2784 bool retval = false;
2785 int i;
2786 bool old_status;
2788 old_status = domain->online;
2789 trusts->count = 0;
2790 trusts->array = NULL;
2792 cache = get_cache(domain);
2793 if (!cache || !cache->tdb) {
2794 goto do_query;
2797 if (domain->online) {
2798 goto do_query;
2801 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2802 if (!retval || !num_domains || !dom_list) {
2803 TALLOC_FREE(dom_list);
2804 goto do_query;
2807 do_fetch_cache:
2808 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2809 if (!trusts->array) {
2810 TALLOC_FREE(dom_list);
2811 return NT_STATUS_NO_MEMORY;
2814 for (i = 0; i < num_domains; i++) {
2815 struct netr_DomainTrust *trust;
2816 struct dom_sid *sid;
2817 struct winbindd_domain *dom;
2819 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2820 if (dom && dom->internal) {
2821 continue;
2824 trust = &trusts->array[trusts->count];
2825 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2826 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2827 sid = talloc(trusts->array, struct dom_sid);
2828 if (!trust->netbios_name || !trust->dns_name ||
2829 !sid) {
2830 TALLOC_FREE(dom_list);
2831 TALLOC_FREE(trusts->array);
2832 return NT_STATUS_NO_MEMORY;
2835 trust->trust_flags = dom_list[i].trust_flags;
2836 trust->trust_attributes = dom_list[i].trust_attribs;
2837 trust->trust_type = dom_list[i].trust_type;
2838 sid_copy(sid, &dom_list[i].sid);
2839 trust->sid = sid;
2840 trusts->count++;
2843 TALLOC_FREE(dom_list);
2844 return NT_STATUS_OK;
2846 do_query:
2847 /* Return status value returned by seq number check */
2849 if (!NT_STATUS_IS_OK(domain->last_status))
2850 return domain->last_status;
2852 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2853 domain->name ));
2855 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2857 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2858 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2859 if (!domain->internal && old_status) {
2860 set_domain_offline(domain);
2862 if (!domain->internal &&
2863 !domain->online &&
2864 old_status) {
2865 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2866 if (retval && num_domains && dom_list) {
2867 TALLOC_FREE(trusts->array);
2868 trusts->count = 0;
2869 goto do_fetch_cache;
2873 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2874 * so that the generic centry handling still applies correctly -
2875 * Guenther*/
2877 if (!NT_STATUS_IS_ERR(status)) {
2878 status = NT_STATUS_OK;
2880 return status;
2883 /* get lockout policy */
2884 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2885 TALLOC_CTX *mem_ctx,
2886 struct samr_DomInfo12 *policy)
2888 struct winbind_cache *cache = get_cache(domain);
2889 struct cache_entry *centry = NULL;
2890 NTSTATUS status;
2891 bool old_status;
2893 old_status = domain->online;
2894 if (!cache->tdb)
2895 goto do_query;
2897 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2899 if (!centry)
2900 goto do_query;
2902 do_fetch_cache:
2903 policy->lockout_duration = centry_nttime(centry);
2904 policy->lockout_window = centry_nttime(centry);
2905 policy->lockout_threshold = centry_uint16(centry);
2907 status = centry->status;
2909 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2910 domain->name, nt_errstr(status) ));
2912 centry_free(centry);
2913 return status;
2915 do_query:
2916 ZERO_STRUCTP(policy);
2918 /* Return status value returned by seq number check */
2920 if (!NT_STATUS_IS_OK(domain->last_status))
2921 return domain->last_status;
2923 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2924 domain->name ));
2926 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2928 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2929 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2930 if (!domain->internal && old_status) {
2931 set_domain_offline(domain);
2933 if (cache->tdb &&
2934 !domain->internal &&
2935 !domain->online &&
2936 old_status) {
2937 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2938 if (centry) {
2939 goto do_fetch_cache;
2943 /* and save it */
2944 refresh_sequence_number(domain, false);
2945 if (!NT_STATUS_IS_OK(status)) {
2946 return status;
2948 wcache_save_lockout_policy(domain, status, policy);
2950 return status;
2953 /* get password policy */
2954 static NTSTATUS password_policy(struct winbindd_domain *domain,
2955 TALLOC_CTX *mem_ctx,
2956 struct samr_DomInfo1 *policy)
2958 struct winbind_cache *cache = get_cache(domain);
2959 struct cache_entry *centry = NULL;
2960 NTSTATUS status;
2961 bool old_status;
2963 old_status = domain->online;
2964 if (!cache->tdb)
2965 goto do_query;
2967 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2969 if (!centry)
2970 goto do_query;
2972 do_fetch_cache:
2973 policy->min_password_length = centry_uint16(centry);
2974 policy->password_history_length = centry_uint16(centry);
2975 policy->password_properties = centry_uint32(centry);
2976 policy->max_password_age = centry_nttime(centry);
2977 policy->min_password_age = centry_nttime(centry);
2979 status = centry->status;
2981 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2982 domain->name, nt_errstr(status) ));
2984 centry_free(centry);
2985 return status;
2987 do_query:
2988 ZERO_STRUCTP(policy);
2990 /* Return status value returned by seq number check */
2992 if (!NT_STATUS_IS_OK(domain->last_status))
2993 return domain->last_status;
2995 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2996 domain->name ));
2998 status = domain->backend->password_policy(domain, mem_ctx, policy);
3000 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3001 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3002 if (!domain->internal && old_status) {
3003 set_domain_offline(domain);
3005 if (cache->tdb &&
3006 !domain->internal &&
3007 !domain->online &&
3008 old_status) {
3009 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3010 if (centry) {
3011 goto do_fetch_cache;
3015 /* and save it */
3016 refresh_sequence_number(domain, false);
3017 if (!NT_STATUS_IS_OK(status)) {
3018 return status;
3020 wcache_save_password_policy(domain, status, policy);
3022 return status;
3026 /* Invalidate cached user and group lists coherently */
3028 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3029 void *state)
3031 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3032 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3033 tdb_delete(the_tdb, kbuf);
3035 return 0;
3038 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3040 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3041 const struct dom_sid *sid)
3043 fstring key_str, sid_string;
3044 struct winbind_cache *cache;
3046 /* dont clear cached U/SID and UG/SID entries when we want to logon
3047 * offline - gd */
3049 if (lp_winbind_offline_logon()) {
3050 return;
3053 if (!domain)
3054 return;
3056 cache = get_cache(domain);
3058 if (!cache->tdb) {
3059 return;
3062 /* Clear U/SID cache entry */
3063 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3064 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3065 tdb_delete(cache->tdb, string_tdb_data(key_str));
3067 /* Clear UG/SID cache entry */
3068 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3069 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3070 tdb_delete(cache->tdb, string_tdb_data(key_str));
3072 /* Samba/winbindd never needs this. */
3073 netsamlogon_clear_cached_user(sid);
3076 bool wcache_invalidate_cache(void)
3078 struct winbindd_domain *domain;
3080 for (domain = domain_list(); domain; domain = domain->next) {
3081 struct winbind_cache *cache = get_cache(domain);
3083 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3084 "entries for %s\n", domain->name));
3085 if (cache) {
3086 if (cache->tdb) {
3087 tdb_traverse(cache->tdb, traverse_fn, NULL);
3088 } else {
3089 return false;
3093 return true;
3096 bool wcache_invalidate_cache_noinit(void)
3098 struct winbindd_domain *domain;
3100 for (domain = domain_list(); domain; domain = domain->next) {
3101 struct winbind_cache *cache;
3103 /* Skip uninitialized domains. */
3104 if (!domain->initialized && !domain->internal) {
3105 continue;
3108 cache = get_cache(domain);
3110 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3111 "entries for %s\n", domain->name));
3112 if (cache) {
3113 if (cache->tdb) {
3114 tdb_traverse(cache->tdb, traverse_fn, NULL);
3116 * Flushing cache has nothing to with domains.
3117 * return here if we successfully flushed once.
3118 * To avoid unnecessary traversing the cache.
3120 return true;
3121 } else {
3122 return false;
3126 return true;
3129 bool init_wcache(void)
3131 if (wcache == NULL) {
3132 wcache = SMB_XMALLOC_P(struct winbind_cache);
3133 ZERO_STRUCTP(wcache);
3136 if (wcache->tdb != NULL)
3137 return true;
3139 /* when working offline we must not clear the cache on restart */
3140 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3141 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3142 TDB_INCOMPATIBLE_HASH |
3143 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3144 O_RDWR|O_CREAT, 0600);
3146 if (wcache->tdb == NULL) {
3147 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3148 return false;
3151 return true;
3154 /************************************************************************
3155 This is called by the parent to initialize the cache file.
3156 We don't need sophisticated locking here as we know we're the
3157 only opener.
3158 ************************************************************************/
3160 bool initialize_winbindd_cache(void)
3162 bool cache_bad = true;
3163 uint32 vers;
3165 if (!init_wcache()) {
3166 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3167 return false;
3170 /* Check version number. */
3171 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3172 vers == WINBINDD_CACHE_VERSION) {
3173 cache_bad = false;
3176 if (cache_bad) {
3177 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3178 "and re-creating with version number %d\n",
3179 WINBINDD_CACHE_VERSION ));
3181 tdb_close(wcache->tdb);
3182 wcache->tdb = NULL;
3184 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3185 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3186 state_path("winbindd_cache.tdb"),
3187 strerror(errno) ));
3188 return false;
3190 if (!init_wcache()) {
3191 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3192 "init_wcache failed.\n"));
3193 return false;
3196 /* Write the version. */
3197 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3198 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3199 tdb_errorstr_compat(wcache->tdb) ));
3200 return false;
3204 tdb_close(wcache->tdb);
3205 wcache->tdb = NULL;
3206 return true;
3209 void close_winbindd_cache(void)
3211 if (!wcache) {
3212 return;
3214 if (wcache->tdb) {
3215 tdb_close(wcache->tdb);
3216 wcache->tdb = NULL;
3220 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3221 char **domain_name, char **name,
3222 enum lsa_SidType *type)
3224 struct winbindd_domain *domain;
3225 NTSTATUS status;
3227 domain = find_lookup_domain_from_sid(sid);
3228 if (domain == NULL) {
3229 return false;
3231 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3232 type);
3233 return NT_STATUS_IS_OK(status);
3236 bool lookup_cached_name(const char *domain_name,
3237 const char *name,
3238 struct dom_sid *sid,
3239 enum lsa_SidType *type)
3241 struct winbindd_domain *domain;
3242 NTSTATUS status;
3243 bool original_online_state;
3245 domain = find_lookup_domain_from_name(domain_name);
3246 if (domain == NULL) {
3247 return false;
3250 /* If we are doing a cached logon, temporarily set the domain
3251 offline so the cache won't expire the entry */
3253 original_online_state = domain->online;
3254 domain->online = false;
3255 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3256 domain->online = original_online_state;
3258 return NT_STATUS_IS_OK(status);
3261 void cache_name2sid(struct winbindd_domain *domain,
3262 const char *domain_name, const char *name,
3263 enum lsa_SidType type, const struct dom_sid *sid)
3265 refresh_sequence_number(domain, false);
3266 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3267 sid, type);
3271 * The original idea that this cache only contains centries has
3272 * been blurred - now other stuff gets put in here. Ensure we
3273 * ignore these things on cleanup.
3276 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3277 TDB_DATA dbuf, void *state)
3279 struct cache_entry *centry;
3281 if (is_non_centry_key(kbuf)) {
3282 return 0;
3285 centry = wcache_fetch_raw((char *)kbuf.dptr);
3286 if (!centry) {
3287 return 0;
3290 if (!NT_STATUS_IS_OK(centry->status)) {
3291 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3292 tdb_delete(the_tdb, kbuf);
3295 centry_free(centry);
3296 return 0;
3299 /* flush the cache */
3300 void wcache_flush_cache(void)
3302 if (!wcache)
3303 return;
3304 if (wcache->tdb) {
3305 tdb_close(wcache->tdb);
3306 wcache->tdb = NULL;
3308 if (!winbindd_use_cache()) {
3309 return;
3312 /* when working offline we must not clear the cache on restart */
3313 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3314 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3315 TDB_INCOMPATIBLE_HASH |
3316 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3317 O_RDWR|O_CREAT, 0600);
3319 if (!wcache->tdb) {
3320 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3321 return;
3324 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3326 DEBUG(10,("wcache_flush_cache success\n"));
3329 /* Count cached creds */
3331 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3332 void *state)
3334 int *cred_count = (int*)state;
3336 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3337 (*cred_count)++;
3339 return 0;
3342 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3344 struct winbind_cache *cache = get_cache(domain);
3346 *count = 0;
3348 if (!cache->tdb) {
3349 return NT_STATUS_INTERNAL_DB_ERROR;
3352 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3354 return NT_STATUS_OK;
3357 struct cred_list {
3358 struct cred_list *prev, *next;
3359 TDB_DATA key;
3360 fstring name;
3361 time_t created;
3363 static struct cred_list *wcache_cred_list;
3365 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3366 void *state)
3368 struct cred_list *cred;
3370 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3372 cred = SMB_MALLOC_P(struct cred_list);
3373 if (cred == NULL) {
3374 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3375 return -1;
3378 ZERO_STRUCTP(cred);
3380 /* save a copy of the key */
3382 fstrcpy(cred->name, (const char *)kbuf.dptr);
3383 DLIST_ADD(wcache_cred_list, cred);
3386 return 0;
3389 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3391 struct winbind_cache *cache = get_cache(domain);
3392 NTSTATUS status;
3393 int ret;
3394 struct cred_list *cred, *oldest = NULL;
3396 if (!cache->tdb) {
3397 return NT_STATUS_INTERNAL_DB_ERROR;
3400 /* we possibly already have an entry */
3401 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3403 fstring key_str, tmp;
3405 DEBUG(11,("we already have an entry, deleting that\n"));
3407 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3409 tdb_delete(cache->tdb, string_tdb_data(key_str));
3411 return NT_STATUS_OK;
3414 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3415 if (ret == 0) {
3416 return NT_STATUS_OK;
3417 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3418 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3421 ZERO_STRUCTP(oldest);
3423 for (cred = wcache_cred_list; cred; cred = cred->next) {
3425 TDB_DATA data;
3426 time_t t;
3428 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3429 if (!data.dptr) {
3430 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3431 cred->name));
3432 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3433 goto done;
3436 t = IVAL(data.dptr, 0);
3437 SAFE_FREE(data.dptr);
3439 if (!oldest) {
3440 oldest = SMB_MALLOC_P(struct cred_list);
3441 if (oldest == NULL) {
3442 status = NT_STATUS_NO_MEMORY;
3443 goto done;
3446 fstrcpy(oldest->name, cred->name);
3447 oldest->created = t;
3448 continue;
3451 if (t < oldest->created) {
3452 fstrcpy(oldest->name, cred->name);
3453 oldest->created = t;
3457 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3458 status = NT_STATUS_OK;
3459 } else {
3460 status = NT_STATUS_UNSUCCESSFUL;
3462 done:
3463 SAFE_FREE(wcache_cred_list);
3464 SAFE_FREE(oldest);
3466 return status;
3469 /* Change the global online/offline state. */
3470 bool set_global_winbindd_state_offline(void)
3472 TDB_DATA data;
3474 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3476 /* Only go offline if someone has created
3477 the key "WINBINDD_OFFLINE" in the cache tdb. */
3479 if (wcache == NULL || wcache->tdb == NULL) {
3480 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3481 return false;
3484 if (!lp_winbind_offline_logon()) {
3485 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3486 return false;
3489 if (global_winbindd_offline_state) {
3490 /* Already offline. */
3491 return true;
3494 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3496 if (!data.dptr || data.dsize != 4) {
3497 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3498 SAFE_FREE(data.dptr);
3499 return false;
3500 } else {
3501 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3502 global_winbindd_offline_state = true;
3503 SAFE_FREE(data.dptr);
3504 return true;
3508 void set_global_winbindd_state_online(void)
3510 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3512 if (!lp_winbind_offline_logon()) {
3513 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3514 return;
3517 if (!global_winbindd_offline_state) {
3518 /* Already online. */
3519 return;
3521 global_winbindd_offline_state = false;
3523 if (!wcache->tdb) {
3524 return;
3527 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3528 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3531 bool get_global_winbindd_state_offline(void)
3533 return global_winbindd_offline_state;
3536 /***********************************************************************
3537 Validate functions for all possible cache tdb keys.
3538 ***********************************************************************/
3540 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3541 struct tdb_validation_status *state)
3543 struct cache_entry *centry;
3545 centry = SMB_XMALLOC_P(struct cache_entry);
3546 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3547 if (!centry->data) {
3548 SAFE_FREE(centry);
3549 return NULL;
3551 centry->len = data.dsize;
3552 centry->ofs = 0;
3554 if (centry->len < 16) {
3555 /* huh? corrupt cache? */
3556 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3557 "(len < 16) ?\n", kstr));
3558 centry_free(centry);
3559 state->bad_entry = true;
3560 state->success = false;
3561 return NULL;
3564 centry->status = NT_STATUS(centry_uint32(centry));
3565 centry->sequence_number = centry_uint32(centry);
3566 centry->timeout = centry_uint64_t(centry);
3567 return centry;
3570 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3571 struct tdb_validation_status *state)
3573 if (dbuf.dsize != 8) {
3574 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3575 keystr, (unsigned int)dbuf.dsize ));
3576 state->bad_entry = true;
3577 return 1;
3579 return 0;
3582 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3583 struct tdb_validation_status *state)
3585 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3586 if (!centry) {
3587 return 1;
3590 (void)centry_uint32(centry);
3591 if (NT_STATUS_IS_OK(centry->status)) {
3592 struct dom_sid sid;
3593 (void)centry_sid(centry, &sid);
3596 centry_free(centry);
3598 if (!(state->success)) {
3599 return 1;
3601 DEBUG(10,("validate_ns: %s ok\n", keystr));
3602 return 0;
3605 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3606 struct tdb_validation_status *state)
3608 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3609 if (!centry) {
3610 return 1;
3613 if (NT_STATUS_IS_OK(centry->status)) {
3614 (void)centry_uint32(centry);
3615 (void)centry_string(centry, mem_ctx);
3616 (void)centry_string(centry, mem_ctx);
3619 centry_free(centry);
3621 if (!(state->success)) {
3622 return 1;
3624 DEBUG(10,("validate_sn: %s ok\n", keystr));
3625 return 0;
3628 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3629 struct tdb_validation_status *state)
3631 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3632 struct dom_sid sid;
3634 if (!centry) {
3635 return 1;
3638 (void)centry_string(centry, mem_ctx);
3639 (void)centry_string(centry, mem_ctx);
3640 (void)centry_string(centry, mem_ctx);
3641 (void)centry_string(centry, mem_ctx);
3642 (void)centry_uint32(centry);
3643 (void)centry_sid(centry, &sid);
3644 (void)centry_sid(centry, &sid);
3646 centry_free(centry);
3648 if (!(state->success)) {
3649 return 1;
3651 DEBUG(10,("validate_u: %s ok\n", keystr));
3652 return 0;
3655 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3656 struct tdb_validation_status *state)
3658 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3660 if (!centry) {
3661 return 1;
3664 (void)centry_nttime(centry);
3665 (void)centry_nttime(centry);
3666 (void)centry_uint16(centry);
3668 centry_free(centry);
3670 if (!(state->success)) {
3671 return 1;
3673 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3674 return 0;
3677 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3678 struct tdb_validation_status *state)
3680 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3682 if (!centry) {
3683 return 1;
3686 (void)centry_uint16(centry);
3687 (void)centry_uint16(centry);
3688 (void)centry_uint32(centry);
3689 (void)centry_nttime(centry);
3690 (void)centry_nttime(centry);
3692 centry_free(centry);
3694 if (!(state->success)) {
3695 return 1;
3697 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3698 return 0;
3701 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3702 struct tdb_validation_status *state)
3704 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3706 if (!centry) {
3707 return 1;
3710 (void)centry_time(centry);
3711 (void)centry_hash16(centry, mem_ctx);
3713 /* We only have 17 bytes more data in the salted cred case. */
3714 if (centry->len - centry->ofs == 17) {
3715 (void)centry_hash16(centry, mem_ctx);
3718 centry_free(centry);
3720 if (!(state->success)) {
3721 return 1;
3723 DEBUG(10,("validate_cred: %s ok\n", keystr));
3724 return 0;
3727 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3728 struct tdb_validation_status *state)
3730 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3731 int32 num_entries, i;
3733 if (!centry) {
3734 return 1;
3737 num_entries = (int32)centry_uint32(centry);
3739 for (i=0; i< num_entries; i++) {
3740 struct dom_sid sid;
3741 (void)centry_string(centry, mem_ctx);
3742 (void)centry_string(centry, mem_ctx);
3743 (void)centry_string(centry, mem_ctx);
3744 (void)centry_string(centry, mem_ctx);
3745 (void)centry_sid(centry, &sid);
3746 (void)centry_sid(centry, &sid);
3749 centry_free(centry);
3751 if (!(state->success)) {
3752 return 1;
3754 DEBUG(10,("validate_ul: %s ok\n", keystr));
3755 return 0;
3758 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3759 struct tdb_validation_status *state)
3761 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3762 int32 num_entries, i;
3764 if (!centry) {
3765 return 1;
3768 num_entries = centry_uint32(centry);
3770 for (i=0; i< num_entries; i++) {
3771 (void)centry_string(centry, mem_ctx);
3772 (void)centry_string(centry, mem_ctx);
3773 (void)centry_uint32(centry);
3776 centry_free(centry);
3778 if (!(state->success)) {
3779 return 1;
3781 DEBUG(10,("validate_gl: %s ok\n", keystr));
3782 return 0;
3785 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3786 struct tdb_validation_status *state)
3788 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3789 int32 num_groups, i;
3791 if (!centry) {
3792 return 1;
3795 num_groups = centry_uint32(centry);
3797 for (i=0; i< num_groups; i++) {
3798 struct dom_sid sid;
3799 centry_sid(centry, &sid);
3802 centry_free(centry);
3804 if (!(state->success)) {
3805 return 1;
3807 DEBUG(10,("validate_ug: %s ok\n", keystr));
3808 return 0;
3811 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3812 struct tdb_validation_status *state)
3814 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3815 int32 num_aliases, i;
3817 if (!centry) {
3818 return 1;
3821 num_aliases = centry_uint32(centry);
3823 for (i=0; i < num_aliases; i++) {
3824 (void)centry_uint32(centry);
3827 centry_free(centry);
3829 if (!(state->success)) {
3830 return 1;
3832 DEBUG(10,("validate_ua: %s ok\n", keystr));
3833 return 0;
3836 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3837 struct tdb_validation_status *state)
3839 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3840 int32 num_names, i;
3842 if (!centry) {
3843 return 1;
3846 num_names = centry_uint32(centry);
3848 for (i=0; i< num_names; i++) {
3849 struct dom_sid sid;
3850 centry_sid(centry, &sid);
3851 (void)centry_string(centry, mem_ctx);
3852 (void)centry_uint32(centry);
3855 centry_free(centry);
3857 if (!(state->success)) {
3858 return 1;
3860 DEBUG(10,("validate_gm: %s ok\n", keystr));
3861 return 0;
3864 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3865 struct tdb_validation_status *state)
3867 /* Can't say anything about this other than must be nonzero. */
3868 if (dbuf.dsize == 0) {
3869 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3870 keystr));
3871 state->bad_entry = true;
3872 state->success = false;
3873 return 1;
3876 DEBUG(10,("validate_dr: %s ok\n", keystr));
3877 return 0;
3880 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3881 struct tdb_validation_status *state)
3883 /* Can't say anything about this other than must be nonzero. */
3884 if (dbuf.dsize == 0) {
3885 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3886 keystr));
3887 state->bad_entry = true;
3888 state->success = false;
3889 return 1;
3892 DEBUG(10,("validate_de: %s ok\n", keystr));
3893 return 0;
3896 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3897 TDB_DATA dbuf, struct tdb_validation_status *state)
3899 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3901 if (!centry) {
3902 return 1;
3905 (void)centry_string(centry, mem_ctx);
3906 (void)centry_string(centry, mem_ctx);
3907 (void)centry_string(centry, mem_ctx);
3908 (void)centry_uint32(centry);
3910 centry_free(centry);
3912 if (!(state->success)) {
3913 return 1;
3915 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3916 return 0;
3919 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3920 TDB_DATA dbuf,
3921 struct tdb_validation_status *state)
3923 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3925 if (!centry) {
3926 return 1;
3929 (void)centry_string( centry, mem_ctx );
3931 centry_free(centry);
3933 if (!(state->success)) {
3934 return 1;
3936 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3937 return 0;
3940 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3941 TDB_DATA dbuf,
3942 struct tdb_validation_status *state)
3944 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3946 if (!centry) {
3947 return 1;
3950 (void)centry_string( centry, mem_ctx );
3952 centry_free(centry);
3954 if (!(state->success)) {
3955 return 1;
3957 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3958 return 0;
3961 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3962 TDB_DATA dbuf,
3963 struct tdb_validation_status *state)
3965 if (dbuf.dsize == 0) {
3966 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3967 "key %s (len ==0) ?\n", keystr));
3968 state->bad_entry = true;
3969 state->success = false;
3970 return 1;
3973 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3974 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3975 return 0;
3978 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3979 struct tdb_validation_status *state)
3981 if (dbuf.dsize != 4) {
3982 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3983 keystr, (unsigned int)dbuf.dsize ));
3984 state->bad_entry = true;
3985 state->success = false;
3986 return 1;
3988 DEBUG(10,("validate_offline: %s ok\n", keystr));
3989 return 0;
3992 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3993 struct tdb_validation_status *state)
3996 * Ignore validation for now. The proper way to do this is with a
3997 * checksum. Just pure parsing does not really catch much.
3999 return 0;
4002 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4003 struct tdb_validation_status *state)
4005 if (dbuf.dsize != 4) {
4006 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4007 "key %s (len %u != 4) ?\n",
4008 keystr, (unsigned int)dbuf.dsize));
4009 state->bad_entry = true;
4010 state->success = false;
4011 return 1;
4014 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4015 return 0;
4018 /***********************************************************************
4019 A list of all possible cache tdb keys with associated validation
4020 functions.
4021 ***********************************************************************/
4023 struct key_val_struct {
4024 const char *keyname;
4025 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4026 } key_val[] = {
4027 {"SEQNUM/", validate_seqnum},
4028 {"NS/", validate_ns},
4029 {"SN/", validate_sn},
4030 {"U/", validate_u},
4031 {"LOC_POL/", validate_loc_pol},
4032 {"PWD_POL/", validate_pwd_pol},
4033 {"CRED/", validate_cred},
4034 {"UL/", validate_ul},
4035 {"GL/", validate_gl},
4036 {"UG/", validate_ug},
4037 {"UA", validate_ua},
4038 {"GM/", validate_gm},
4039 {"DR/", validate_dr},
4040 {"DE/", validate_de},
4041 {"NSS/PWINFO/", validate_pwinfo},
4042 {"TRUSTDOMCACHE/", validate_trustdomcache},
4043 {"NSS/NA/", validate_nss_na},
4044 {"NSS/AN/", validate_nss_an},
4045 {"WINBINDD_OFFLINE", validate_offline},
4046 {"NDR/", validate_ndr},
4047 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4048 {NULL, NULL}
4051 /***********************************************************************
4052 Function to look at every entry in the tdb and validate it as far as
4053 possible.
4054 ***********************************************************************/
4056 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4058 int i;
4059 unsigned int max_key_len = 1024;
4060 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4062 /* Paranoia check. */
4063 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4064 max_key_len = 1024 * 1024;
4066 if (kbuf.dsize > max_key_len) {
4067 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4068 "(%u) > (%u)\n\n",
4069 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4070 return 1;
4073 for (i = 0; key_val[i].keyname; i++) {
4074 size_t namelen = strlen(key_val[i].keyname);
4075 if (kbuf.dsize >= namelen && (
4076 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4077 TALLOC_CTX *mem_ctx;
4078 char *keystr;
4079 int ret;
4081 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4082 if (!keystr) {
4083 return 1;
4085 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4086 keystr[kbuf.dsize] = '\0';
4088 mem_ctx = talloc_init("validate_ctx");
4089 if (!mem_ctx) {
4090 SAFE_FREE(keystr);
4091 return 1;
4094 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4095 v_state);
4097 SAFE_FREE(keystr);
4098 talloc_destroy(mem_ctx);
4099 return ret;
4103 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4104 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4105 DEBUG(0,("data :\n"));
4106 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4107 v_state->unknown_key = true;
4108 v_state->success = false;
4109 return 1; /* terminate. */
4112 static void validate_panic(const char *const why)
4114 DEBUG(0,("validating cache: would panic %s\n", why ));
4115 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4116 exit(47);
4119 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4120 TDB_DATA key,
4121 TDB_DATA data,
4122 void *state)
4124 uint64_t ctimeout;
4125 TDB_DATA blob;
4127 if (is_non_centry_key(key)) {
4128 return 0;
4131 if (data.dptr == NULL || data.dsize == 0) {
4132 if (tdb_delete(tdb, key) < 0) {
4133 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4134 key.dptr));
4135 return 1;
4139 /* add timeout to blob (uint64_t) */
4140 blob.dsize = data.dsize + 8;
4142 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4143 if (blob.dptr == NULL) {
4144 return 1;
4146 memset(blob.dptr, 0, blob.dsize);
4148 /* copy status and seqnum */
4149 memcpy(blob.dptr, data.dptr, 8);
4151 /* add timeout */
4152 ctimeout = lp_winbind_cache_time() + time(NULL);
4153 SBVAL(blob.dptr, 8, ctimeout);
4155 /* copy the rest */
4156 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4158 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4159 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4160 key.dptr));
4161 SAFE_FREE(blob.dptr);
4162 return 1;
4165 SAFE_FREE(blob.dptr);
4166 return 0;
4169 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4171 int rc;
4173 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4175 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4176 if (rc < 0) {
4177 return false;
4180 return true;
4183 /***********************************************************************
4184 Try and validate every entry in the winbindd cache. If we fail here,
4185 delete the cache tdb and return non-zero.
4186 ***********************************************************************/
4188 int winbindd_validate_cache(void)
4190 int ret = -1;
4191 const char *tdb_path = state_path("winbindd_cache.tdb");
4192 TDB_CONTEXT *tdb = NULL;
4193 uint32_t vers_id;
4194 bool ok;
4196 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4197 smb_panic_fn = validate_panic;
4199 tdb = tdb_open_log(tdb_path,
4200 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4201 TDB_INCOMPATIBLE_HASH |
4202 ( lp_winbind_offline_logon()
4203 ? TDB_DEFAULT
4204 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4205 O_RDWR|O_CREAT,
4206 0600);
4207 if (!tdb) {
4208 DEBUG(0, ("winbindd_validate_cache: "
4209 "error opening/initializing tdb\n"));
4210 goto done;
4213 /* Version check and upgrade code. */
4214 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4215 DEBUG(10, ("Fresh database\n"));
4216 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4217 vers_id = WINBINDD_CACHE_VERSION;
4220 if (vers_id != WINBINDD_CACHE_VERSION) {
4221 if (vers_id == WINBINDD_CACHE_VER1) {
4222 ok = wbcache_upgrade_v1_to_v2(tdb);
4223 if (!ok) {
4224 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4225 unlink(tdb_path);
4226 goto done;
4229 tdb_store_uint32(tdb,
4230 WINBINDD_CACHE_VERSION_KEYSTR,
4231 WINBINDD_CACHE_VERSION);
4232 vers_id = WINBINDD_CACHE_VER2;
4236 tdb_close(tdb);
4238 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4240 if (ret != 0) {
4241 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4242 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4243 unlink(tdb_path);
4246 done:
4247 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4248 smb_panic_fn = smb_panic;
4249 return ret;
4252 /***********************************************************************
4253 Try and validate every entry in the winbindd cache.
4254 ***********************************************************************/
4256 int winbindd_validate_cache_nobackup(void)
4258 int ret = -1;
4259 const char *tdb_path = state_path("winbindd_cache.tdb");
4261 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4262 smb_panic_fn = validate_panic;
4265 if (wcache == NULL || wcache->tdb == NULL) {
4266 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4267 } else {
4268 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4271 if (ret != 0) {
4272 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4273 "successful.\n"));
4276 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4277 "function\n"));
4278 smb_panic_fn = smb_panic;
4279 return ret;
4282 bool winbindd_cache_validate_and_initialize(void)
4284 close_winbindd_cache();
4286 if (lp_winbind_offline_logon()) {
4287 if (winbindd_validate_cache() < 0) {
4288 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4289 "could be restored.\n"));
4293 return initialize_winbindd_cache();
4296 /*********************************************************************
4297 ********************************************************************/
4299 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4300 struct winbindd_tdc_domain **domains,
4301 size_t *num_domains )
4303 struct winbindd_tdc_domain *list = NULL;
4304 size_t idx;
4305 int i;
4306 bool set_only = false;
4308 /* don't allow duplicates */
4310 idx = *num_domains;
4311 list = *domains;
4313 for ( i=0; i< (*num_domains); i++ ) {
4314 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4315 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4316 new_dom->name));
4317 idx = i;
4318 set_only = true;
4320 break;
4324 if ( !set_only ) {
4325 if ( !*domains ) {
4326 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4327 idx = 0;
4328 } else {
4329 list = talloc_realloc( *domains, *domains,
4330 struct winbindd_tdc_domain,
4331 (*num_domains)+1);
4332 idx = *num_domains;
4335 ZERO_STRUCT( list[idx] );
4338 if ( !list )
4339 return false;
4341 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4342 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4344 if ( !is_null_sid( &new_dom->sid ) ) {
4345 sid_copy( &list[idx].sid, &new_dom->sid );
4346 } else {
4347 sid_copy(&list[idx].sid, &global_sid_NULL);
4350 if ( new_dom->domain_flags != 0x0 )
4351 list[idx].trust_flags = new_dom->domain_flags;
4353 if ( new_dom->domain_type != 0x0 )
4354 list[idx].trust_type = new_dom->domain_type;
4356 if ( new_dom->domain_trust_attribs != 0x0 )
4357 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4359 if ( !set_only ) {
4360 *domains = list;
4361 *num_domains = idx + 1;
4364 return true;
4367 /*********************************************************************
4368 ********************************************************************/
4370 static TDB_DATA make_tdc_key( const char *domain_name )
4372 char *keystr = NULL;
4373 TDB_DATA key = { NULL, 0 };
4375 if ( !domain_name ) {
4376 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4377 return key;
4380 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4381 return key;
4383 key = string_term_tdb_data(keystr);
4385 return key;
4388 /*********************************************************************
4389 ********************************************************************/
4391 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4392 size_t num_domains,
4393 unsigned char **buf )
4395 unsigned char *buffer = NULL;
4396 int len = 0;
4397 int buflen = 0;
4398 int i = 0;
4400 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4401 (int)num_domains));
4403 buflen = 0;
4405 again:
4406 len = 0;
4408 /* Store the number of array items first */
4409 len += tdb_pack( buffer+len, buflen-len, "d",
4410 num_domains );
4412 /* now pack each domain trust record */
4413 for ( i=0; i<num_domains; i++ ) {
4415 fstring tmp;
4417 if ( buflen > 0 ) {
4418 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4419 domains[i].domain_name,
4420 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4423 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4424 domains[i].domain_name,
4425 domains[i].dns_name,
4426 sid_to_fstring(tmp, &domains[i].sid),
4427 domains[i].trust_flags,
4428 domains[i].trust_attribs,
4429 domains[i].trust_type );
4432 if ( buflen < len ) {
4433 SAFE_FREE(buffer);
4434 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4435 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4436 buflen = -1;
4437 goto done;
4439 buflen = len;
4440 goto again;
4443 *buf = buffer;
4445 done:
4446 return buflen;
4449 /*********************************************************************
4450 ********************************************************************/
4452 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4453 struct winbindd_tdc_domain **domains )
4455 fstring domain_name, dns_name, sid_string;
4456 uint32 type, attribs, flags;
4457 int num_domains;
4458 int len = 0;
4459 int i;
4460 struct winbindd_tdc_domain *list = NULL;
4462 /* get the number of domains */
4463 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4464 if ( len == -1 ) {
4465 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4466 return 0;
4469 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4470 if ( !list ) {
4471 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4472 return 0;
4475 for ( i=0; i<num_domains; i++ ) {
4476 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4477 domain_name,
4478 dns_name,
4479 sid_string,
4480 &flags,
4481 &attribs,
4482 &type );
4484 if ( len == -1 ) {
4485 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4486 TALLOC_FREE( list );
4487 return 0;
4490 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4491 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4492 domain_name, dns_name, sid_string,
4493 flags, attribs, type));
4495 list[i].domain_name = talloc_strdup( list, domain_name );
4496 list[i].dns_name = talloc_strdup( list, dns_name );
4497 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4498 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4499 domain_name));
4501 list[i].trust_flags = flags;
4502 list[i].trust_attribs = attribs;
4503 list[i].trust_type = type;
4506 *domains = list;
4508 return num_domains;
4511 /*********************************************************************
4512 ********************************************************************/
4514 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4516 TDB_DATA key = make_tdc_key( lp_workgroup() );
4517 TDB_DATA data = { NULL, 0 };
4518 int ret;
4520 if ( !key.dptr )
4521 return false;
4523 /* See if we were asked to delete the cache entry */
4525 if ( !domains ) {
4526 ret = tdb_delete( wcache->tdb, key );
4527 goto done;
4530 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4532 if ( !data.dptr ) {
4533 ret = -1;
4534 goto done;
4537 ret = tdb_store( wcache->tdb, key, data, 0 );
4539 done:
4540 SAFE_FREE( data.dptr );
4541 SAFE_FREE( key.dptr );
4543 return ( ret == 0 );
4546 /*********************************************************************
4547 ********************************************************************/
4549 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4551 TDB_DATA key = make_tdc_key( lp_workgroup() );
4552 TDB_DATA data = { NULL, 0 };
4554 *domains = NULL;
4555 *num_domains = 0;
4557 if ( !key.dptr )
4558 return false;
4560 data = tdb_fetch_compat( wcache->tdb, key );
4562 SAFE_FREE( key.dptr );
4564 if ( !data.dptr )
4565 return false;
4567 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4569 SAFE_FREE( data.dptr );
4571 if ( !*domains )
4572 return false;
4574 return true;
4577 /*********************************************************************
4578 ********************************************************************/
4580 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4582 struct winbindd_tdc_domain *dom_list = NULL;
4583 size_t num_domains = 0;
4584 bool ret = false;
4586 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4587 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4588 domain->name, domain->alt_name,
4589 sid_string_dbg(&domain->sid),
4590 domain->domain_flags,
4591 domain->domain_trust_attribs,
4592 domain->domain_type));
4594 if ( !init_wcache() ) {
4595 return false;
4598 /* fetch the list */
4600 wcache_tdc_fetch_list( &dom_list, &num_domains );
4602 /* add the new domain */
4604 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4605 goto done;
4608 /* pack the domain */
4610 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4611 goto done;
4614 /* Success */
4616 ret = true;
4617 done:
4618 TALLOC_FREE( dom_list );
4620 return ret;
4623 /*********************************************************************
4624 ********************************************************************/
4626 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4628 struct winbindd_tdc_domain *dom_list = NULL;
4629 size_t num_domains = 0;
4630 int i;
4631 struct winbindd_tdc_domain *d = NULL;
4633 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4635 if ( !init_wcache() ) {
4636 return NULL;
4639 /* fetch the list */
4641 wcache_tdc_fetch_list( &dom_list, &num_domains );
4643 for ( i=0; i<num_domains; i++ ) {
4644 if ( strequal(name, dom_list[i].domain_name) ||
4645 strequal(name, dom_list[i].dns_name) )
4647 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4648 name));
4650 d = talloc( ctx, struct winbindd_tdc_domain );
4651 if ( !d )
4652 break;
4654 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4655 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4656 sid_copy( &d->sid, &dom_list[i].sid );
4657 d->trust_flags = dom_list[i].trust_flags;
4658 d->trust_type = dom_list[i].trust_type;
4659 d->trust_attribs = dom_list[i].trust_attribs;
4661 break;
4665 TALLOC_FREE( dom_list );
4667 return d;
4670 /*********************************************************************
4671 ********************************************************************/
4673 struct winbindd_tdc_domain*
4674 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4675 const struct dom_sid *sid)
4677 struct winbindd_tdc_domain *dom_list = NULL;
4678 size_t num_domains = 0;
4679 int i;
4680 struct winbindd_tdc_domain *d = NULL;
4682 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4683 sid_string_dbg(sid)));
4685 if (!init_wcache()) {
4686 return NULL;
4689 /* fetch the list */
4691 wcache_tdc_fetch_list(&dom_list, &num_domains);
4693 for (i = 0; i<num_domains; i++) {
4694 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4695 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4696 "Found domain %s for SID %s\n",
4697 dom_list[i].domain_name,
4698 sid_string_dbg(sid)));
4700 d = talloc(ctx, struct winbindd_tdc_domain);
4701 if (!d)
4702 break;
4704 d->domain_name = talloc_strdup(d,
4705 dom_list[i].domain_name);
4707 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4708 sid_copy(&d->sid, &dom_list[i].sid);
4709 d->trust_flags = dom_list[i].trust_flags;
4710 d->trust_type = dom_list[i].trust_type;
4711 d->trust_attribs = dom_list[i].trust_attribs;
4713 break;
4717 TALLOC_FREE(dom_list);
4719 return d;
4723 /*********************************************************************
4724 ********************************************************************/
4726 void wcache_tdc_clear( void )
4728 if ( !init_wcache() )
4729 return;
4731 wcache_tdc_store_list( NULL, 0 );
4733 return;
4737 /*********************************************************************
4738 ********************************************************************/
4740 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4741 NTSTATUS status,
4742 const struct dom_sid *user_sid,
4743 const char *homedir,
4744 const char *shell,
4745 const char *gecos,
4746 uint32 gid)
4748 struct cache_entry *centry;
4749 fstring tmp;
4751 if ( (centry = centry_start(domain, status)) == NULL )
4752 return;
4754 centry_put_string( centry, homedir );
4755 centry_put_string( centry, shell );
4756 centry_put_string( centry, gecos );
4757 centry_put_uint32( centry, gid );
4759 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4761 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4763 centry_free(centry);
4766 #ifdef HAVE_ADS
4768 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4769 const struct dom_sid *user_sid,
4770 TALLOC_CTX *ctx,
4771 const char **homedir, const char **shell,
4772 const char **gecos, gid_t *p_gid)
4774 struct winbind_cache *cache = get_cache(domain);
4775 struct cache_entry *centry = NULL;
4776 NTSTATUS nt_status;
4777 fstring tmp;
4779 if (!cache->tdb)
4780 goto do_query;
4782 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4783 sid_to_fstring(tmp, user_sid));
4785 if (!centry)
4786 goto do_query;
4788 *homedir = centry_string( centry, ctx );
4789 *shell = centry_string( centry, ctx );
4790 *gecos = centry_string( centry, ctx );
4791 *p_gid = centry_uint32( centry );
4793 centry_free(centry);
4795 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4796 sid_string_dbg(user_sid)));
4798 return NT_STATUS_OK;
4800 do_query:
4802 nt_status = nss_get_info( domain->name, user_sid, ctx,
4803 homedir, shell, gecos, p_gid );
4805 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4807 if ( NT_STATUS_IS_OK(nt_status) ) {
4808 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4809 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4810 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4811 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4813 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4814 *homedir, *shell, *gecos, *p_gid );
4817 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4818 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4819 domain->name ));
4820 set_domain_offline( domain );
4823 return nt_status;
4826 #endif
4828 /* the cache backend methods are exposed via this structure */
4829 struct winbindd_methods cache_methods = {
4830 true,
4831 query_user_list,
4832 enum_dom_groups,
4833 enum_local_groups,
4834 name_to_sid,
4835 sid_to_name,
4836 rids_to_names,
4837 query_user,
4838 lookup_usergroups,
4839 lookup_useraliases,
4840 lookup_groupmem,
4841 sequence_number,
4842 lockout_policy,
4843 password_policy,
4844 trusted_domains
4847 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4848 uint32_t opnum, const DATA_BLOB *req,
4849 TDB_DATA *pkey)
4851 char *key;
4852 size_t keylen;
4854 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4855 if (key == NULL) {
4856 return false;
4858 keylen = talloc_get_size(key) - 1;
4860 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4861 if (key == NULL) {
4862 return false;
4864 memcpy(key + keylen, req->data, req->length);
4866 pkey->dptr = (uint8_t *)key;
4867 pkey->dsize = talloc_get_size(key);
4868 return true;
4871 static bool wcache_opnum_cacheable(uint32_t opnum)
4873 switch (opnum) {
4874 case NDR_WBINT_PING:
4875 case NDR_WBINT_QUERYSEQUENCENUMBER:
4876 case NDR_WBINT_ALLOCATEUID:
4877 case NDR_WBINT_ALLOCATEGID:
4878 case NDR_WBINT_CHECKMACHINEACCOUNT:
4879 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4880 case NDR_WBINT_PINGDC:
4881 return false;
4883 return true;
4886 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4887 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4889 TDB_DATA key, data;
4890 bool ret = false;
4892 if (!wcache_opnum_cacheable(opnum) ||
4893 is_my_own_sam_domain(domain) ||
4894 is_builtin_domain(domain)) {
4895 return false;
4898 if (wcache->tdb == NULL) {
4899 return false;
4902 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4903 return false;
4905 data = tdb_fetch_compat(wcache->tdb, key);
4906 TALLOC_FREE(key.dptr);
4908 if (data.dptr == NULL) {
4909 return false;
4911 if (data.dsize < 12) {
4912 goto fail;
4915 if (!is_domain_offline(domain)) {
4916 uint32_t entry_seqnum, dom_seqnum, last_check;
4917 uint64_t entry_timeout;
4919 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4920 &last_check)) {
4921 goto fail;
4923 entry_seqnum = IVAL(data.dptr, 0);
4924 if (entry_seqnum != dom_seqnum) {
4925 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4926 (int)entry_seqnum));
4927 goto fail;
4929 entry_timeout = BVAL(data.dptr, 4);
4930 if (time(NULL) > entry_timeout) {
4931 DEBUG(10, ("Entry has timed out\n"));
4932 goto fail;
4936 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4937 data.dsize - 12);
4938 if (resp->data == NULL) {
4939 DEBUG(10, ("talloc failed\n"));
4940 goto fail;
4942 resp->length = data.dsize - 12;
4944 ret = true;
4945 fail:
4946 SAFE_FREE(data.dptr);
4947 return ret;
4950 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4951 const DATA_BLOB *req, const DATA_BLOB *resp)
4953 TDB_DATA key, data;
4954 uint32_t dom_seqnum, last_check;
4955 uint64_t timeout;
4957 if (!wcache_opnum_cacheable(opnum) ||
4958 is_my_own_sam_domain(domain) ||
4959 is_builtin_domain(domain)) {
4960 return;
4963 if (wcache->tdb == NULL) {
4964 return;
4967 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4968 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4969 domain->name));
4970 return;
4973 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4974 return;
4977 timeout = time(NULL) + lp_winbind_cache_time();
4979 data.dsize = resp->length + 12;
4980 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4981 if (data.dptr == NULL) {
4982 goto done;
4985 SIVAL(data.dptr, 0, dom_seqnum);
4986 SBVAL(data.dptr, 4, timeout);
4987 memcpy(data.dptr + 12, resp->data, resp->length);
4989 tdb_store(wcache->tdb, key, data, 0);
4991 done:
4992 TALLOC_FREE(key.dptr);
4993 return;