s4:librpc/rpc: also call dcerpc_schedule_io_trigger() after bind and alter_context...
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob2c9dd4a9b3e19472e9cfd0ceaff1d1b669cb037c
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;
946 centry_put_uint32(centry, type);
947 centry_put_sid(centry, sid);
948 fstrcpy(uname, name);
949 (void)strupper_m(uname);
950 centry_end(centry, "NS/%s/%s", domain_name, uname);
951 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
952 uname, sid_string_dbg(sid), nt_errstr(status)));
953 centry_free(centry);
956 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
957 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
959 struct cache_entry *centry;
960 fstring sid_string;
962 centry = centry_start(domain, status);
963 if (!centry)
964 return;
966 if (NT_STATUS_IS_OK(status)) {
967 centry_put_uint32(centry, type);
968 centry_put_string(centry, domain_name);
969 centry_put_string(centry, name);
972 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
973 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
974 domain_name, name, nt_errstr(status)));
975 centry_free(centry);
979 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
980 struct wbint_userinfo *info)
982 struct cache_entry *centry;
983 fstring sid_string;
985 if (is_null_sid(&info->user_sid)) {
986 return;
989 centry = centry_start(domain, status);
990 if (!centry)
991 return;
992 centry_put_string(centry, info->acct_name);
993 centry_put_string(centry, info->full_name);
994 centry_put_string(centry, info->homedir);
995 centry_put_string(centry, info->shell);
996 centry_put_uint32(centry, info->primary_gid);
997 centry_put_sid(centry, &info->user_sid);
998 centry_put_sid(centry, &info->group_sid);
999 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1000 &info->user_sid));
1001 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1002 centry_free(centry);
1005 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1006 NTSTATUS status,
1007 struct samr_DomInfo12 *lockout_policy)
1009 struct cache_entry *centry;
1011 centry = centry_start(domain, status);
1012 if (!centry)
1013 return;
1015 centry_put_nttime(centry, lockout_policy->lockout_duration);
1016 centry_put_nttime(centry, lockout_policy->lockout_window);
1017 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1019 centry_end(centry, "LOC_POL/%s", domain->name);
1021 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1023 centry_free(centry);
1028 static void wcache_save_password_policy(struct winbindd_domain *domain,
1029 NTSTATUS status,
1030 struct samr_DomInfo1 *policy)
1032 struct cache_entry *centry;
1034 centry = centry_start(domain, status);
1035 if (!centry)
1036 return;
1038 centry_put_uint16(centry, policy->min_password_length);
1039 centry_put_uint16(centry, policy->password_history_length);
1040 centry_put_uint32(centry, policy->password_properties);
1041 centry_put_nttime(centry, policy->max_password_age);
1042 centry_put_nttime(centry, policy->min_password_age);
1044 centry_end(centry, "PWD_POL/%s", domain->name);
1046 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1048 centry_free(centry);
1051 /***************************************************************************
1052 ***************************************************************************/
1054 static void wcache_save_username_alias(struct winbindd_domain *domain,
1055 NTSTATUS status,
1056 const char *name, const char *alias)
1058 struct cache_entry *centry;
1059 fstring uname;
1061 if ( (centry = centry_start(domain, status)) == NULL )
1062 return;
1064 centry_put_string( centry, alias );
1066 fstrcpy(uname, name);
1067 (void)strupper_m(uname);
1068 centry_end(centry, "NSS/NA/%s", uname);
1070 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1072 centry_free(centry);
1075 static void wcache_save_alias_username(struct winbindd_domain *domain,
1076 NTSTATUS status,
1077 const char *alias, const char *name)
1079 struct cache_entry *centry;
1080 fstring uname;
1082 if ( (centry = centry_start(domain, status)) == NULL )
1083 return;
1085 centry_put_string( centry, name );
1087 fstrcpy(uname, alias);
1088 (void)strupper_m(uname);
1089 centry_end(centry, "NSS/AN/%s", uname);
1091 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1093 centry_free(centry);
1096 /***************************************************************************
1097 ***************************************************************************/
1099 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1100 struct winbindd_domain *domain,
1101 const char *name, char **alias )
1103 struct winbind_cache *cache = get_cache(domain);
1104 struct cache_entry *centry = NULL;
1105 NTSTATUS status;
1106 char *upper_name;
1108 if ( domain->internal )
1109 return NT_STATUS_NOT_SUPPORTED;
1111 if (!cache->tdb)
1112 goto do_query;
1114 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1115 return NT_STATUS_NO_MEMORY;
1116 if (!strupper_m(upper_name)) {
1117 SAFE_FREE(name);
1118 return NT_STATUS_INVALID_PARAMETER;
1121 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1123 SAFE_FREE( upper_name );
1125 if (!centry)
1126 goto do_query;
1128 status = centry->status;
1130 if (!NT_STATUS_IS_OK(status)) {
1131 centry_free(centry);
1132 return status;
1135 *alias = centry_string( centry, mem_ctx );
1137 centry_free(centry);
1139 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1140 name, *alias ? *alias : "(none)"));
1142 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1144 do_query:
1146 /* If its not in cache and we are offline, then fail */
1148 if ( get_global_winbindd_state_offline() || !domain->online ) {
1149 DEBUG(8,("resolve_username_to_alias: rejecting query "
1150 "in offline mode\n"));
1151 return NT_STATUS_NOT_FOUND;
1154 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1156 if ( NT_STATUS_IS_OK( status ) ) {
1157 wcache_save_username_alias(domain, status, name, *alias);
1160 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1161 wcache_save_username_alias(domain, status, name, "(NULL)");
1164 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1165 nt_errstr(status)));
1167 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1168 set_domain_offline( domain );
1171 return status;
1174 /***************************************************************************
1175 ***************************************************************************/
1177 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1178 struct winbindd_domain *domain,
1179 const char *alias, char **name )
1181 struct winbind_cache *cache = get_cache(domain);
1182 struct cache_entry *centry = NULL;
1183 NTSTATUS status;
1184 char *upper_name;
1186 if ( domain->internal )
1187 return NT_STATUS_NOT_SUPPORTED;
1189 if (!cache->tdb)
1190 goto do_query;
1192 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1193 return NT_STATUS_NO_MEMORY;
1194 if (!strupper_m(upper_name)) {
1195 SAFE_FREE(alias);
1196 return NT_STATUS_INVALID_PARAMETER;
1199 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1201 SAFE_FREE( upper_name );
1203 if (!centry)
1204 goto do_query;
1206 status = centry->status;
1208 if (!NT_STATUS_IS_OK(status)) {
1209 centry_free(centry);
1210 return status;
1213 *name = centry_string( centry, mem_ctx );
1215 centry_free(centry);
1217 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1218 alias, *name ? *name : "(none)"));
1220 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1222 do_query:
1224 /* If its not in cache and we are offline, then fail */
1226 if ( get_global_winbindd_state_offline() || !domain->online ) {
1227 DEBUG(8,("resolve_alias_to_username: rejecting query "
1228 "in offline mode\n"));
1229 return NT_STATUS_NOT_FOUND;
1232 /* an alias cannot contain a domain prefix or '@' */
1234 if (strchr(alias, '\\') || strchr(alias, '@')) {
1235 DEBUG(10,("resolve_alias_to_username: skipping fully "
1236 "qualified name %s\n", alias));
1237 return NT_STATUS_OBJECT_NAME_INVALID;
1240 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1242 if ( NT_STATUS_IS_OK( status ) ) {
1243 wcache_save_alias_username( domain, status, alias, *name );
1246 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1247 wcache_save_alias_username(domain, status, alias, "(NULL)");
1250 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1251 nt_errstr(status)));
1253 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1254 set_domain_offline( domain );
1257 return status;
1260 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1262 struct winbind_cache *cache = get_cache(domain);
1263 TDB_DATA data;
1264 fstring key_str, tmp;
1265 uint32 rid;
1267 if (!cache->tdb) {
1268 return NT_STATUS_INTERNAL_DB_ERROR;
1271 if (is_null_sid(sid)) {
1272 return NT_STATUS_INVALID_SID;
1275 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1276 return NT_STATUS_INVALID_SID;
1279 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1281 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1282 if (!data.dptr) {
1283 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1286 SAFE_FREE(data.dptr);
1287 return NT_STATUS_OK;
1290 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1291 as new salted ones. */
1293 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1294 TALLOC_CTX *mem_ctx,
1295 const struct dom_sid *sid,
1296 const uint8 **cached_nt_pass,
1297 const uint8 **cached_salt)
1299 struct winbind_cache *cache = get_cache(domain);
1300 struct cache_entry *centry = NULL;
1301 NTSTATUS status;
1302 uint32 rid;
1303 fstring tmp;
1305 if (!winbindd_use_cache()) {
1306 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1309 if (!cache->tdb) {
1310 return NT_STATUS_INTERNAL_DB_ERROR;
1313 if (is_null_sid(sid)) {
1314 return NT_STATUS_INVALID_SID;
1317 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1318 return NT_STATUS_INVALID_SID;
1321 /* Try and get a salted cred first. If we can't
1322 fall back to an unsalted cred. */
1324 centry = wcache_fetch(cache, domain, "CRED/%s",
1325 sid_to_fstring(tmp, sid));
1326 if (!centry) {
1327 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1328 sid_string_dbg(sid)));
1329 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1332 /* In the salted case this isn't actually the nt_hash itself,
1333 but the MD5 of the salt + nt_hash. Let the caller
1334 sort this out. It can tell as we only return the cached_salt
1335 if we are returning a salted cred. */
1337 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1338 if (*cached_nt_pass == NULL) {
1339 fstring sidstr;
1341 sid_to_fstring(sidstr, sid);
1343 /* Bad (old) cred cache. Delete and pretend we
1344 don't have it. */
1345 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1346 sidstr));
1347 wcache_delete("CRED/%s", sidstr);
1348 centry_free(centry);
1349 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1352 /* We only have 17 bytes more data in the salted cred case. */
1353 if (centry->len - centry->ofs == 17) {
1354 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1355 } else {
1356 *cached_salt = NULL;
1359 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1360 if (*cached_salt) {
1361 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1364 status = centry->status;
1366 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1367 sid_string_dbg(sid), nt_errstr(status) ));
1369 centry_free(centry);
1370 return status;
1373 /* Store creds for a SID - only writes out new salted ones. */
1375 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1376 const struct dom_sid *sid,
1377 const uint8 nt_pass[NT_HASH_LEN])
1379 struct cache_entry *centry;
1380 fstring sid_string;
1381 uint32 rid;
1382 uint8 cred_salt[NT_HASH_LEN];
1383 uint8 salted_hash[NT_HASH_LEN];
1385 if (is_null_sid(sid)) {
1386 return NT_STATUS_INVALID_SID;
1389 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1390 return NT_STATUS_INVALID_SID;
1393 centry = centry_start(domain, NT_STATUS_OK);
1394 if (!centry) {
1395 return NT_STATUS_INTERNAL_DB_ERROR;
1398 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1400 centry_put_time(centry, time(NULL));
1402 /* Create a salt and then salt the hash. */
1403 generate_random_buffer(cred_salt, NT_HASH_LEN);
1404 E_md5hash(cred_salt, nt_pass, salted_hash);
1406 centry_put_hash16(centry, salted_hash);
1407 centry_put_hash16(centry, cred_salt);
1408 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1410 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1412 centry_free(centry);
1414 return NT_STATUS_OK;
1418 /* Query display info. This is the basic user list fn */
1419 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1420 TALLOC_CTX *mem_ctx,
1421 uint32 *num_entries,
1422 struct wbint_userinfo **info)
1424 struct winbind_cache *cache = get_cache(domain);
1425 struct cache_entry *centry = NULL;
1426 NTSTATUS status;
1427 unsigned int i, retry;
1428 bool old_status = domain->online;
1430 if (!cache->tdb)
1431 goto do_query;
1433 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1434 if (!centry)
1435 goto do_query;
1437 do_fetch_cache:
1438 *num_entries = centry_uint32(centry);
1440 if (*num_entries == 0)
1441 goto do_cached;
1443 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1444 if (! (*info)) {
1445 smb_panic_fn("query_user_list out of memory");
1447 for (i=0; i<(*num_entries); i++) {
1448 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1449 (*info)[i].full_name = centry_string(centry, mem_ctx);
1450 (*info)[i].homedir = centry_string(centry, mem_ctx);
1451 (*info)[i].shell = centry_string(centry, mem_ctx);
1452 centry_sid(centry, &(*info)[i].user_sid);
1453 centry_sid(centry, &(*info)[i].group_sid);
1456 do_cached:
1457 status = centry->status;
1459 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1460 domain->name, nt_errstr(status) ));
1462 centry_free(centry);
1463 return status;
1465 do_query:
1466 *num_entries = 0;
1467 *info = NULL;
1469 /* Return status value returned by seq number check */
1471 if (!NT_STATUS_IS_OK(domain->last_status))
1472 return domain->last_status;
1474 /* Put the query_user_list() in a retry loop. There appears to be
1475 * some bug either with Windows 2000 or Samba's handling of large
1476 * rpc replies. This manifests itself as sudden disconnection
1477 * at a random point in the enumeration of a large (60k) user list.
1478 * The retry loop simply tries the operation again. )-: It's not
1479 * pretty but an acceptable workaround until we work out what the
1480 * real problem is. */
1482 retry = 0;
1483 do {
1485 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1486 domain->name ));
1488 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1489 if (!NT_STATUS_IS_OK(status)) {
1490 DEBUG(3, ("query_user_list: returned 0x%08x, "
1491 "retrying\n", NT_STATUS_V(status)));
1493 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1494 DEBUG(3, ("query_user_list: flushing "
1495 "connection cache\n"));
1496 invalidate_cm_connection(&domain->conn);
1498 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1499 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1500 if (!domain->internal && old_status) {
1501 set_domain_offline(domain);
1503 /* store partial response. */
1504 if (*num_entries > 0) {
1506 * humm, what about the status used for cache?
1507 * Should it be NT_STATUS_OK?
1509 break;
1512 * domain is offline now, and there is no user entries,
1513 * try to fetch from cache again.
1515 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1516 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1517 /* partial response... */
1518 if (!centry) {
1519 goto skip_save;
1520 } else {
1521 goto do_fetch_cache;
1523 } else {
1524 goto skip_save;
1528 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1529 (retry++ < 5));
1531 /* and save it */
1532 refresh_sequence_number(domain, false);
1533 if (!NT_STATUS_IS_OK(status)) {
1534 return status;
1536 centry = centry_start(domain, status);
1537 if (!centry)
1538 goto skip_save;
1539 centry_put_uint32(centry, *num_entries);
1540 for (i=0; i<(*num_entries); i++) {
1541 centry_put_string(centry, (*info)[i].acct_name);
1542 centry_put_string(centry, (*info)[i].full_name);
1543 centry_put_string(centry, (*info)[i].homedir);
1544 centry_put_string(centry, (*info)[i].shell);
1545 centry_put_sid(centry, &(*info)[i].user_sid);
1546 centry_put_sid(centry, &(*info)[i].group_sid);
1547 if (domain->backend && domain->backend->consistent) {
1548 /* when the backend is consistent we can pre-prime some mappings */
1549 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1550 domain->name,
1551 (*info)[i].acct_name,
1552 &(*info)[i].user_sid,
1553 SID_NAME_USER);
1554 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1555 &(*info)[i].user_sid,
1556 domain->name,
1557 (*info)[i].acct_name,
1558 SID_NAME_USER);
1559 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1562 centry_end(centry, "UL/%s", domain->name);
1563 centry_free(centry);
1565 skip_save:
1566 return status;
1569 /* list all domain groups */
1570 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1571 TALLOC_CTX *mem_ctx,
1572 uint32 *num_entries,
1573 struct wb_acct_info **info)
1575 struct winbind_cache *cache = get_cache(domain);
1576 struct cache_entry *centry = NULL;
1577 NTSTATUS status;
1578 unsigned int i;
1579 bool old_status;
1581 old_status = domain->online;
1582 if (!cache->tdb)
1583 goto do_query;
1585 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1586 if (!centry)
1587 goto do_query;
1589 do_fetch_cache:
1590 *num_entries = centry_uint32(centry);
1592 if (*num_entries == 0)
1593 goto do_cached;
1595 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1596 if (! (*info)) {
1597 smb_panic_fn("enum_dom_groups out of memory");
1599 for (i=0; i<(*num_entries); i++) {
1600 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1601 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1602 (*info)[i].rid = centry_uint32(centry);
1605 do_cached:
1606 status = centry->status;
1608 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1609 domain->name, nt_errstr(status) ));
1611 centry_free(centry);
1612 return status;
1614 do_query:
1615 *num_entries = 0;
1616 *info = NULL;
1618 /* Return status value returned by seq number check */
1620 if (!NT_STATUS_IS_OK(domain->last_status))
1621 return domain->last_status;
1623 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1624 domain->name ));
1626 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1628 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1629 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1630 if (!domain->internal && old_status) {
1631 set_domain_offline(domain);
1633 if (cache->tdb &&
1634 !domain->online &&
1635 !domain->internal &&
1636 old_status) {
1637 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1638 if (centry) {
1639 goto do_fetch_cache;
1643 /* and save it */
1644 refresh_sequence_number(domain, false);
1645 if (!NT_STATUS_IS_OK(status)) {
1646 return status;
1648 centry = centry_start(domain, status);
1649 if (!centry)
1650 goto skip_save;
1651 centry_put_uint32(centry, *num_entries);
1652 for (i=0; i<(*num_entries); i++) {
1653 centry_put_string(centry, (*info)[i].acct_name);
1654 centry_put_string(centry, (*info)[i].acct_desc);
1655 centry_put_uint32(centry, (*info)[i].rid);
1657 centry_end(centry, "GL/%s/domain", domain->name);
1658 centry_free(centry);
1660 skip_save:
1661 return status;
1664 /* list all domain groups */
1665 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1666 TALLOC_CTX *mem_ctx,
1667 uint32 *num_entries,
1668 struct wb_acct_info **info)
1670 struct winbind_cache *cache = get_cache(domain);
1671 struct cache_entry *centry = NULL;
1672 NTSTATUS status;
1673 unsigned int i;
1674 bool old_status;
1676 old_status = domain->online;
1677 if (!cache->tdb)
1678 goto do_query;
1680 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1681 if (!centry)
1682 goto do_query;
1684 do_fetch_cache:
1685 *num_entries = centry_uint32(centry);
1687 if (*num_entries == 0)
1688 goto do_cached;
1690 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1691 if (! (*info)) {
1692 smb_panic_fn("enum_dom_groups out of memory");
1694 for (i=0; i<(*num_entries); i++) {
1695 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1696 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1697 (*info)[i].rid = centry_uint32(centry);
1700 do_cached:
1702 /* If we are returning cached data and the domain controller
1703 is down then we don't know whether the data is up to date
1704 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1705 indicate this. */
1707 if (wcache_server_down(domain)) {
1708 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1709 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1710 } else
1711 status = centry->status;
1713 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1714 domain->name, nt_errstr(status) ));
1716 centry_free(centry);
1717 return status;
1719 do_query:
1720 *num_entries = 0;
1721 *info = NULL;
1723 /* Return status value returned by seq number check */
1725 if (!NT_STATUS_IS_OK(domain->last_status))
1726 return domain->last_status;
1728 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1729 domain->name ));
1731 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1733 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1734 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1735 if (!domain->internal && old_status) {
1736 set_domain_offline(domain);
1738 if (cache->tdb &&
1739 !domain->internal &&
1740 !domain->online &&
1741 old_status) {
1742 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1743 if (centry) {
1744 goto do_fetch_cache;
1748 /* and save it */
1749 refresh_sequence_number(domain, false);
1750 if (!NT_STATUS_IS_OK(status)) {
1751 return status;
1753 centry = centry_start(domain, status);
1754 if (!centry)
1755 goto skip_save;
1756 centry_put_uint32(centry, *num_entries);
1757 for (i=0; i<(*num_entries); i++) {
1758 centry_put_string(centry, (*info)[i].acct_name);
1759 centry_put_string(centry, (*info)[i].acct_desc);
1760 centry_put_uint32(centry, (*info)[i].rid);
1762 centry_end(centry, "GL/%s/local", domain->name);
1763 centry_free(centry);
1765 skip_save:
1766 return status;
1769 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1770 const char *domain_name,
1771 const char *name,
1772 struct dom_sid *sid,
1773 enum lsa_SidType *type)
1775 struct winbind_cache *cache = get_cache(domain);
1776 struct cache_entry *centry;
1777 NTSTATUS status;
1778 char *uname;
1780 if (cache->tdb == NULL) {
1781 return NT_STATUS_NOT_FOUND;
1784 uname = talloc_strdup_upper(talloc_tos(), name);
1785 if (uname == NULL) {
1786 return NT_STATUS_NO_MEMORY;
1789 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1790 TALLOC_FREE(uname);
1791 if (centry == NULL) {
1792 return NT_STATUS_NOT_FOUND;
1795 status = centry->status;
1796 if (NT_STATUS_IS_OK(status)) {
1797 *type = (enum lsa_SidType)centry_uint32(centry);
1798 centry_sid(centry, sid);
1801 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1802 "%s\n", domain->name, nt_errstr(status) ));
1804 centry_free(centry);
1805 return status;
1808 /* convert a single name to a sid in a domain */
1809 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1810 TALLOC_CTX *mem_ctx,
1811 const char *domain_name,
1812 const char *name,
1813 uint32_t flags,
1814 struct dom_sid *sid,
1815 enum lsa_SidType *type)
1817 NTSTATUS status;
1818 bool old_status;
1820 old_status = domain->online;
1822 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1823 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1824 return status;
1827 ZERO_STRUCTP(sid);
1829 /* If the seq number check indicated that there is a problem
1830 * with this DC, then return that status... except for
1831 * access_denied. This is special because the dc may be in
1832 * "restrict anonymous = 1" mode, in which case it will deny
1833 * most unauthenticated operations, but *will* allow the LSA
1834 * name-to-sid that we try as a fallback. */
1836 if (!(NT_STATUS_IS_OK(domain->last_status)
1837 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1838 return domain->last_status;
1840 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1841 domain->name ));
1843 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1844 name, flags, sid, type);
1846 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1847 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1848 if (!domain->internal && old_status) {
1849 set_domain_offline(domain);
1851 if (!domain->internal &&
1852 !domain->online &&
1853 old_status) {
1854 NTSTATUS cache_status;
1855 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1856 return cache_status;
1859 /* and save it */
1860 refresh_sequence_number(domain, false);
1862 if (domain->online &&
1863 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1864 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1866 /* Only save the reverse mapping if this was not a UPN */
1867 if (!strchr(name, '@')) {
1868 if (!strupper_m(discard_const_p(char, domain_name))) {
1869 return NT_STATUS_INVALID_PARAMETER;
1871 (void)strlower_m(discard_const_p(char, name));
1872 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1876 return status;
1879 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1880 const struct dom_sid *sid,
1881 TALLOC_CTX *mem_ctx,
1882 char **domain_name,
1883 char **name,
1884 enum lsa_SidType *type)
1886 struct winbind_cache *cache = get_cache(domain);
1887 struct cache_entry *centry;
1888 char *sid_string;
1889 NTSTATUS status;
1891 if (cache->tdb == NULL) {
1892 return NT_STATUS_NOT_FOUND;
1895 sid_string = sid_string_tos(sid);
1896 if (sid_string == NULL) {
1897 return NT_STATUS_NO_MEMORY;
1900 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1901 TALLOC_FREE(sid_string);
1902 if (centry == NULL) {
1903 return NT_STATUS_NOT_FOUND;
1906 if (NT_STATUS_IS_OK(centry->status)) {
1907 *type = (enum lsa_SidType)centry_uint32(centry);
1908 *domain_name = centry_string(centry, mem_ctx);
1909 *name = centry_string(centry, mem_ctx);
1912 status = centry->status;
1913 centry_free(centry);
1915 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1916 "%s\n", domain->name, nt_errstr(status) ));
1918 return status;
1921 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1922 given */
1923 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1924 TALLOC_CTX *mem_ctx,
1925 const struct dom_sid *sid,
1926 char **domain_name,
1927 char **name,
1928 enum lsa_SidType *type)
1930 NTSTATUS status;
1931 bool old_status;
1933 old_status = domain->online;
1934 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1935 type);
1936 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1937 return status;
1940 *name = NULL;
1941 *domain_name = NULL;
1943 /* If the seq number check indicated that there is a problem
1944 * with this DC, then return that status... except for
1945 * access_denied. This is special because the dc may be in
1946 * "restrict anonymous = 1" mode, in which case it will deny
1947 * most unauthenticated operations, but *will* allow the LSA
1948 * sid-to-name that we try as a fallback. */
1950 if (!(NT_STATUS_IS_OK(domain->last_status)
1951 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1952 return domain->last_status;
1954 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1955 domain->name ));
1957 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1959 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1960 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1961 if (!domain->internal && old_status) {
1962 set_domain_offline(domain);
1964 if (!domain->internal &&
1965 !domain->online &&
1966 old_status) {
1967 NTSTATUS cache_status;
1968 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1969 domain_name, name, type);
1970 return cache_status;
1973 /* and save it */
1974 refresh_sequence_number(domain, false);
1975 if (!NT_STATUS_IS_OK(status)) {
1976 return status;
1978 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1980 /* We can't save the name to sid mapping here, as with sid history a
1981 * later name2sid would give the wrong sid. */
1983 return status;
1986 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1987 TALLOC_CTX *mem_ctx,
1988 const struct dom_sid *domain_sid,
1989 uint32 *rids,
1990 size_t num_rids,
1991 char **domain_name,
1992 char ***names,
1993 enum lsa_SidType **types)
1995 struct winbind_cache *cache = get_cache(domain);
1996 size_t i;
1997 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1998 bool have_mapped;
1999 bool have_unmapped;
2000 bool old_status;
2002 old_status = domain->online;
2003 *domain_name = NULL;
2004 *names = NULL;
2005 *types = NULL;
2007 if (!cache->tdb) {
2008 goto do_query;
2011 if (num_rids == 0) {
2012 return NT_STATUS_OK;
2015 *names = talloc_array(mem_ctx, char *, num_rids);
2016 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2018 if ((*names == NULL) || (*types == NULL)) {
2019 result = NT_STATUS_NO_MEMORY;
2020 goto error;
2023 have_mapped = have_unmapped = false;
2025 for (i=0; i<num_rids; i++) {
2026 struct dom_sid sid;
2027 struct cache_entry *centry;
2028 fstring tmp;
2030 if (!sid_compose(&sid, domain_sid, rids[i])) {
2031 result = NT_STATUS_INTERNAL_ERROR;
2032 goto error;
2035 centry = wcache_fetch(cache, domain, "SN/%s",
2036 sid_to_fstring(tmp, &sid));
2037 if (!centry) {
2038 goto do_query;
2041 (*types)[i] = SID_NAME_UNKNOWN;
2042 (*names)[i] = talloc_strdup(*names, "");
2044 if (NT_STATUS_IS_OK(centry->status)) {
2045 char *dom;
2046 have_mapped = true;
2047 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2049 dom = centry_string(centry, mem_ctx);
2050 if (*domain_name == NULL) {
2051 *domain_name = dom;
2052 } else {
2053 talloc_free(dom);
2056 (*names)[i] = centry_string(centry, *names);
2058 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2059 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2060 have_unmapped = true;
2062 } else {
2063 /* something's definitely wrong */
2064 result = centry->status;
2065 goto error;
2068 centry_free(centry);
2071 if (!have_mapped) {
2072 return NT_STATUS_NONE_MAPPED;
2074 if (!have_unmapped) {
2075 return NT_STATUS_OK;
2077 return STATUS_SOME_UNMAPPED;
2079 do_query:
2081 TALLOC_FREE(*names);
2082 TALLOC_FREE(*types);
2084 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2085 rids, num_rids, domain_name,
2086 names, types);
2088 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2089 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2090 if (!domain->internal && old_status) {
2091 set_domain_offline(domain);
2093 if (cache->tdb &&
2094 !domain->internal &&
2095 !domain->online &&
2096 old_status) {
2097 have_mapped = have_unmapped = false;
2099 for (i=0; i<num_rids; i++) {
2100 struct dom_sid sid;
2101 struct cache_entry *centry;
2102 fstring tmp;
2104 if (!sid_compose(&sid, domain_sid, rids[i])) {
2105 result = NT_STATUS_INTERNAL_ERROR;
2106 goto error;
2109 centry = wcache_fetch(cache, domain, "SN/%s",
2110 sid_to_fstring(tmp, &sid));
2111 if (!centry) {
2112 (*types)[i] = SID_NAME_UNKNOWN;
2113 (*names)[i] = talloc_strdup(*names, "");
2114 continue;
2117 (*types)[i] = SID_NAME_UNKNOWN;
2118 (*names)[i] = talloc_strdup(*names, "");
2120 if (NT_STATUS_IS_OK(centry->status)) {
2121 char *dom;
2122 have_mapped = true;
2123 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2125 dom = centry_string(centry, mem_ctx);
2126 if (*domain_name == NULL) {
2127 *domain_name = dom;
2128 } else {
2129 talloc_free(dom);
2132 (*names)[i] = centry_string(centry, *names);
2134 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2135 have_unmapped = true;
2137 } else {
2138 /* something's definitely wrong */
2139 result = centry->status;
2140 goto error;
2143 centry_free(centry);
2146 if (!have_mapped) {
2147 return NT_STATUS_NONE_MAPPED;
2149 if (!have_unmapped) {
2150 return NT_STATUS_OK;
2152 return STATUS_SOME_UNMAPPED;
2156 None of the queried rids has been found so save all negative entries
2158 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2159 for (i = 0; i < num_rids; i++) {
2160 struct dom_sid sid;
2161 const char *name = "";
2162 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2163 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2165 if (!sid_compose(&sid, domain_sid, rids[i])) {
2166 return NT_STATUS_INTERNAL_ERROR;
2169 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2170 name, type);
2173 return result;
2177 Some or all of the queried rids have been found.
2179 if (!NT_STATUS_IS_OK(result) &&
2180 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2181 return result;
2184 refresh_sequence_number(domain, false);
2186 for (i=0; i<num_rids; i++) {
2187 struct dom_sid sid;
2188 NTSTATUS status;
2190 if (!sid_compose(&sid, domain_sid, rids[i])) {
2191 result = NT_STATUS_INTERNAL_ERROR;
2192 goto error;
2195 status = (*types)[i] == SID_NAME_UNKNOWN ?
2196 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2198 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2199 (*names)[i], (*types)[i]);
2202 return result;
2204 error:
2205 TALLOC_FREE(*names);
2206 TALLOC_FREE(*types);
2207 return result;
2210 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2211 TALLOC_CTX *mem_ctx,
2212 const struct dom_sid *user_sid,
2213 struct wbint_userinfo *info)
2215 struct winbind_cache *cache = get_cache(domain);
2216 struct cache_entry *centry = NULL;
2217 NTSTATUS status;
2218 char *sid_string;
2220 if (cache->tdb == NULL) {
2221 return NT_STATUS_NOT_FOUND;
2224 sid_string = sid_string_tos(user_sid);
2225 if (sid_string == NULL) {
2226 return NT_STATUS_NO_MEMORY;
2229 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2230 TALLOC_FREE(sid_string);
2231 if (centry == NULL) {
2232 return NT_STATUS_NOT_FOUND;
2236 * If we have an access denied cache entry and a cached info3
2237 * in the samlogon cache then do a query. This will force the
2238 * rpc back end to return the info3 data.
2241 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2242 netsamlogon_cache_have(user_sid)) {
2243 DEBUG(10, ("query_user: cached access denied and have cached "
2244 "info3\n"));
2245 domain->last_status = NT_STATUS_OK;
2246 centry_free(centry);
2247 return NT_STATUS_NOT_FOUND;
2250 /* if status is not ok then this is a negative hit
2251 and the rest of the data doesn't matter */
2252 status = centry->status;
2253 if (NT_STATUS_IS_OK(status)) {
2254 info->acct_name = centry_string(centry, mem_ctx);
2255 info->full_name = centry_string(centry, mem_ctx);
2256 info->homedir = centry_string(centry, mem_ctx);
2257 info->shell = centry_string(centry, mem_ctx);
2258 info->primary_gid = centry_uint32(centry);
2259 centry_sid(centry, &info->user_sid);
2260 centry_sid(centry, &info->group_sid);
2263 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2264 "%s\n", domain->name, nt_errstr(status) ));
2266 centry_free(centry);
2267 return status;
2270 /* Lookup user information from a rid */
2271 static NTSTATUS query_user(struct winbindd_domain *domain,
2272 TALLOC_CTX *mem_ctx,
2273 const struct dom_sid *user_sid,
2274 struct wbint_userinfo *info)
2276 NTSTATUS status;
2277 bool old_status;
2279 old_status = domain->online;
2280 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2281 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2282 return status;
2285 ZERO_STRUCTP(info);
2287 /* Return status value returned by seq number check */
2289 if (!NT_STATUS_IS_OK(domain->last_status))
2290 return domain->last_status;
2292 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2293 domain->name ));
2295 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2297 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2298 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2299 if (!domain->internal && old_status) {
2300 set_domain_offline(domain);
2302 if (!domain->internal &&
2303 !domain->online &&
2304 old_status) {
2305 NTSTATUS cache_status;
2306 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2307 return cache_status;
2310 /* and save it */
2311 refresh_sequence_number(domain, false);
2312 if (!NT_STATUS_IS_OK(status)) {
2313 return status;
2315 wcache_save_user(domain, status, info);
2317 return status;
2320 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2321 TALLOC_CTX *mem_ctx,
2322 const struct dom_sid *user_sid,
2323 uint32_t *pnum_sids,
2324 struct dom_sid **psids)
2326 struct winbind_cache *cache = get_cache(domain);
2327 struct cache_entry *centry = NULL;
2328 NTSTATUS status;
2329 uint32_t i, num_sids;
2330 struct dom_sid *sids;
2331 fstring sid_string;
2333 if (cache->tdb == NULL) {
2334 return NT_STATUS_NOT_FOUND;
2337 centry = wcache_fetch(cache, domain, "UG/%s",
2338 sid_to_fstring(sid_string, user_sid));
2339 if (centry == NULL) {
2340 return NT_STATUS_NOT_FOUND;
2343 /* If we have an access denied cache entry and a cached info3 in the
2344 samlogon cache then do a query. This will force the rpc back end
2345 to return the info3 data. */
2347 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2348 && netsamlogon_cache_have(user_sid)) {
2349 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2350 "cached info3\n"));
2351 domain->last_status = NT_STATUS_OK;
2352 centry_free(centry);
2353 return NT_STATUS_NOT_FOUND;
2356 num_sids = centry_uint32(centry);
2357 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2358 if (sids == NULL) {
2359 centry_free(centry);
2360 return NT_STATUS_NO_MEMORY;
2363 for (i=0; i<num_sids; i++) {
2364 centry_sid(centry, &sids[i]);
2367 status = centry->status;
2369 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2370 "status: %s\n", domain->name, nt_errstr(status)));
2372 centry_free(centry);
2374 *pnum_sids = num_sids;
2375 *psids = sids;
2376 return status;
2379 /* Lookup groups a user is a member of. */
2380 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2381 TALLOC_CTX *mem_ctx,
2382 const struct dom_sid *user_sid,
2383 uint32 *num_groups, struct dom_sid **user_gids)
2385 struct cache_entry *centry = NULL;
2386 NTSTATUS status;
2387 unsigned int i;
2388 fstring sid_string;
2389 bool old_status;
2391 old_status = domain->online;
2392 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2393 num_groups, user_gids);
2394 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2395 return status;
2398 (*num_groups) = 0;
2399 (*user_gids) = NULL;
2401 /* Return status value returned by seq number check */
2403 if (!NT_STATUS_IS_OK(domain->last_status))
2404 return domain->last_status;
2406 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2407 domain->name ));
2409 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2411 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2412 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2413 if (!domain->internal && old_status) {
2414 set_domain_offline(domain);
2416 if (!domain->internal &&
2417 !domain->online &&
2418 old_status) {
2419 NTSTATUS cache_status;
2420 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2421 num_groups, user_gids);
2422 return cache_status;
2425 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2426 goto skip_save;
2428 /* and save it */
2429 refresh_sequence_number(domain, false);
2430 if (!NT_STATUS_IS_OK(status)) {
2431 return status;
2433 centry = centry_start(domain, status);
2434 if (!centry)
2435 goto skip_save;
2437 centry_put_uint32(centry, *num_groups);
2438 for (i=0; i<(*num_groups); i++) {
2439 centry_put_sid(centry, &(*user_gids)[i]);
2442 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2443 centry_free(centry);
2445 skip_save:
2446 return status;
2449 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2450 const struct dom_sid *sids)
2452 uint32_t i;
2453 char *sidlist;
2455 sidlist = talloc_strdup(mem_ctx, "");
2456 if (sidlist == NULL) {
2457 return NULL;
2459 for (i=0; i<num_sids; i++) {
2460 fstring tmp;
2461 sidlist = talloc_asprintf_append_buffer(
2462 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2463 if (sidlist == NULL) {
2464 return NULL;
2467 return sidlist;
2470 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2471 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2472 const struct dom_sid *sids,
2473 uint32_t *pnum_aliases, uint32_t **paliases)
2475 struct winbind_cache *cache = get_cache(domain);
2476 struct cache_entry *centry = NULL;
2477 uint32_t num_aliases;
2478 uint32_t *aliases;
2479 NTSTATUS status;
2480 char *sidlist;
2481 int i;
2483 if (cache->tdb == NULL) {
2484 return NT_STATUS_NOT_FOUND;
2487 if (num_sids == 0) {
2488 *pnum_aliases = 0;
2489 *paliases = NULL;
2490 return NT_STATUS_OK;
2493 /* We need to cache indexed by the whole list of SIDs, the aliases
2494 * resulting might come from any of the SIDs. */
2496 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2497 if (sidlist == NULL) {
2498 return NT_STATUS_NO_MEMORY;
2501 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2502 TALLOC_FREE(sidlist);
2503 if (centry == NULL) {
2504 return NT_STATUS_NOT_FOUND;
2507 num_aliases = centry_uint32(centry);
2508 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2509 if (aliases == NULL) {
2510 centry_free(centry);
2511 return NT_STATUS_NO_MEMORY;
2514 for (i=0; i<num_aliases; i++) {
2515 aliases[i] = centry_uint32(centry);
2518 status = centry->status;
2520 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2521 "status %s\n", domain->name, nt_errstr(status)));
2523 centry_free(centry);
2525 *pnum_aliases = num_aliases;
2526 *paliases = aliases;
2528 return status;
2531 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2532 TALLOC_CTX *mem_ctx,
2533 uint32 num_sids, const struct dom_sid *sids,
2534 uint32 *num_aliases, uint32 **alias_rids)
2536 struct cache_entry *centry = NULL;
2537 NTSTATUS status;
2538 char *sidlist;
2539 int i;
2540 bool old_status;
2542 old_status = domain->online;
2543 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2544 num_aliases, alias_rids);
2545 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2546 return status;
2549 (*num_aliases) = 0;
2550 (*alias_rids) = NULL;
2552 if (!NT_STATUS_IS_OK(domain->last_status))
2553 return domain->last_status;
2555 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2556 "for domain %s\n", domain->name ));
2558 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2559 if (sidlist == NULL) {
2560 return NT_STATUS_NO_MEMORY;
2563 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2564 num_sids, sids,
2565 num_aliases, alias_rids);
2567 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2568 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2569 if (!domain->internal && old_status) {
2570 set_domain_offline(domain);
2572 if (!domain->internal &&
2573 !domain->online &&
2574 old_status) {
2575 NTSTATUS cache_status;
2576 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2577 sids, num_aliases, alias_rids);
2578 return cache_status;
2581 /* and save it */
2582 refresh_sequence_number(domain, false);
2583 if (!NT_STATUS_IS_OK(status)) {
2584 return status;
2586 centry = centry_start(domain, status);
2587 if (!centry)
2588 goto skip_save;
2589 centry_put_uint32(centry, *num_aliases);
2590 for (i=0; i<(*num_aliases); i++)
2591 centry_put_uint32(centry, (*alias_rids)[i]);
2592 centry_end(centry, "UA%s", sidlist);
2593 centry_free(centry);
2595 skip_save:
2596 return status;
2599 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2600 TALLOC_CTX *mem_ctx,
2601 const struct dom_sid *group_sid,
2602 uint32_t *num_names,
2603 struct dom_sid **sid_mem, char ***names,
2604 uint32_t **name_types)
2606 struct winbind_cache *cache = get_cache(domain);
2607 struct cache_entry *centry = NULL;
2608 NTSTATUS status;
2609 unsigned int i;
2610 char *sid_string;
2612 if (cache->tdb == NULL) {
2613 return NT_STATUS_NOT_FOUND;
2616 sid_string = sid_string_tos(group_sid);
2617 if (sid_string == NULL) {
2618 return NT_STATUS_NO_MEMORY;
2621 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2622 TALLOC_FREE(sid_string);
2623 if (centry == NULL) {
2624 return NT_STATUS_NOT_FOUND;
2627 *sid_mem = NULL;
2628 *names = NULL;
2629 *name_types = NULL;
2631 *num_names = centry_uint32(centry);
2632 if (*num_names == 0) {
2633 centry_free(centry);
2634 return NT_STATUS_OK;
2637 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2638 *names = talloc_array(mem_ctx, char *, *num_names);
2639 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2641 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2642 TALLOC_FREE(*sid_mem);
2643 TALLOC_FREE(*names);
2644 TALLOC_FREE(*name_types);
2645 centry_free(centry);
2646 return NT_STATUS_NO_MEMORY;
2649 for (i=0; i<(*num_names); i++) {
2650 centry_sid(centry, &(*sid_mem)[i]);
2651 (*names)[i] = centry_string(centry, mem_ctx);
2652 (*name_types)[i] = centry_uint32(centry);
2655 status = centry->status;
2657 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2658 "status: %s\n", domain->name, nt_errstr(status)));
2660 centry_free(centry);
2661 return status;
2664 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2665 TALLOC_CTX *mem_ctx,
2666 const struct dom_sid *group_sid,
2667 enum lsa_SidType type,
2668 uint32 *num_names,
2669 struct dom_sid **sid_mem, char ***names,
2670 uint32 **name_types)
2672 struct cache_entry *centry = NULL;
2673 NTSTATUS status;
2674 unsigned int i;
2675 fstring sid_string;
2676 bool old_status;
2678 old_status = domain->online;
2679 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2680 sid_mem, names, name_types);
2681 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2682 return status;
2685 (*num_names) = 0;
2686 (*sid_mem) = NULL;
2687 (*names) = NULL;
2688 (*name_types) = NULL;
2690 /* Return status value returned by seq number check */
2692 if (!NT_STATUS_IS_OK(domain->last_status))
2693 return domain->last_status;
2695 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2696 domain->name ));
2698 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2699 type, num_names,
2700 sid_mem, names, name_types);
2702 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2703 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2704 if (!domain->internal && old_status) {
2705 set_domain_offline(domain);
2707 if (!domain->internal &&
2708 !domain->online &&
2709 old_status) {
2710 NTSTATUS cache_status;
2711 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2712 num_names, sid_mem, names,
2713 name_types);
2714 return cache_status;
2717 /* and save it */
2718 refresh_sequence_number(domain, false);
2719 if (!NT_STATUS_IS_OK(status)) {
2720 return status;
2722 centry = centry_start(domain, status);
2723 if (!centry)
2724 goto skip_save;
2725 centry_put_uint32(centry, *num_names);
2726 for (i=0; i<(*num_names); i++) {
2727 centry_put_sid(centry, &(*sid_mem)[i]);
2728 centry_put_string(centry, (*names)[i]);
2729 centry_put_uint32(centry, (*name_types)[i]);
2731 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2732 centry_free(centry);
2734 skip_save:
2735 return status;
2738 /* find the sequence number for a domain */
2739 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2741 refresh_sequence_number(domain, false);
2743 *seq = domain->sequence_number;
2745 return NT_STATUS_OK;
2748 /* enumerate trusted domains
2749 * (we need to have the list of trustdoms in the cache when we go offline) -
2750 * Guenther */
2751 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2752 TALLOC_CTX *mem_ctx,
2753 struct netr_DomainTrustList *trusts)
2755 NTSTATUS status;
2756 struct winbind_cache *cache;
2757 struct winbindd_tdc_domain *dom_list = NULL;
2758 size_t num_domains = 0;
2759 bool retval = false;
2760 int i;
2761 bool old_status;
2763 old_status = domain->online;
2764 trusts->count = 0;
2765 trusts->array = NULL;
2767 cache = get_cache(domain);
2768 if (!cache || !cache->tdb) {
2769 goto do_query;
2772 if (domain->online) {
2773 goto do_query;
2776 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2777 if (!retval || !num_domains || !dom_list) {
2778 TALLOC_FREE(dom_list);
2779 goto do_query;
2782 do_fetch_cache:
2783 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2784 if (!trusts->array) {
2785 TALLOC_FREE(dom_list);
2786 return NT_STATUS_NO_MEMORY;
2789 for (i = 0; i < num_domains; i++) {
2790 struct netr_DomainTrust *trust;
2791 struct dom_sid *sid;
2792 struct winbindd_domain *dom;
2794 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2795 if (dom && dom->internal) {
2796 continue;
2799 trust = &trusts->array[trusts->count];
2800 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2801 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2802 sid = talloc(trusts->array, struct dom_sid);
2803 if (!trust->netbios_name || !trust->dns_name ||
2804 !sid) {
2805 TALLOC_FREE(dom_list);
2806 TALLOC_FREE(trusts->array);
2807 return NT_STATUS_NO_MEMORY;
2810 trust->trust_flags = dom_list[i].trust_flags;
2811 trust->trust_attributes = dom_list[i].trust_attribs;
2812 trust->trust_type = dom_list[i].trust_type;
2813 sid_copy(sid, &dom_list[i].sid);
2814 trust->sid = sid;
2815 trusts->count++;
2818 TALLOC_FREE(dom_list);
2819 return NT_STATUS_OK;
2821 do_query:
2822 /* Return status value returned by seq number check */
2824 if (!NT_STATUS_IS_OK(domain->last_status))
2825 return domain->last_status;
2827 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2828 domain->name ));
2830 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2832 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2833 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2834 if (!domain->internal && old_status) {
2835 set_domain_offline(domain);
2837 if (!domain->internal &&
2838 !domain->online &&
2839 old_status) {
2840 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2841 if (retval && num_domains && dom_list) {
2842 TALLOC_FREE(trusts->array);
2843 trusts->count = 0;
2844 goto do_fetch_cache;
2848 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2849 * so that the generic centry handling still applies correctly -
2850 * Guenther*/
2852 if (!NT_STATUS_IS_ERR(status)) {
2853 status = NT_STATUS_OK;
2855 return status;
2858 /* get lockout policy */
2859 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2860 TALLOC_CTX *mem_ctx,
2861 struct samr_DomInfo12 *policy)
2863 struct winbind_cache *cache = get_cache(domain);
2864 struct cache_entry *centry = NULL;
2865 NTSTATUS status;
2866 bool old_status;
2868 old_status = domain->online;
2869 if (!cache->tdb)
2870 goto do_query;
2872 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2874 if (!centry)
2875 goto do_query;
2877 do_fetch_cache:
2878 policy->lockout_duration = centry_nttime(centry);
2879 policy->lockout_window = centry_nttime(centry);
2880 policy->lockout_threshold = centry_uint16(centry);
2882 status = centry->status;
2884 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2885 domain->name, nt_errstr(status) ));
2887 centry_free(centry);
2888 return status;
2890 do_query:
2891 ZERO_STRUCTP(policy);
2893 /* Return status value returned by seq number check */
2895 if (!NT_STATUS_IS_OK(domain->last_status))
2896 return domain->last_status;
2898 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2899 domain->name ));
2901 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2903 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2904 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2905 if (!domain->internal && old_status) {
2906 set_domain_offline(domain);
2908 if (cache->tdb &&
2909 !domain->internal &&
2910 !domain->online &&
2911 old_status) {
2912 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2913 if (centry) {
2914 goto do_fetch_cache;
2918 /* and save it */
2919 refresh_sequence_number(domain, false);
2920 if (!NT_STATUS_IS_OK(status)) {
2921 return status;
2923 wcache_save_lockout_policy(domain, status, policy);
2925 return status;
2928 /* get password policy */
2929 static NTSTATUS password_policy(struct winbindd_domain *domain,
2930 TALLOC_CTX *mem_ctx,
2931 struct samr_DomInfo1 *policy)
2933 struct winbind_cache *cache = get_cache(domain);
2934 struct cache_entry *centry = NULL;
2935 NTSTATUS status;
2936 bool old_status;
2938 old_status = domain->online;
2939 if (!cache->tdb)
2940 goto do_query;
2942 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2944 if (!centry)
2945 goto do_query;
2947 do_fetch_cache:
2948 policy->min_password_length = centry_uint16(centry);
2949 policy->password_history_length = centry_uint16(centry);
2950 policy->password_properties = centry_uint32(centry);
2951 policy->max_password_age = centry_nttime(centry);
2952 policy->min_password_age = centry_nttime(centry);
2954 status = centry->status;
2956 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2957 domain->name, nt_errstr(status) ));
2959 centry_free(centry);
2960 return status;
2962 do_query:
2963 ZERO_STRUCTP(policy);
2965 /* Return status value returned by seq number check */
2967 if (!NT_STATUS_IS_OK(domain->last_status))
2968 return domain->last_status;
2970 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2971 domain->name ));
2973 status = domain->backend->password_policy(domain, mem_ctx, policy);
2975 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2976 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2977 if (!domain->internal && old_status) {
2978 set_domain_offline(domain);
2980 if (cache->tdb &&
2981 !domain->internal &&
2982 !domain->online &&
2983 old_status) {
2984 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2985 if (centry) {
2986 goto do_fetch_cache;
2990 /* and save it */
2991 refresh_sequence_number(domain, false);
2992 if (!NT_STATUS_IS_OK(status)) {
2993 return status;
2995 wcache_save_password_policy(domain, status, policy);
2997 return status;
3001 /* Invalidate cached user and group lists coherently */
3003 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3004 void *state)
3006 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3007 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3008 tdb_delete(the_tdb, kbuf);
3010 return 0;
3013 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3015 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3016 const struct dom_sid *sid)
3018 fstring key_str, sid_string;
3019 struct winbind_cache *cache;
3021 /* dont clear cached U/SID and UG/SID entries when we want to logon
3022 * offline - gd */
3024 if (lp_winbind_offline_logon()) {
3025 return;
3028 if (!domain)
3029 return;
3031 cache = get_cache(domain);
3033 if (!cache->tdb) {
3034 return;
3037 /* Clear U/SID cache entry */
3038 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3039 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3040 tdb_delete(cache->tdb, string_tdb_data(key_str));
3042 /* Clear UG/SID cache entry */
3043 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3044 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3045 tdb_delete(cache->tdb, string_tdb_data(key_str));
3047 /* Samba/winbindd never needs this. */
3048 netsamlogon_clear_cached_user(sid);
3051 bool wcache_invalidate_cache(void)
3053 struct winbindd_domain *domain;
3055 for (domain = domain_list(); domain; domain = domain->next) {
3056 struct winbind_cache *cache = get_cache(domain);
3058 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3059 "entries for %s\n", domain->name));
3060 if (cache) {
3061 if (cache->tdb) {
3062 tdb_traverse(cache->tdb, traverse_fn, NULL);
3063 } else {
3064 return false;
3068 return true;
3071 bool wcache_invalidate_cache_noinit(void)
3073 struct winbindd_domain *domain;
3075 for (domain = domain_list(); domain; domain = domain->next) {
3076 struct winbind_cache *cache;
3078 /* Skip uninitialized domains. */
3079 if (!domain->initialized && !domain->internal) {
3080 continue;
3083 cache = get_cache(domain);
3085 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3086 "entries for %s\n", domain->name));
3087 if (cache) {
3088 if (cache->tdb) {
3089 tdb_traverse(cache->tdb, traverse_fn, NULL);
3091 * Flushing cache has nothing to with domains.
3092 * return here if we successfully flushed once.
3093 * To avoid unnecessary traversing the cache.
3095 return true;
3096 } else {
3097 return false;
3101 return true;
3104 bool init_wcache(void)
3106 if (wcache == NULL) {
3107 wcache = SMB_XMALLOC_P(struct winbind_cache);
3108 ZERO_STRUCTP(wcache);
3111 if (wcache->tdb != NULL)
3112 return true;
3114 /* when working offline we must not clear the cache on restart */
3115 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3116 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3117 TDB_INCOMPATIBLE_HASH |
3118 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3119 O_RDWR|O_CREAT, 0600);
3121 if (wcache->tdb == NULL) {
3122 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3123 return false;
3126 return true;
3129 /************************************************************************
3130 This is called by the parent to initialize the cache file.
3131 We don't need sophisticated locking here as we know we're the
3132 only opener.
3133 ************************************************************************/
3135 bool initialize_winbindd_cache(void)
3137 bool cache_bad = true;
3138 uint32 vers;
3140 if (!init_wcache()) {
3141 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3142 return false;
3145 /* Check version number. */
3146 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3147 vers == WINBINDD_CACHE_VERSION) {
3148 cache_bad = false;
3151 if (cache_bad) {
3152 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3153 "and re-creating with version number %d\n",
3154 WINBINDD_CACHE_VERSION ));
3156 tdb_close(wcache->tdb);
3157 wcache->tdb = NULL;
3159 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3160 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3161 state_path("winbindd_cache.tdb"),
3162 strerror(errno) ));
3163 return false;
3165 if (!init_wcache()) {
3166 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3167 "init_wcache failed.\n"));
3168 return false;
3171 /* Write the version. */
3172 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3173 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3174 tdb_errorstr_compat(wcache->tdb) ));
3175 return false;
3179 tdb_close(wcache->tdb);
3180 wcache->tdb = NULL;
3181 return true;
3184 void close_winbindd_cache(void)
3186 if (!wcache) {
3187 return;
3189 if (wcache->tdb) {
3190 tdb_close(wcache->tdb);
3191 wcache->tdb = NULL;
3195 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3196 char **domain_name, char **name,
3197 enum lsa_SidType *type)
3199 struct winbindd_domain *domain;
3200 NTSTATUS status;
3202 domain = find_lookup_domain_from_sid(sid);
3203 if (domain == NULL) {
3204 return false;
3206 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3207 type);
3208 return NT_STATUS_IS_OK(status);
3211 bool lookup_cached_name(const char *domain_name,
3212 const char *name,
3213 struct dom_sid *sid,
3214 enum lsa_SidType *type)
3216 struct winbindd_domain *domain;
3217 NTSTATUS status;
3218 bool original_online_state;
3220 domain = find_lookup_domain_from_name(domain_name);
3221 if (domain == NULL) {
3222 return false;
3225 /* If we are doing a cached logon, temporarily set the domain
3226 offline so the cache won't expire the entry */
3228 original_online_state = domain->online;
3229 domain->online = false;
3230 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3231 domain->online = original_online_state;
3233 return NT_STATUS_IS_OK(status);
3236 void cache_name2sid(struct winbindd_domain *domain,
3237 const char *domain_name, const char *name,
3238 enum lsa_SidType type, const struct dom_sid *sid)
3240 refresh_sequence_number(domain, false);
3241 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3242 sid, type);
3246 * The original idea that this cache only contains centries has
3247 * been blurred - now other stuff gets put in here. Ensure we
3248 * ignore these things on cleanup.
3251 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3252 TDB_DATA dbuf, void *state)
3254 struct cache_entry *centry;
3256 if (is_non_centry_key(kbuf)) {
3257 return 0;
3260 centry = wcache_fetch_raw((char *)kbuf.dptr);
3261 if (!centry) {
3262 return 0;
3265 if (!NT_STATUS_IS_OK(centry->status)) {
3266 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3267 tdb_delete(the_tdb, kbuf);
3270 centry_free(centry);
3271 return 0;
3274 /* flush the cache */
3275 void wcache_flush_cache(void)
3277 if (!wcache)
3278 return;
3279 if (wcache->tdb) {
3280 tdb_close(wcache->tdb);
3281 wcache->tdb = NULL;
3283 if (!winbindd_use_cache()) {
3284 return;
3287 /* when working offline we must not clear the cache on restart */
3288 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3289 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3290 TDB_INCOMPATIBLE_HASH |
3291 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3292 O_RDWR|O_CREAT, 0600);
3294 if (!wcache->tdb) {
3295 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3296 return;
3299 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3301 DEBUG(10,("wcache_flush_cache success\n"));
3304 /* Count cached creds */
3306 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3307 void *state)
3309 int *cred_count = (int*)state;
3311 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3312 (*cred_count)++;
3314 return 0;
3317 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3319 struct winbind_cache *cache = get_cache(domain);
3321 *count = 0;
3323 if (!cache->tdb) {
3324 return NT_STATUS_INTERNAL_DB_ERROR;
3327 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3329 return NT_STATUS_OK;
3332 struct cred_list {
3333 struct cred_list *prev, *next;
3334 TDB_DATA key;
3335 fstring name;
3336 time_t created;
3338 static struct cred_list *wcache_cred_list;
3340 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3341 void *state)
3343 struct cred_list *cred;
3345 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3347 cred = SMB_MALLOC_P(struct cred_list);
3348 if (cred == NULL) {
3349 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3350 return -1;
3353 ZERO_STRUCTP(cred);
3355 /* save a copy of the key */
3357 fstrcpy(cred->name, (const char *)kbuf.dptr);
3358 DLIST_ADD(wcache_cred_list, cred);
3361 return 0;
3364 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3366 struct winbind_cache *cache = get_cache(domain);
3367 NTSTATUS status;
3368 int ret;
3369 struct cred_list *cred, *oldest = NULL;
3371 if (!cache->tdb) {
3372 return NT_STATUS_INTERNAL_DB_ERROR;
3375 /* we possibly already have an entry */
3376 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3378 fstring key_str, tmp;
3380 DEBUG(11,("we already have an entry, deleting that\n"));
3382 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3384 tdb_delete(cache->tdb, string_tdb_data(key_str));
3386 return NT_STATUS_OK;
3389 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3390 if (ret == 0) {
3391 return NT_STATUS_OK;
3392 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3393 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3396 ZERO_STRUCTP(oldest);
3398 for (cred = wcache_cred_list; cred; cred = cred->next) {
3400 TDB_DATA data;
3401 time_t t;
3403 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3404 if (!data.dptr) {
3405 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3406 cred->name));
3407 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3408 goto done;
3411 t = IVAL(data.dptr, 0);
3412 SAFE_FREE(data.dptr);
3414 if (!oldest) {
3415 oldest = SMB_MALLOC_P(struct cred_list);
3416 if (oldest == NULL) {
3417 status = NT_STATUS_NO_MEMORY;
3418 goto done;
3421 fstrcpy(oldest->name, cred->name);
3422 oldest->created = t;
3423 continue;
3426 if (t < oldest->created) {
3427 fstrcpy(oldest->name, cred->name);
3428 oldest->created = t;
3432 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3433 status = NT_STATUS_OK;
3434 } else {
3435 status = NT_STATUS_UNSUCCESSFUL;
3437 done:
3438 SAFE_FREE(wcache_cred_list);
3439 SAFE_FREE(oldest);
3441 return status;
3444 /* Change the global online/offline state. */
3445 bool set_global_winbindd_state_offline(void)
3447 TDB_DATA data;
3449 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3451 /* Only go offline if someone has created
3452 the key "WINBINDD_OFFLINE" in the cache tdb. */
3454 if (wcache == NULL || wcache->tdb == NULL) {
3455 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3456 return false;
3459 if (!lp_winbind_offline_logon()) {
3460 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3461 return false;
3464 if (global_winbindd_offline_state) {
3465 /* Already offline. */
3466 return true;
3469 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3471 if (!data.dptr || data.dsize != 4) {
3472 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3473 SAFE_FREE(data.dptr);
3474 return false;
3475 } else {
3476 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3477 global_winbindd_offline_state = true;
3478 SAFE_FREE(data.dptr);
3479 return true;
3483 void set_global_winbindd_state_online(void)
3485 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3487 if (!lp_winbind_offline_logon()) {
3488 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3489 return;
3492 if (!global_winbindd_offline_state) {
3493 /* Already online. */
3494 return;
3496 global_winbindd_offline_state = false;
3498 if (!wcache->tdb) {
3499 return;
3502 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3503 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3506 bool get_global_winbindd_state_offline(void)
3508 return global_winbindd_offline_state;
3511 /***********************************************************************
3512 Validate functions for all possible cache tdb keys.
3513 ***********************************************************************/
3515 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3516 struct tdb_validation_status *state)
3518 struct cache_entry *centry;
3520 centry = SMB_XMALLOC_P(struct cache_entry);
3521 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3522 if (!centry->data) {
3523 SAFE_FREE(centry);
3524 return NULL;
3526 centry->len = data.dsize;
3527 centry->ofs = 0;
3529 if (centry->len < 16) {
3530 /* huh? corrupt cache? */
3531 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3532 "(len < 16) ?\n", kstr));
3533 centry_free(centry);
3534 state->bad_entry = true;
3535 state->success = false;
3536 return NULL;
3539 centry->status = NT_STATUS(centry_uint32(centry));
3540 centry->sequence_number = centry_uint32(centry);
3541 centry->timeout = centry_uint64_t(centry);
3542 return centry;
3545 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3546 struct tdb_validation_status *state)
3548 if (dbuf.dsize != 8) {
3549 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3550 keystr, (unsigned int)dbuf.dsize ));
3551 state->bad_entry = true;
3552 return 1;
3554 return 0;
3557 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3558 struct tdb_validation_status *state)
3560 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3561 if (!centry) {
3562 return 1;
3565 (void)centry_uint32(centry);
3566 if (NT_STATUS_IS_OK(centry->status)) {
3567 struct dom_sid sid;
3568 (void)centry_sid(centry, &sid);
3571 centry_free(centry);
3573 if (!(state->success)) {
3574 return 1;
3576 DEBUG(10,("validate_ns: %s ok\n", keystr));
3577 return 0;
3580 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3581 struct tdb_validation_status *state)
3583 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3584 if (!centry) {
3585 return 1;
3588 if (NT_STATUS_IS_OK(centry->status)) {
3589 (void)centry_uint32(centry);
3590 (void)centry_string(centry, mem_ctx);
3591 (void)centry_string(centry, mem_ctx);
3594 centry_free(centry);
3596 if (!(state->success)) {
3597 return 1;
3599 DEBUG(10,("validate_sn: %s ok\n", keystr));
3600 return 0;
3603 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3604 struct tdb_validation_status *state)
3606 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3607 struct dom_sid sid;
3609 if (!centry) {
3610 return 1;
3613 (void)centry_string(centry, mem_ctx);
3614 (void)centry_string(centry, mem_ctx);
3615 (void)centry_string(centry, mem_ctx);
3616 (void)centry_string(centry, mem_ctx);
3617 (void)centry_uint32(centry);
3618 (void)centry_sid(centry, &sid);
3619 (void)centry_sid(centry, &sid);
3621 centry_free(centry);
3623 if (!(state->success)) {
3624 return 1;
3626 DEBUG(10,("validate_u: %s ok\n", keystr));
3627 return 0;
3630 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3631 struct tdb_validation_status *state)
3633 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3635 if (!centry) {
3636 return 1;
3639 (void)centry_nttime(centry);
3640 (void)centry_nttime(centry);
3641 (void)centry_uint16(centry);
3643 centry_free(centry);
3645 if (!(state->success)) {
3646 return 1;
3648 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3649 return 0;
3652 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3653 struct tdb_validation_status *state)
3655 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3657 if (!centry) {
3658 return 1;
3661 (void)centry_uint16(centry);
3662 (void)centry_uint16(centry);
3663 (void)centry_uint32(centry);
3664 (void)centry_nttime(centry);
3665 (void)centry_nttime(centry);
3667 centry_free(centry);
3669 if (!(state->success)) {
3670 return 1;
3672 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3673 return 0;
3676 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3677 struct tdb_validation_status *state)
3679 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3681 if (!centry) {
3682 return 1;
3685 (void)centry_time(centry);
3686 (void)centry_hash16(centry, mem_ctx);
3688 /* We only have 17 bytes more data in the salted cred case. */
3689 if (centry->len - centry->ofs == 17) {
3690 (void)centry_hash16(centry, mem_ctx);
3693 centry_free(centry);
3695 if (!(state->success)) {
3696 return 1;
3698 DEBUG(10,("validate_cred: %s ok\n", keystr));
3699 return 0;
3702 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3703 struct tdb_validation_status *state)
3705 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3706 int32 num_entries, i;
3708 if (!centry) {
3709 return 1;
3712 num_entries = (int32)centry_uint32(centry);
3714 for (i=0; i< num_entries; i++) {
3715 struct dom_sid sid;
3716 (void)centry_string(centry, mem_ctx);
3717 (void)centry_string(centry, mem_ctx);
3718 (void)centry_string(centry, mem_ctx);
3719 (void)centry_string(centry, mem_ctx);
3720 (void)centry_sid(centry, &sid);
3721 (void)centry_sid(centry, &sid);
3724 centry_free(centry);
3726 if (!(state->success)) {
3727 return 1;
3729 DEBUG(10,("validate_ul: %s ok\n", keystr));
3730 return 0;
3733 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3734 struct tdb_validation_status *state)
3736 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3737 int32 num_entries, i;
3739 if (!centry) {
3740 return 1;
3743 num_entries = centry_uint32(centry);
3745 for (i=0; i< num_entries; i++) {
3746 (void)centry_string(centry, mem_ctx);
3747 (void)centry_string(centry, mem_ctx);
3748 (void)centry_uint32(centry);
3751 centry_free(centry);
3753 if (!(state->success)) {
3754 return 1;
3756 DEBUG(10,("validate_gl: %s ok\n", keystr));
3757 return 0;
3760 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3761 struct tdb_validation_status *state)
3763 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3764 int32 num_groups, i;
3766 if (!centry) {
3767 return 1;
3770 num_groups = centry_uint32(centry);
3772 for (i=0; i< num_groups; i++) {
3773 struct dom_sid sid;
3774 centry_sid(centry, &sid);
3777 centry_free(centry);
3779 if (!(state->success)) {
3780 return 1;
3782 DEBUG(10,("validate_ug: %s ok\n", keystr));
3783 return 0;
3786 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3787 struct tdb_validation_status *state)
3789 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3790 int32 num_aliases, i;
3792 if (!centry) {
3793 return 1;
3796 num_aliases = centry_uint32(centry);
3798 for (i=0; i < num_aliases; i++) {
3799 (void)centry_uint32(centry);
3802 centry_free(centry);
3804 if (!(state->success)) {
3805 return 1;
3807 DEBUG(10,("validate_ua: %s ok\n", keystr));
3808 return 0;
3811 static int validate_gm(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_names, i;
3817 if (!centry) {
3818 return 1;
3821 num_names = centry_uint32(centry);
3823 for (i=0; i< num_names; i++) {
3824 struct dom_sid sid;
3825 centry_sid(centry, &sid);
3826 (void)centry_string(centry, mem_ctx);
3827 (void)centry_uint32(centry);
3830 centry_free(centry);
3832 if (!(state->success)) {
3833 return 1;
3835 DEBUG(10,("validate_gm: %s ok\n", keystr));
3836 return 0;
3839 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3840 struct tdb_validation_status *state)
3842 /* Can't say anything about this other than must be nonzero. */
3843 if (dbuf.dsize == 0) {
3844 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3845 keystr));
3846 state->bad_entry = true;
3847 state->success = false;
3848 return 1;
3851 DEBUG(10,("validate_dr: %s ok\n", keystr));
3852 return 0;
3855 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3856 struct tdb_validation_status *state)
3858 /* Can't say anything about this other than must be nonzero. */
3859 if (dbuf.dsize == 0) {
3860 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3861 keystr));
3862 state->bad_entry = true;
3863 state->success = false;
3864 return 1;
3867 DEBUG(10,("validate_de: %s ok\n", keystr));
3868 return 0;
3871 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3872 TDB_DATA dbuf, struct tdb_validation_status *state)
3874 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3876 if (!centry) {
3877 return 1;
3880 (void)centry_string(centry, mem_ctx);
3881 (void)centry_string(centry, mem_ctx);
3882 (void)centry_string(centry, mem_ctx);
3883 (void)centry_uint32(centry);
3885 centry_free(centry);
3887 if (!(state->success)) {
3888 return 1;
3890 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3891 return 0;
3894 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3895 TDB_DATA dbuf,
3896 struct tdb_validation_status *state)
3898 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3900 if (!centry) {
3901 return 1;
3904 (void)centry_string( centry, mem_ctx );
3906 centry_free(centry);
3908 if (!(state->success)) {
3909 return 1;
3911 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3912 return 0;
3915 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3916 TDB_DATA dbuf,
3917 struct tdb_validation_status *state)
3919 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3921 if (!centry) {
3922 return 1;
3925 (void)centry_string( centry, mem_ctx );
3927 centry_free(centry);
3929 if (!(state->success)) {
3930 return 1;
3932 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3933 return 0;
3936 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3937 TDB_DATA dbuf,
3938 struct tdb_validation_status *state)
3940 if (dbuf.dsize == 0) {
3941 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3942 "key %s (len ==0) ?\n", keystr));
3943 state->bad_entry = true;
3944 state->success = false;
3945 return 1;
3948 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3949 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3950 return 0;
3953 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3954 struct tdb_validation_status *state)
3956 if (dbuf.dsize != 4) {
3957 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3958 keystr, (unsigned int)dbuf.dsize ));
3959 state->bad_entry = true;
3960 state->success = false;
3961 return 1;
3963 DEBUG(10,("validate_offline: %s ok\n", keystr));
3964 return 0;
3967 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3968 struct tdb_validation_status *state)
3971 * Ignore validation for now. The proper way to do this is with a
3972 * checksum. Just pure parsing does not really catch much.
3974 return 0;
3977 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3978 struct tdb_validation_status *state)
3980 if (dbuf.dsize != 4) {
3981 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3982 "key %s (len %u != 4) ?\n",
3983 keystr, (unsigned int)dbuf.dsize));
3984 state->bad_entry = true;
3985 state->success = false;
3986 return 1;
3989 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3990 return 0;
3993 /***********************************************************************
3994 A list of all possible cache tdb keys with associated validation
3995 functions.
3996 ***********************************************************************/
3998 struct key_val_struct {
3999 const char *keyname;
4000 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4001 } key_val[] = {
4002 {"SEQNUM/", validate_seqnum},
4003 {"NS/", validate_ns},
4004 {"SN/", validate_sn},
4005 {"U/", validate_u},
4006 {"LOC_POL/", validate_loc_pol},
4007 {"PWD_POL/", validate_pwd_pol},
4008 {"CRED/", validate_cred},
4009 {"UL/", validate_ul},
4010 {"GL/", validate_gl},
4011 {"UG/", validate_ug},
4012 {"UA", validate_ua},
4013 {"GM/", validate_gm},
4014 {"DR/", validate_dr},
4015 {"DE/", validate_de},
4016 {"NSS/PWINFO/", validate_pwinfo},
4017 {"TRUSTDOMCACHE/", validate_trustdomcache},
4018 {"NSS/NA/", validate_nss_na},
4019 {"NSS/AN/", validate_nss_an},
4020 {"WINBINDD_OFFLINE", validate_offline},
4021 {"NDR/", validate_ndr},
4022 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4023 {NULL, NULL}
4026 /***********************************************************************
4027 Function to look at every entry in the tdb and validate it as far as
4028 possible.
4029 ***********************************************************************/
4031 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4033 int i;
4034 unsigned int max_key_len = 1024;
4035 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4037 /* Paranoia check. */
4038 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4039 max_key_len = 1024 * 1024;
4041 if (kbuf.dsize > max_key_len) {
4042 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4043 "(%u) > (%u)\n\n",
4044 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4045 return 1;
4048 for (i = 0; key_val[i].keyname; i++) {
4049 size_t namelen = strlen(key_val[i].keyname);
4050 if (kbuf.dsize >= namelen && (
4051 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4052 TALLOC_CTX *mem_ctx;
4053 char *keystr;
4054 int ret;
4056 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4057 if (!keystr) {
4058 return 1;
4060 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4061 keystr[kbuf.dsize] = '\0';
4063 mem_ctx = talloc_init("validate_ctx");
4064 if (!mem_ctx) {
4065 SAFE_FREE(keystr);
4066 return 1;
4069 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4070 v_state);
4072 SAFE_FREE(keystr);
4073 talloc_destroy(mem_ctx);
4074 return ret;
4078 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4079 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4080 DEBUG(0,("data :\n"));
4081 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4082 v_state->unknown_key = true;
4083 v_state->success = false;
4084 return 1; /* terminate. */
4087 static void validate_panic(const char *const why)
4089 DEBUG(0,("validating cache: would panic %s\n", why ));
4090 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4091 exit(47);
4094 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4095 TDB_DATA key,
4096 TDB_DATA data,
4097 void *state)
4099 uint64_t ctimeout;
4100 TDB_DATA blob;
4102 if (is_non_centry_key(key)) {
4103 return 0;
4106 if (data.dptr == NULL || data.dsize == 0) {
4107 if (tdb_delete(tdb, key) < 0) {
4108 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4109 key.dptr));
4110 return 1;
4114 /* add timeout to blob (uint64_t) */
4115 blob.dsize = data.dsize + 8;
4117 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4118 if (blob.dptr == NULL) {
4119 return 1;
4121 memset(blob.dptr, 0, blob.dsize);
4123 /* copy status and seqnum */
4124 memcpy(blob.dptr, data.dptr, 8);
4126 /* add timeout */
4127 ctimeout = lp_winbind_cache_time() + time(NULL);
4128 SBVAL(blob.dptr, 8, ctimeout);
4130 /* copy the rest */
4131 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4133 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4134 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4135 key.dptr));
4136 SAFE_FREE(blob.dptr);
4137 return 1;
4140 SAFE_FREE(blob.dptr);
4141 return 0;
4144 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4146 int rc;
4148 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4150 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4151 if (rc < 0) {
4152 return false;
4155 return true;
4158 /***********************************************************************
4159 Try and validate every entry in the winbindd cache. If we fail here,
4160 delete the cache tdb and return non-zero.
4161 ***********************************************************************/
4163 int winbindd_validate_cache(void)
4165 int ret = -1;
4166 const char *tdb_path = state_path("winbindd_cache.tdb");
4167 TDB_CONTEXT *tdb = NULL;
4168 uint32_t vers_id;
4169 bool ok;
4171 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4172 smb_panic_fn = validate_panic;
4174 tdb = tdb_open_log(tdb_path,
4175 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4176 TDB_INCOMPATIBLE_HASH |
4177 ( lp_winbind_offline_logon()
4178 ? TDB_DEFAULT
4179 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4180 O_RDWR|O_CREAT,
4181 0600);
4182 if (!tdb) {
4183 DEBUG(0, ("winbindd_validate_cache: "
4184 "error opening/initializing tdb\n"));
4185 goto done;
4188 /* Version check and upgrade code. */
4189 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4190 DEBUG(10, ("Fresh database\n"));
4191 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4192 vers_id = WINBINDD_CACHE_VERSION;
4195 if (vers_id != WINBINDD_CACHE_VERSION) {
4196 if (vers_id == WINBINDD_CACHE_VER1) {
4197 ok = wbcache_upgrade_v1_to_v2(tdb);
4198 if (!ok) {
4199 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4200 unlink(tdb_path);
4201 goto done;
4204 tdb_store_uint32(tdb,
4205 WINBINDD_CACHE_VERSION_KEYSTR,
4206 WINBINDD_CACHE_VERSION);
4207 vers_id = WINBINDD_CACHE_VER2;
4211 tdb_close(tdb);
4213 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4215 if (ret != 0) {
4216 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4217 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4218 unlink(tdb_path);
4221 done:
4222 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4223 smb_panic_fn = smb_panic;
4224 return ret;
4227 /***********************************************************************
4228 Try and validate every entry in the winbindd cache.
4229 ***********************************************************************/
4231 int winbindd_validate_cache_nobackup(void)
4233 int ret = -1;
4234 const char *tdb_path = state_path("winbindd_cache.tdb");
4236 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4237 smb_panic_fn = validate_panic;
4240 if (wcache == NULL || wcache->tdb == NULL) {
4241 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4242 } else {
4243 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4246 if (ret != 0) {
4247 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4248 "successful.\n"));
4251 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4252 "function\n"));
4253 smb_panic_fn = smb_panic;
4254 return ret;
4257 bool winbindd_cache_validate_and_initialize(void)
4259 close_winbindd_cache();
4261 if (lp_winbind_offline_logon()) {
4262 if (winbindd_validate_cache() < 0) {
4263 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4264 "could be restored.\n"));
4268 return initialize_winbindd_cache();
4271 /*********************************************************************
4272 ********************************************************************/
4274 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4275 struct winbindd_tdc_domain **domains,
4276 size_t *num_domains )
4278 struct winbindd_tdc_domain *list = NULL;
4279 size_t idx;
4280 int i;
4281 bool set_only = false;
4283 /* don't allow duplicates */
4285 idx = *num_domains;
4286 list = *domains;
4288 for ( i=0; i< (*num_domains); i++ ) {
4289 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4290 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4291 new_dom->name));
4292 idx = i;
4293 set_only = true;
4295 break;
4299 if ( !set_only ) {
4300 if ( !*domains ) {
4301 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4302 idx = 0;
4303 } else {
4304 list = talloc_realloc( *domains, *domains,
4305 struct winbindd_tdc_domain,
4306 (*num_domains)+1);
4307 idx = *num_domains;
4310 ZERO_STRUCT( list[idx] );
4313 if ( !list )
4314 return false;
4316 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4317 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4319 if ( !is_null_sid( &new_dom->sid ) ) {
4320 sid_copy( &list[idx].sid, &new_dom->sid );
4321 } else {
4322 sid_copy(&list[idx].sid, &global_sid_NULL);
4325 if ( new_dom->domain_flags != 0x0 )
4326 list[idx].trust_flags = new_dom->domain_flags;
4328 if ( new_dom->domain_type != 0x0 )
4329 list[idx].trust_type = new_dom->domain_type;
4331 if ( new_dom->domain_trust_attribs != 0x0 )
4332 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4334 if ( !set_only ) {
4335 *domains = list;
4336 *num_domains = idx + 1;
4339 return true;
4342 /*********************************************************************
4343 ********************************************************************/
4345 static TDB_DATA make_tdc_key( const char *domain_name )
4347 char *keystr = NULL;
4348 TDB_DATA key = { NULL, 0 };
4350 if ( !domain_name ) {
4351 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4352 return key;
4355 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4356 return key;
4358 key = string_term_tdb_data(keystr);
4360 return key;
4363 /*********************************************************************
4364 ********************************************************************/
4366 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4367 size_t num_domains,
4368 unsigned char **buf )
4370 unsigned char *buffer = NULL;
4371 int len = 0;
4372 int buflen = 0;
4373 int i = 0;
4375 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4376 (int)num_domains));
4378 buflen = 0;
4380 again:
4381 len = 0;
4383 /* Store the number of array items first */
4384 len += tdb_pack( buffer+len, buflen-len, "d",
4385 num_domains );
4387 /* now pack each domain trust record */
4388 for ( i=0; i<num_domains; i++ ) {
4390 fstring tmp;
4392 if ( buflen > 0 ) {
4393 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4394 domains[i].domain_name,
4395 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4398 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4399 domains[i].domain_name,
4400 domains[i].dns_name,
4401 sid_to_fstring(tmp, &domains[i].sid),
4402 domains[i].trust_flags,
4403 domains[i].trust_attribs,
4404 domains[i].trust_type );
4407 if ( buflen < len ) {
4408 SAFE_FREE(buffer);
4409 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4410 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4411 buflen = -1;
4412 goto done;
4414 buflen = len;
4415 goto again;
4418 *buf = buffer;
4420 done:
4421 return buflen;
4424 /*********************************************************************
4425 ********************************************************************/
4427 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4428 struct winbindd_tdc_domain **domains )
4430 fstring domain_name, dns_name, sid_string;
4431 uint32 type, attribs, flags;
4432 int num_domains;
4433 int len = 0;
4434 int i;
4435 struct winbindd_tdc_domain *list = NULL;
4437 /* get the number of domains */
4438 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4439 if ( len == -1 ) {
4440 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4441 return 0;
4444 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4445 if ( !list ) {
4446 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4447 return 0;
4450 for ( i=0; i<num_domains; i++ ) {
4451 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4452 domain_name,
4453 dns_name,
4454 sid_string,
4455 &flags,
4456 &attribs,
4457 &type );
4459 if ( len == -1 ) {
4460 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4461 TALLOC_FREE( list );
4462 return 0;
4465 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4466 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4467 domain_name, dns_name, sid_string,
4468 flags, attribs, type));
4470 list[i].domain_name = talloc_strdup( list, domain_name );
4471 list[i].dns_name = talloc_strdup( list, dns_name );
4472 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4473 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4474 domain_name));
4476 list[i].trust_flags = flags;
4477 list[i].trust_attribs = attribs;
4478 list[i].trust_type = type;
4481 *domains = list;
4483 return num_domains;
4486 /*********************************************************************
4487 ********************************************************************/
4489 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4491 TDB_DATA key = make_tdc_key( lp_workgroup() );
4492 TDB_DATA data = { NULL, 0 };
4493 int ret;
4495 if ( !key.dptr )
4496 return false;
4498 /* See if we were asked to delete the cache entry */
4500 if ( !domains ) {
4501 ret = tdb_delete( wcache->tdb, key );
4502 goto done;
4505 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4507 if ( !data.dptr ) {
4508 ret = -1;
4509 goto done;
4512 ret = tdb_store( wcache->tdb, key, data, 0 );
4514 done:
4515 SAFE_FREE( data.dptr );
4516 SAFE_FREE( key.dptr );
4518 return ( ret == 0 );
4521 /*********************************************************************
4522 ********************************************************************/
4524 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4526 TDB_DATA key = make_tdc_key( lp_workgroup() );
4527 TDB_DATA data = { NULL, 0 };
4529 *domains = NULL;
4530 *num_domains = 0;
4532 if ( !key.dptr )
4533 return false;
4535 data = tdb_fetch_compat( wcache->tdb, key );
4537 SAFE_FREE( key.dptr );
4539 if ( !data.dptr )
4540 return false;
4542 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4544 SAFE_FREE( data.dptr );
4546 if ( !*domains )
4547 return false;
4549 return true;
4552 /*********************************************************************
4553 ********************************************************************/
4555 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4557 struct winbindd_tdc_domain *dom_list = NULL;
4558 size_t num_domains = 0;
4559 bool ret = false;
4561 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4562 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4563 domain->name, domain->alt_name,
4564 sid_string_dbg(&domain->sid),
4565 domain->domain_flags,
4566 domain->domain_trust_attribs,
4567 domain->domain_type));
4569 if ( !init_wcache() ) {
4570 return false;
4573 /* fetch the list */
4575 wcache_tdc_fetch_list( &dom_list, &num_domains );
4577 /* add the new domain */
4579 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4580 goto done;
4583 /* pack the domain */
4585 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4586 goto done;
4589 /* Success */
4591 ret = true;
4592 done:
4593 TALLOC_FREE( dom_list );
4595 return ret;
4598 /*********************************************************************
4599 ********************************************************************/
4601 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4603 struct winbindd_tdc_domain *dom_list = NULL;
4604 size_t num_domains = 0;
4605 int i;
4606 struct winbindd_tdc_domain *d = NULL;
4608 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4610 if ( !init_wcache() ) {
4611 return NULL;
4614 /* fetch the list */
4616 wcache_tdc_fetch_list( &dom_list, &num_domains );
4618 for ( i=0; i<num_domains; i++ ) {
4619 if ( strequal(name, dom_list[i].domain_name) ||
4620 strequal(name, dom_list[i].dns_name) )
4622 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4623 name));
4625 d = talloc( ctx, struct winbindd_tdc_domain );
4626 if ( !d )
4627 break;
4629 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4630 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4631 sid_copy( &d->sid, &dom_list[i].sid );
4632 d->trust_flags = dom_list[i].trust_flags;
4633 d->trust_type = dom_list[i].trust_type;
4634 d->trust_attribs = dom_list[i].trust_attribs;
4636 break;
4640 TALLOC_FREE( dom_list );
4642 return d;
4645 /*********************************************************************
4646 ********************************************************************/
4648 struct winbindd_tdc_domain*
4649 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4650 const struct dom_sid *sid)
4652 struct winbindd_tdc_domain *dom_list = NULL;
4653 size_t num_domains = 0;
4654 int i;
4655 struct winbindd_tdc_domain *d = NULL;
4657 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4658 sid_string_dbg(sid)));
4660 if (!init_wcache()) {
4661 return NULL;
4664 /* fetch the list */
4666 wcache_tdc_fetch_list(&dom_list, &num_domains);
4668 for (i = 0; i<num_domains; i++) {
4669 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4670 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4671 "Found domain %s for SID %s\n",
4672 dom_list[i].domain_name,
4673 sid_string_dbg(sid)));
4675 d = talloc(ctx, struct winbindd_tdc_domain);
4676 if (!d)
4677 break;
4679 d->domain_name = talloc_strdup(d,
4680 dom_list[i].domain_name);
4682 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4683 sid_copy(&d->sid, &dom_list[i].sid);
4684 d->trust_flags = dom_list[i].trust_flags;
4685 d->trust_type = dom_list[i].trust_type;
4686 d->trust_attribs = dom_list[i].trust_attribs;
4688 break;
4692 TALLOC_FREE(dom_list);
4694 return d;
4698 /*********************************************************************
4699 ********************************************************************/
4701 void wcache_tdc_clear( void )
4703 if ( !init_wcache() )
4704 return;
4706 wcache_tdc_store_list( NULL, 0 );
4708 return;
4712 /*********************************************************************
4713 ********************************************************************/
4715 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4716 NTSTATUS status,
4717 const struct dom_sid *user_sid,
4718 const char *homedir,
4719 const char *shell,
4720 const char *gecos,
4721 uint32 gid)
4723 struct cache_entry *centry;
4724 fstring tmp;
4726 if ( (centry = centry_start(domain, status)) == NULL )
4727 return;
4729 centry_put_string( centry, homedir );
4730 centry_put_string( centry, shell );
4731 centry_put_string( centry, gecos );
4732 centry_put_uint32( centry, gid );
4734 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4736 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4738 centry_free(centry);
4741 #ifdef HAVE_ADS
4743 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4744 const struct dom_sid *user_sid,
4745 TALLOC_CTX *ctx,
4746 const char **homedir, const char **shell,
4747 const char **gecos, gid_t *p_gid)
4749 struct winbind_cache *cache = get_cache(domain);
4750 struct cache_entry *centry = NULL;
4751 NTSTATUS nt_status;
4752 fstring tmp;
4754 if (!cache->tdb)
4755 goto do_query;
4757 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4758 sid_to_fstring(tmp, user_sid));
4760 if (!centry)
4761 goto do_query;
4763 *homedir = centry_string( centry, ctx );
4764 *shell = centry_string( centry, ctx );
4765 *gecos = centry_string( centry, ctx );
4766 *p_gid = centry_uint32( centry );
4768 centry_free(centry);
4770 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4771 sid_string_dbg(user_sid)));
4773 return NT_STATUS_OK;
4775 do_query:
4777 nt_status = nss_get_info( domain->name, user_sid, ctx,
4778 homedir, shell, gecos, p_gid );
4780 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4782 if ( NT_STATUS_IS_OK(nt_status) ) {
4783 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4784 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4785 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4786 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4788 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4789 *homedir, *shell, *gecos, *p_gid );
4792 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4793 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4794 domain->name ));
4795 set_domain_offline( domain );
4798 return nt_status;
4801 #endif
4803 /* the cache backend methods are exposed via this structure */
4804 struct winbindd_methods cache_methods = {
4805 true,
4806 query_user_list,
4807 enum_dom_groups,
4808 enum_local_groups,
4809 name_to_sid,
4810 sid_to_name,
4811 rids_to_names,
4812 query_user,
4813 lookup_usergroups,
4814 lookup_useraliases,
4815 lookup_groupmem,
4816 sequence_number,
4817 lockout_policy,
4818 password_policy,
4819 trusted_domains
4822 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4823 uint32_t opnum, const DATA_BLOB *req,
4824 TDB_DATA *pkey)
4826 char *key;
4827 size_t keylen;
4829 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4830 if (key == NULL) {
4831 return false;
4833 keylen = talloc_get_size(key) - 1;
4835 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4836 if (key == NULL) {
4837 return false;
4839 memcpy(key + keylen, req->data, req->length);
4841 pkey->dptr = (uint8_t *)key;
4842 pkey->dsize = talloc_get_size(key);
4843 return true;
4846 static bool wcache_opnum_cacheable(uint32_t opnum)
4848 switch (opnum) {
4849 case NDR_WBINT_PING:
4850 case NDR_WBINT_QUERYSEQUENCENUMBER:
4851 case NDR_WBINT_ALLOCATEUID:
4852 case NDR_WBINT_ALLOCATEGID:
4853 case NDR_WBINT_CHECKMACHINEACCOUNT:
4854 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4855 case NDR_WBINT_PINGDC:
4856 return false;
4858 return true;
4861 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4862 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4864 TDB_DATA key, data;
4865 bool ret = false;
4867 if (!wcache_opnum_cacheable(opnum) ||
4868 is_my_own_sam_domain(domain) ||
4869 is_builtin_domain(domain)) {
4870 return false;
4873 if (wcache->tdb == NULL) {
4874 return false;
4877 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4878 return false;
4880 data = tdb_fetch_compat(wcache->tdb, key);
4881 TALLOC_FREE(key.dptr);
4883 if (data.dptr == NULL) {
4884 return false;
4886 if (data.dsize < 12) {
4887 goto fail;
4890 if (!is_domain_offline(domain)) {
4891 uint32_t entry_seqnum, dom_seqnum, last_check;
4892 uint64_t entry_timeout;
4894 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4895 &last_check)) {
4896 goto fail;
4898 entry_seqnum = IVAL(data.dptr, 0);
4899 if (entry_seqnum != dom_seqnum) {
4900 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4901 (int)entry_seqnum));
4902 goto fail;
4904 entry_timeout = BVAL(data.dptr, 4);
4905 if (time(NULL) > entry_timeout) {
4906 DEBUG(10, ("Entry has timed out\n"));
4907 goto fail;
4911 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4912 data.dsize - 12);
4913 if (resp->data == NULL) {
4914 DEBUG(10, ("talloc failed\n"));
4915 goto fail;
4917 resp->length = data.dsize - 12;
4919 ret = true;
4920 fail:
4921 SAFE_FREE(data.dptr);
4922 return ret;
4925 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4926 const DATA_BLOB *req, const DATA_BLOB *resp)
4928 TDB_DATA key, data;
4929 uint32_t dom_seqnum, last_check;
4930 uint64_t timeout;
4932 if (!wcache_opnum_cacheable(opnum) ||
4933 is_my_own_sam_domain(domain) ||
4934 is_builtin_domain(domain)) {
4935 return;
4938 if (wcache->tdb == NULL) {
4939 return;
4942 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4943 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4944 domain->name));
4945 return;
4948 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4949 return;
4952 timeout = time(NULL) + lp_winbind_cache_time();
4954 data.dsize = resp->length + 12;
4955 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4956 if (data.dptr == NULL) {
4957 goto done;
4960 SIVAL(data.dptr, 0, dom_seqnum);
4961 SBVAL(data.dptr, 4, timeout);
4962 memcpy(data.dptr + 12, resp->data, resp->length);
4964 tdb_store(wcache->tdb, key, data, 0);
4966 done:
4967 TALLOC_FREE(key.dptr);
4968 return;