s3: Fix a winbind race leading to 100% CPU
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob4f902b5291a6b62d26f2d39f4500b5b57b979896
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_VERSION 2
42 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
44 extern struct winbindd_methods reconnect_methods;
45 #ifdef HAVE_ADS
46 extern struct winbindd_methods ads_methods;
47 #endif
48 extern struct winbindd_methods builtin_passdb_methods;
49 extern struct winbindd_methods sam_passdb_methods;
52 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
53 * Here are the list of entry types that are *not* stored
54 * as form struct cache_entry in the cache.
57 static const char *non_centry_keys[] = {
58 "SEQNUM/",
59 "DR/",
60 "DE/",
61 "WINBINDD_OFFLINE",
62 WINBINDD_CACHE_VERSION_KEYSTR,
63 NULL
66 /************************************************************************
67 Is this key a non-centry type ?
68 ************************************************************************/
70 static bool is_non_centry_key(TDB_DATA kbuf)
72 int i;
74 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
75 return false;
77 for (i = 0; non_centry_keys[i] != NULL; i++) {
78 size_t namelen = strlen(non_centry_keys[i]);
79 if (kbuf.dsize < namelen) {
80 continue;
82 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
83 return true;
86 return false;
89 /* Global online/offline state - False when online. winbindd starts up online
90 and sets this to true if the first query fails and there's an entry in
91 the cache tdb telling us to stay offline. */
93 static bool global_winbindd_offline_state;
95 struct winbind_cache {
96 TDB_CONTEXT *tdb;
99 struct cache_entry {
100 NTSTATUS status;
101 uint32 sequence_number;
102 uint64_t timeout;
103 uint8 *data;
104 uint32 len, ofs;
107 void (*smb_panic_fn)(const char *const why) = smb_panic;
109 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
111 static struct winbind_cache *wcache;
113 /* get the winbind_cache structure */
114 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
116 struct winbind_cache *ret = wcache;
118 /* We have to know what type of domain we are dealing with first. */
120 if (domain->internal) {
121 domain->backend = &builtin_passdb_methods;
122 domain->initialized = True;
125 if (strequal(domain->name, get_global_sam_name()) &&
126 sid_check_is_domain(&domain->sid)) {
127 domain->backend = &sam_passdb_methods;
128 domain->initialized = True;
131 if ( !domain->initialized ) {
132 init_dc_connection( domain );
136 OK. listen up becasue I'm only going to say this once.
137 We have the following scenarios to consider
138 (a) trusted AD domains on a Samba DC,
139 (b) trusted AD domains and we are joined to a non-kerberos domain
140 (c) trusted AD domains and we are joined to a kerberos (AD) domain
142 For (a) we can always contact the trusted domain using krb5
143 since we have the domain trust account password
145 For (b) we can only use RPC since we have no way of
146 getting a krb5 ticket in our own domain
148 For (c) we can always use krb5 since we have a kerberos trust
150 --jerry
153 if (!domain->backend) {
154 #ifdef HAVE_ADS
155 struct winbindd_domain *our_domain = domain;
157 /* find our domain first so we can figure out if we
158 are joined to a kerberized domain */
160 if ( !domain->primary )
161 our_domain = find_our_domain();
163 if ((our_domain->active_directory || IS_DC)
164 && domain->active_directory
165 && !lp_winbind_rpc_only()) {
166 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
167 domain->backend = &ads_methods;
168 } else {
169 #endif /* HAVE_ADS */
170 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
171 domain->backend = &reconnect_methods;
172 #ifdef HAVE_ADS
174 #endif /* HAVE_ADS */
177 if (ret)
178 return ret;
180 ret = SMB_XMALLOC_P(struct winbind_cache);
181 ZERO_STRUCTP(ret);
183 wcache = ret;
184 wcache_flush_cache();
186 return ret;
190 free a centry structure
192 static void centry_free(struct cache_entry *centry)
194 if (!centry)
195 return;
196 SAFE_FREE(centry->data);
197 free(centry);
200 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
202 if (centry->len - centry->ofs < nbytes) {
203 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
204 (unsigned int)nbytes,
205 centry->len - centry->ofs));
206 return false;
208 return true;
212 pull a uint64_t from a cache entry
214 static uint64_t centry_uint64_t(struct cache_entry *centry)
216 uint64_t ret;
218 if (!centry_check_bytes(centry, 8)) {
219 smb_panic_fn("centry_uint64_t");
221 ret = BVAL(centry->data, centry->ofs);
222 centry->ofs += 8;
223 return ret;
227 pull a uint32 from a cache entry
229 static uint32 centry_uint32(struct cache_entry *centry)
231 uint32 ret;
233 if (!centry_check_bytes(centry, 4)) {
234 smb_panic_fn("centry_uint32");
236 ret = IVAL(centry->data, centry->ofs);
237 centry->ofs += 4;
238 return ret;
242 pull a uint16 from a cache entry
244 static uint16 centry_uint16(struct cache_entry *centry)
246 uint16 ret;
247 if (!centry_check_bytes(centry, 2)) {
248 smb_panic_fn("centry_uint16");
250 ret = SVAL(centry->data, centry->ofs);
251 centry->ofs += 2;
252 return ret;
256 pull a uint8 from a cache entry
258 static uint8 centry_uint8(struct cache_entry *centry)
260 uint8 ret;
261 if (!centry_check_bytes(centry, 1)) {
262 smb_panic_fn("centry_uint8");
264 ret = CVAL(centry->data, centry->ofs);
265 centry->ofs += 1;
266 return ret;
270 pull a NTTIME from a cache entry
272 static NTTIME centry_nttime(struct cache_entry *centry)
274 NTTIME ret;
275 if (!centry_check_bytes(centry, 8)) {
276 smb_panic_fn("centry_nttime");
278 ret = IVAL(centry->data, centry->ofs);
279 centry->ofs += 4;
280 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
281 centry->ofs += 4;
282 return ret;
286 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
288 static time_t centry_time(struct cache_entry *centry)
290 return (time_t)centry_nttime(centry);
293 /* pull a string from a cache entry, using the supplied
294 talloc context
296 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
298 uint32 len;
299 char *ret;
301 len = centry_uint8(centry);
303 if (len == 0xFF) {
304 /* a deliberate NULL string */
305 return NULL;
308 if (!centry_check_bytes(centry, (size_t)len)) {
309 smb_panic_fn("centry_string");
312 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
313 if (!ret) {
314 smb_panic_fn("centry_string out of memory\n");
316 memcpy(ret,centry->data + centry->ofs, len);
317 ret[len] = 0;
318 centry->ofs += len;
319 return ret;
322 /* pull a hash16 from a cache entry, using the supplied
323 talloc context
325 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
327 uint32 len;
328 char *ret;
330 len = centry_uint8(centry);
332 if (len != 16) {
333 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
334 len ));
335 return NULL;
338 if (!centry_check_bytes(centry, 16)) {
339 return NULL;
342 ret = TALLOC_ARRAY(mem_ctx, char, 16);
343 if (!ret) {
344 smb_panic_fn("centry_hash out of memory\n");
346 memcpy(ret,centry->data + centry->ofs, 16);
347 centry->ofs += 16;
348 return ret;
351 /* pull a sid from a cache entry, using the supplied
352 talloc context
354 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
356 char *sid_string;
357 bool ret;
359 sid_string = centry_string(centry, talloc_tos());
360 if (sid_string == NULL) {
361 return false;
363 ret = string_to_sid(sid, sid_string);
364 TALLOC_FREE(sid_string);
365 return ret;
370 pull a NTSTATUS from a cache entry
372 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
374 NTSTATUS status;
376 status = NT_STATUS(centry_uint32(centry));
377 return status;
381 /* the server is considered down if it can't give us a sequence number */
382 static bool wcache_server_down(struct winbindd_domain *domain)
384 bool ret;
386 if (!wcache->tdb)
387 return false;
389 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
391 if (ret)
392 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
393 domain->name ));
394 return ret;
397 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
398 uint32_t *last_seq_check)
400 char *key;
401 TDB_DATA data;
403 if (wcache->tdb == NULL) {
404 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
405 return false;
408 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
409 if (key == NULL) {
410 DEBUG(10, ("talloc failed\n"));
411 return false;
414 data = tdb_fetch_bystring(wcache->tdb, key);
415 TALLOC_FREE(key);
417 if (data.dptr == NULL) {
418 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
419 domain_name));
420 return false;
422 if (data.dsize != 8) {
423 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
424 (int)data.dsize));
425 SAFE_FREE(data.dptr);
426 return false;
429 *seqnum = IVAL(data.dptr, 0);
430 *last_seq_check = IVAL(data.dptr, 4);
431 SAFE_FREE(data.dptr);
433 return true;
436 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
438 uint32 last_check, time_diff;
440 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
441 &last_check)) {
442 return NT_STATUS_UNSUCCESSFUL;
444 domain->last_seq_check = last_check;
446 /* have we expired? */
448 time_diff = now - domain->last_seq_check;
449 if ( time_diff > lp_winbind_cache_time() ) {
450 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
451 domain->name, domain->sequence_number,
452 (uint32)domain->last_seq_check));
453 return NT_STATUS_UNSUCCESSFUL;
456 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
457 domain->name, domain->sequence_number,
458 (uint32)domain->last_seq_check));
460 return NT_STATUS_OK;
463 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
464 time_t last_seq_check)
466 char *key_str;
467 uint8_t buf[8];
468 int ret;
470 if (wcache->tdb == NULL) {
471 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
472 return false;
475 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
476 if (key_str == NULL) {
477 DEBUG(10, ("talloc_asprintf failed\n"));
478 return false;
481 SIVAL(buf, 0, seqnum);
482 SIVAL(buf, 4, last_seq_check);
484 ret = tdb_store_bystring(wcache->tdb, key_str,
485 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
486 TALLOC_FREE(key_str);
487 if (ret == -1) {
488 DEBUG(10, ("tdb_store_bystring failed: %s\n",
489 tdb_errorstr(wcache->tdb)));
490 TALLOC_FREE(key_str);
491 return false;
494 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
495 domain_name, seqnum, (unsigned)last_seq_check));
497 return true;
500 static bool store_cache_seqnum( struct winbindd_domain *domain )
502 return wcache_store_seqnum(domain->name, domain->sequence_number,
503 domain->last_seq_check);
507 refresh the domain sequence number. If force is true
508 then always refresh it, no matter how recently we fetched it
511 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
513 NTSTATUS status;
514 unsigned time_diff;
515 time_t t = time(NULL);
516 unsigned cache_time = lp_winbind_cache_time();
518 if (is_domain_offline(domain)) {
519 return;
522 get_cache( domain );
524 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
525 /* trying to reconnect is expensive, don't do it too often */
526 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
527 cache_time *= 8;
529 #endif
531 time_diff = t - domain->last_seq_check;
533 /* see if we have to refetch the domain sequence number */
534 if (!force && (time_diff < cache_time) &&
535 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
536 NT_STATUS_IS_OK(domain->last_status)) {
537 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
538 goto done;
541 /* try to get the sequence number from the tdb cache first */
542 /* this will update the timestamp as well */
544 status = fetch_cache_seqnum( domain, t );
545 if (NT_STATUS_IS_OK(status) &&
546 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
547 NT_STATUS_IS_OK(domain->last_status)) {
548 goto done;
551 /* important! make sure that we know if this is a native
552 mode domain or not. And that we can contact it. */
554 if ( winbindd_can_contact_domain( domain ) ) {
555 status = domain->backend->sequence_number(domain,
556 &domain->sequence_number);
557 } else {
558 /* just use the current time */
559 status = NT_STATUS_OK;
560 domain->sequence_number = time(NULL);
564 /* the above call could have set our domain->backend to NULL when
565 * coming from offline to online mode, make sure to reinitialize the
566 * backend - Guenther */
567 get_cache( domain );
569 if (!NT_STATUS_IS_OK(status)) {
570 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
571 domain->sequence_number = DOM_SEQUENCE_NONE;
574 domain->last_status = status;
575 domain->last_seq_check = time(NULL);
577 /* save the new sequence number in the cache */
578 store_cache_seqnum( domain );
580 done:
581 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
582 domain->name, domain->sequence_number));
584 return;
588 decide if a cache entry has expired
590 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
592 /* If we've been told to be offline - stay in that state... */
593 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
594 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
595 keystr, domain->name ));
596 return false;
599 /* when the domain is offline return the cached entry.
600 * This deals with transient offline states... */
602 if (!domain->online) {
603 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
604 keystr, domain->name ));
605 return false;
608 /* if the server is OK and our cache entry came from when it was down then
609 the entry is invalid */
610 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
611 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
612 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
613 keystr, domain->name ));
614 return true;
617 /* if the server is down or the cache entry is not older than the
618 current sequence number or it did not timeout then it is OK */
619 if (wcache_server_down(domain)
620 || ((centry->sequence_number == domain->sequence_number)
621 && (centry->timeout > time(NULL)))) {
622 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
623 keystr, domain->name ));
624 return false;
627 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
628 keystr, domain->name ));
630 /* it's expired */
631 return true;
634 static struct cache_entry *wcache_fetch_raw(char *kstr)
636 TDB_DATA data;
637 struct cache_entry *centry;
638 TDB_DATA key;
640 key = string_tdb_data(kstr);
641 data = tdb_fetch(wcache->tdb, key);
642 if (!data.dptr) {
643 /* a cache miss */
644 return NULL;
647 centry = SMB_XMALLOC_P(struct cache_entry);
648 centry->data = (unsigned char *)data.dptr;
649 centry->len = data.dsize;
650 centry->ofs = 0;
652 if (centry->len < 16) {
653 /* huh? corrupt cache? */
654 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
655 "(len < 16)?\n", kstr));
656 centry_free(centry);
657 return NULL;
660 centry->status = centry_ntstatus(centry);
661 centry->sequence_number = centry_uint32(centry);
662 centry->timeout = centry_uint64_t(centry);
664 return centry;
667 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
669 if (strequal(domain->name, get_global_sam_name()) &&
670 sid_check_is_domain(&domain->sid)) {
671 return true;
674 return false;
677 static bool is_builtin_domain(struct winbindd_domain *domain)
679 if (strequal(domain->name, "BUILTIN") &&
680 sid_check_is_builtin(&domain->sid)) {
681 return true;
684 return false;
688 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
689 number and return status
691 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
692 struct winbindd_domain *domain,
693 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
694 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
695 struct winbindd_domain *domain,
696 const char *format, ...)
698 va_list ap;
699 char *kstr;
700 struct cache_entry *centry;
702 if (!winbindd_use_cache() ||
703 is_my_own_sam_domain(domain) ||
704 is_builtin_domain(domain)) {
705 return NULL;
708 refresh_sequence_number(domain, false);
710 va_start(ap, format);
711 smb_xvasprintf(&kstr, format, ap);
712 va_end(ap);
714 centry = wcache_fetch_raw(kstr);
715 if (centry == NULL) {
716 free(kstr);
717 return NULL;
720 if (centry_expired(domain, kstr, centry)) {
722 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
723 kstr, domain->name ));
725 centry_free(centry);
726 free(kstr);
727 return NULL;
730 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
731 kstr, domain->name ));
733 free(kstr);
734 return centry;
737 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
738 static void wcache_delete(const char *format, ...)
740 va_list ap;
741 char *kstr;
742 TDB_DATA key;
744 va_start(ap, format);
745 smb_xvasprintf(&kstr, format, ap);
746 va_end(ap);
748 key = string_tdb_data(kstr);
750 tdb_delete(wcache->tdb, key);
751 free(kstr);
755 make sure we have at least len bytes available in a centry
757 static void centry_expand(struct cache_entry *centry, uint32 len)
759 if (centry->len - centry->ofs >= len)
760 return;
761 centry->len *= 2;
762 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
763 centry->len);
764 if (!centry->data) {
765 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
766 smb_panic_fn("out of memory in centry_expand");
771 push a uint64_t into a centry
773 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
775 centry_expand(centry, 8);
776 SBVAL(centry->data, centry->ofs, v);
777 centry->ofs += 8;
781 push a uint32 into a centry
783 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
785 centry_expand(centry, 4);
786 SIVAL(centry->data, centry->ofs, v);
787 centry->ofs += 4;
791 push a uint16 into a centry
793 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
795 centry_expand(centry, 2);
796 SSVAL(centry->data, centry->ofs, v);
797 centry->ofs += 2;
801 push a uint8 into a centry
803 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
805 centry_expand(centry, 1);
806 SCVAL(centry->data, centry->ofs, v);
807 centry->ofs += 1;
811 push a string into a centry
813 static void centry_put_string(struct cache_entry *centry, const char *s)
815 int len;
817 if (!s) {
818 /* null strings are marked as len 0xFFFF */
819 centry_put_uint8(centry, 0xFF);
820 return;
823 len = strlen(s);
824 /* can't handle more than 254 char strings. Truncating is probably best */
825 if (len > 254) {
826 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
827 len = 254;
829 centry_put_uint8(centry, len);
830 centry_expand(centry, len);
831 memcpy(centry->data + centry->ofs, s, len);
832 centry->ofs += len;
836 push a 16 byte hash into a centry - treat as 16 byte string.
838 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
840 centry_put_uint8(centry, 16);
841 centry_expand(centry, 16);
842 memcpy(centry->data + centry->ofs, val, 16);
843 centry->ofs += 16;
846 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
848 fstring sid_string;
849 centry_put_string(centry, sid_to_fstring(sid_string, sid));
854 put NTSTATUS into a centry
856 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
858 uint32 status_value = NT_STATUS_V(status);
859 centry_put_uint32(centry, status_value);
864 push a NTTIME into a centry
866 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
868 centry_expand(centry, 8);
869 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
870 centry->ofs += 4;
871 SIVAL(centry->data, centry->ofs, nt >> 32);
872 centry->ofs += 4;
876 push a time_t into a centry - use a 64 bit size.
877 NTTIME here is being used as a convenient 64-bit size.
879 static void centry_put_time(struct cache_entry *centry, time_t t)
881 NTTIME nt = (NTTIME)t;
882 centry_put_nttime(centry, nt);
886 start a centry for output. When finished, call centry_end()
888 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
890 struct cache_entry *centry;
892 if (!wcache->tdb)
893 return NULL;
895 centry = SMB_XMALLOC_P(struct cache_entry);
897 centry->len = 8192; /* reasonable default */
898 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
899 centry->ofs = 0;
900 centry->sequence_number = domain->sequence_number;
901 centry->timeout = lp_winbind_cache_time() + time(NULL);
902 centry_put_ntstatus(centry, status);
903 centry_put_uint32(centry, centry->sequence_number);
904 centry_put_uint64_t(centry, centry->timeout);
905 return centry;
909 finish a centry and write it to the tdb
911 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
912 static void centry_end(struct cache_entry *centry, const char *format, ...)
914 va_list ap;
915 char *kstr;
916 TDB_DATA key, data;
918 if (!winbindd_use_cache()) {
919 return;
922 va_start(ap, format);
923 smb_xvasprintf(&kstr, format, ap);
924 va_end(ap);
926 key = string_tdb_data(kstr);
927 data.dptr = centry->data;
928 data.dsize = centry->ofs;
930 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
931 free(kstr);
934 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
935 NTSTATUS status, const char *domain_name,
936 const char *name, const struct dom_sid *sid,
937 enum lsa_SidType type)
939 struct cache_entry *centry;
940 fstring uname;
942 centry = centry_start(domain, status);
943 if (!centry)
944 return;
945 centry_put_uint32(centry, type);
946 centry_put_sid(centry, sid);
947 fstrcpy(uname, name);
948 strupper_m(uname);
949 centry_end(centry, "NS/%s/%s", domain_name, uname);
950 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
951 uname, sid_string_dbg(sid), nt_errstr(status)));
952 centry_free(centry);
955 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
956 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
958 struct cache_entry *centry;
959 fstring sid_string;
961 centry = centry_start(domain, status);
962 if (!centry)
963 return;
965 if (NT_STATUS_IS_OK(status)) {
966 centry_put_uint32(centry, type);
967 centry_put_string(centry, domain_name);
968 centry_put_string(centry, name);
971 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
972 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
973 name, nt_errstr(status)));
974 centry_free(centry);
978 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
979 struct wbint_userinfo *info)
981 struct cache_entry *centry;
982 fstring sid_string;
984 if (is_null_sid(&info->user_sid)) {
985 return;
988 centry = centry_start(domain, status);
989 if (!centry)
990 return;
991 centry_put_string(centry, info->acct_name);
992 centry_put_string(centry, info->full_name);
993 centry_put_string(centry, info->homedir);
994 centry_put_string(centry, info->shell);
995 centry_put_uint32(centry, info->primary_gid);
996 centry_put_sid(centry, &info->user_sid);
997 centry_put_sid(centry, &info->group_sid);
998 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
999 &info->user_sid));
1000 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1001 centry_free(centry);
1004 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1005 NTSTATUS status,
1006 struct samr_DomInfo12 *lockout_policy)
1008 struct cache_entry *centry;
1010 centry = centry_start(domain, status);
1011 if (!centry)
1012 return;
1014 centry_put_nttime(centry, lockout_policy->lockout_duration);
1015 centry_put_nttime(centry, lockout_policy->lockout_window);
1016 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1018 centry_end(centry, "LOC_POL/%s", domain->name);
1020 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1022 centry_free(centry);
1027 static void wcache_save_password_policy(struct winbindd_domain *domain,
1028 NTSTATUS status,
1029 struct samr_DomInfo1 *policy)
1031 struct cache_entry *centry;
1033 centry = centry_start(domain, status);
1034 if (!centry)
1035 return;
1037 centry_put_uint16(centry, policy->min_password_length);
1038 centry_put_uint16(centry, policy->password_history_length);
1039 centry_put_uint32(centry, policy->password_properties);
1040 centry_put_nttime(centry, policy->max_password_age);
1041 centry_put_nttime(centry, policy->min_password_age);
1043 centry_end(centry, "PWD_POL/%s", domain->name);
1045 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1047 centry_free(centry);
1050 /***************************************************************************
1051 ***************************************************************************/
1053 static void wcache_save_username_alias(struct winbindd_domain *domain,
1054 NTSTATUS status,
1055 const char *name, const char *alias)
1057 struct cache_entry *centry;
1058 fstring uname;
1060 if ( (centry = centry_start(domain, status)) == NULL )
1061 return;
1063 centry_put_string( centry, alias );
1065 fstrcpy(uname, name);
1066 strupper_m(uname);
1067 centry_end(centry, "NSS/NA/%s", uname);
1069 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1071 centry_free(centry);
1074 static void wcache_save_alias_username(struct winbindd_domain *domain,
1075 NTSTATUS status,
1076 const char *alias, const char *name)
1078 struct cache_entry *centry;
1079 fstring uname;
1081 if ( (centry = centry_start(domain, status)) == NULL )
1082 return;
1084 centry_put_string( centry, name );
1086 fstrcpy(uname, alias);
1087 strupper_m(uname);
1088 centry_end(centry, "NSS/AN/%s", uname);
1090 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1092 centry_free(centry);
1095 /***************************************************************************
1096 ***************************************************************************/
1098 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1099 struct winbindd_domain *domain,
1100 const char *name, char **alias )
1102 struct winbind_cache *cache = get_cache(domain);
1103 struct cache_entry *centry = NULL;
1104 NTSTATUS status;
1105 char *upper_name;
1107 if ( domain->internal )
1108 return NT_STATUS_NOT_SUPPORTED;
1110 if (!cache->tdb)
1111 goto do_query;
1113 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1114 return NT_STATUS_NO_MEMORY;
1115 strupper_m(upper_name);
1117 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1119 SAFE_FREE( upper_name );
1121 if (!centry)
1122 goto do_query;
1124 status = centry->status;
1126 if (!NT_STATUS_IS_OK(status)) {
1127 centry_free(centry);
1128 return status;
1131 *alias = centry_string( centry, mem_ctx );
1133 centry_free(centry);
1135 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1136 name, *alias ? *alias : "(none)"));
1138 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1140 do_query:
1142 /* If its not in cache and we are offline, then fail */
1144 if ( get_global_winbindd_state_offline() || !domain->online ) {
1145 DEBUG(8,("resolve_username_to_alias: rejecting query "
1146 "in offline mode\n"));
1147 return NT_STATUS_NOT_FOUND;
1150 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1152 if ( NT_STATUS_IS_OK( status ) ) {
1153 wcache_save_username_alias(domain, status, name, *alias);
1156 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1157 wcache_save_username_alias(domain, status, name, "(NULL)");
1160 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1161 nt_errstr(status)));
1163 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1164 set_domain_offline( domain );
1167 return status;
1170 /***************************************************************************
1171 ***************************************************************************/
1173 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1174 struct winbindd_domain *domain,
1175 const char *alias, char **name )
1177 struct winbind_cache *cache = get_cache(domain);
1178 struct cache_entry *centry = NULL;
1179 NTSTATUS status;
1180 char *upper_name;
1182 if ( domain->internal )
1183 return NT_STATUS_NOT_SUPPORTED;
1185 if (!cache->tdb)
1186 goto do_query;
1188 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1189 return NT_STATUS_NO_MEMORY;
1190 strupper_m(upper_name);
1192 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1194 SAFE_FREE( upper_name );
1196 if (!centry)
1197 goto do_query;
1199 status = centry->status;
1201 if (!NT_STATUS_IS_OK(status)) {
1202 centry_free(centry);
1203 return status;
1206 *name = centry_string( centry, mem_ctx );
1208 centry_free(centry);
1210 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1211 alias, *name ? *name : "(none)"));
1213 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1215 do_query:
1217 /* If its not in cache and we are offline, then fail */
1219 if ( get_global_winbindd_state_offline() || !domain->online ) {
1220 DEBUG(8,("resolve_alias_to_username: rejecting query "
1221 "in offline mode\n"));
1222 return NT_STATUS_NOT_FOUND;
1225 /* an alias cannot contain a domain prefix or '@' */
1227 if (strchr(alias, '\\') || strchr(alias, '@')) {
1228 DEBUG(10,("resolve_alias_to_username: skipping fully "
1229 "qualified name %s\n", alias));
1230 return NT_STATUS_OBJECT_NAME_INVALID;
1233 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1235 if ( NT_STATUS_IS_OK( status ) ) {
1236 wcache_save_alias_username( domain, status, alias, *name );
1239 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1240 wcache_save_alias_username(domain, status, alias, "(NULL)");
1243 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1244 nt_errstr(status)));
1246 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1247 set_domain_offline( domain );
1250 return status;
1253 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1255 struct winbind_cache *cache = get_cache(domain);
1256 TDB_DATA data;
1257 fstring key_str, tmp;
1258 uint32 rid;
1260 if (!cache->tdb) {
1261 return NT_STATUS_INTERNAL_DB_ERROR;
1264 if (is_null_sid(sid)) {
1265 return NT_STATUS_INVALID_SID;
1268 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1269 return NT_STATUS_INVALID_SID;
1272 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1274 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1275 if (!data.dptr) {
1276 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1279 SAFE_FREE(data.dptr);
1280 return NT_STATUS_OK;
1283 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1284 as new salted ones. */
1286 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1287 TALLOC_CTX *mem_ctx,
1288 const struct dom_sid *sid,
1289 const uint8 **cached_nt_pass,
1290 const uint8 **cached_salt)
1292 struct winbind_cache *cache = get_cache(domain);
1293 struct cache_entry *centry = NULL;
1294 NTSTATUS status;
1295 time_t t;
1296 uint32 rid;
1297 fstring tmp;
1299 if (!cache->tdb) {
1300 return NT_STATUS_INTERNAL_DB_ERROR;
1303 if (is_null_sid(sid)) {
1304 return NT_STATUS_INVALID_SID;
1307 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1308 return NT_STATUS_INVALID_SID;
1311 /* Try and get a salted cred first. If we can't
1312 fall back to an unsalted cred. */
1314 centry = wcache_fetch(cache, domain, "CRED/%s",
1315 sid_to_fstring(tmp, sid));
1316 if (!centry) {
1317 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1318 sid_string_dbg(sid)));
1319 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1322 t = centry_time(centry);
1324 /* In the salted case this isn't actually the nt_hash itself,
1325 but the MD5 of the salt + nt_hash. Let the caller
1326 sort this out. It can tell as we only return the cached_salt
1327 if we are returning a salted cred. */
1329 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1330 if (*cached_nt_pass == NULL) {
1331 fstring sidstr;
1333 sid_to_fstring(sidstr, sid);
1335 /* Bad (old) cred cache. Delete and pretend we
1336 don't have it. */
1337 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1338 sidstr));
1339 wcache_delete("CRED/%s", sidstr);
1340 centry_free(centry);
1341 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1344 /* We only have 17 bytes more data in the salted cred case. */
1345 if (centry->len - centry->ofs == 17) {
1346 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1347 } else {
1348 *cached_salt = NULL;
1351 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1352 if (*cached_salt) {
1353 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1356 status = centry->status;
1358 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1359 sid_string_dbg(sid), nt_errstr(status) ));
1361 centry_free(centry);
1362 return status;
1365 /* Store creds for a SID - only writes out new salted ones. */
1367 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1368 const struct dom_sid *sid,
1369 const uint8 nt_pass[NT_HASH_LEN])
1371 struct cache_entry *centry;
1372 fstring sid_string;
1373 uint32 rid;
1374 uint8 cred_salt[NT_HASH_LEN];
1375 uint8 salted_hash[NT_HASH_LEN];
1377 if (is_null_sid(sid)) {
1378 return NT_STATUS_INVALID_SID;
1381 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1382 return NT_STATUS_INVALID_SID;
1385 centry = centry_start(domain, NT_STATUS_OK);
1386 if (!centry) {
1387 return NT_STATUS_INTERNAL_DB_ERROR;
1390 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1392 centry_put_time(centry, time(NULL));
1394 /* Create a salt and then salt the hash. */
1395 generate_random_buffer(cred_salt, NT_HASH_LEN);
1396 E_md5hash(cred_salt, nt_pass, salted_hash);
1398 centry_put_hash16(centry, salted_hash);
1399 centry_put_hash16(centry, cred_salt);
1400 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1402 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1404 centry_free(centry);
1406 return NT_STATUS_OK;
1410 /* Query display info. This is the basic user list fn */
1411 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1412 TALLOC_CTX *mem_ctx,
1413 uint32 *num_entries,
1414 struct wbint_userinfo **info)
1416 struct winbind_cache *cache = get_cache(domain);
1417 struct cache_entry *centry = NULL;
1418 NTSTATUS status;
1419 unsigned int i, retry;
1420 bool old_status = domain->online;
1422 if (!cache->tdb)
1423 goto do_query;
1425 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1426 if (!centry)
1427 goto do_query;
1429 do_fetch_cache:
1430 *num_entries = centry_uint32(centry);
1432 if (*num_entries == 0)
1433 goto do_cached;
1435 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1436 if (! (*info)) {
1437 smb_panic_fn("query_user_list out of memory");
1439 for (i=0; i<(*num_entries); i++) {
1440 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1441 (*info)[i].full_name = centry_string(centry, mem_ctx);
1442 (*info)[i].homedir = centry_string(centry, mem_ctx);
1443 (*info)[i].shell = centry_string(centry, mem_ctx);
1444 centry_sid(centry, &(*info)[i].user_sid);
1445 centry_sid(centry, &(*info)[i].group_sid);
1448 do_cached:
1449 status = centry->status;
1451 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1452 domain->name, nt_errstr(status) ));
1454 centry_free(centry);
1455 return status;
1457 do_query:
1458 *num_entries = 0;
1459 *info = NULL;
1461 /* Return status value returned by seq number check */
1463 if (!NT_STATUS_IS_OK(domain->last_status))
1464 return domain->last_status;
1466 /* Put the query_user_list() in a retry loop. There appears to be
1467 * some bug either with Windows 2000 or Samba's handling of large
1468 * rpc replies. This manifests itself as sudden disconnection
1469 * at a random point in the enumeration of a large (60k) user list.
1470 * The retry loop simply tries the operation again. )-: It's not
1471 * pretty but an acceptable workaround until we work out what the
1472 * real problem is. */
1474 retry = 0;
1475 do {
1477 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1478 domain->name ));
1480 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1481 if (!NT_STATUS_IS_OK(status)) {
1482 DEBUG(3, ("query_user_list: returned 0x%08x, "
1483 "retrying\n", NT_STATUS_V(status)));
1485 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1486 DEBUG(3, ("query_user_list: flushing "
1487 "connection cache\n"));
1488 invalidate_cm_connection(&domain->conn);
1490 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1491 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1492 if (!domain->internal && old_status) {
1493 set_domain_offline(domain);
1495 /* store partial response. */
1496 if (*num_entries > 0) {
1498 * humm, what about the status used for cache?
1499 * Should it be NT_STATUS_OK?
1501 break;
1504 * domain is offline now, and there is no user entries,
1505 * try to fetch from cache again.
1507 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1508 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1509 /* partial response... */
1510 if (!centry) {
1511 goto skip_save;
1512 } else {
1513 goto do_fetch_cache;
1515 } else {
1516 goto skip_save;
1520 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1521 (retry++ < 5));
1523 /* and save it */
1524 refresh_sequence_number(domain, false);
1525 if (!NT_STATUS_IS_OK(status)) {
1526 return status;
1528 centry = centry_start(domain, status);
1529 if (!centry)
1530 goto skip_save;
1531 centry_put_uint32(centry, *num_entries);
1532 for (i=0; i<(*num_entries); i++) {
1533 centry_put_string(centry, (*info)[i].acct_name);
1534 centry_put_string(centry, (*info)[i].full_name);
1535 centry_put_string(centry, (*info)[i].homedir);
1536 centry_put_string(centry, (*info)[i].shell);
1537 centry_put_sid(centry, &(*info)[i].user_sid);
1538 centry_put_sid(centry, &(*info)[i].group_sid);
1539 if (domain->backend && domain->backend->consistent) {
1540 /* when the backend is consistent we can pre-prime some mappings */
1541 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1542 domain->name,
1543 (*info)[i].acct_name,
1544 &(*info)[i].user_sid,
1545 SID_NAME_USER);
1546 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1547 &(*info)[i].user_sid,
1548 domain->name,
1549 (*info)[i].acct_name,
1550 SID_NAME_USER);
1551 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1554 centry_end(centry, "UL/%s", domain->name);
1555 centry_free(centry);
1557 skip_save:
1558 return status;
1561 /* list all domain groups */
1562 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1563 TALLOC_CTX *mem_ctx,
1564 uint32 *num_entries,
1565 struct wb_acct_info **info)
1567 struct winbind_cache *cache = get_cache(domain);
1568 struct cache_entry *centry = NULL;
1569 NTSTATUS status;
1570 unsigned int i;
1571 bool old_status;
1573 old_status = domain->online;
1574 if (!cache->tdb)
1575 goto do_query;
1577 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1578 if (!centry)
1579 goto do_query;
1581 do_fetch_cache:
1582 *num_entries = centry_uint32(centry);
1584 if (*num_entries == 0)
1585 goto do_cached;
1587 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1588 if (! (*info)) {
1589 smb_panic_fn("enum_dom_groups out of memory");
1591 for (i=0; i<(*num_entries); i++) {
1592 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1593 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1594 (*info)[i].rid = centry_uint32(centry);
1597 do_cached:
1598 status = centry->status;
1600 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1601 domain->name, nt_errstr(status) ));
1603 centry_free(centry);
1604 return status;
1606 do_query:
1607 *num_entries = 0;
1608 *info = NULL;
1610 /* Return status value returned by seq number check */
1612 if (!NT_STATUS_IS_OK(domain->last_status))
1613 return domain->last_status;
1615 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1616 domain->name ));
1618 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1620 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1621 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1622 if (!domain->internal && old_status) {
1623 set_domain_offline(domain);
1625 if (cache->tdb &&
1626 !domain->online &&
1627 !domain->internal &&
1628 old_status) {
1629 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1630 if (centry) {
1631 goto do_fetch_cache;
1635 /* and save it */
1636 refresh_sequence_number(domain, false);
1637 if (!NT_STATUS_IS_OK(status)) {
1638 return status;
1640 centry = centry_start(domain, status);
1641 if (!centry)
1642 goto skip_save;
1643 centry_put_uint32(centry, *num_entries);
1644 for (i=0; i<(*num_entries); i++) {
1645 centry_put_string(centry, (*info)[i].acct_name);
1646 centry_put_string(centry, (*info)[i].acct_desc);
1647 centry_put_uint32(centry, (*info)[i].rid);
1649 centry_end(centry, "GL/%s/domain", domain->name);
1650 centry_free(centry);
1652 skip_save:
1653 return status;
1656 /* list all domain groups */
1657 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1658 TALLOC_CTX *mem_ctx,
1659 uint32 *num_entries,
1660 struct wb_acct_info **info)
1662 struct winbind_cache *cache = get_cache(domain);
1663 struct cache_entry *centry = NULL;
1664 NTSTATUS status;
1665 unsigned int i;
1666 bool old_status;
1668 old_status = domain->online;
1669 if (!cache->tdb)
1670 goto do_query;
1672 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1673 if (!centry)
1674 goto do_query;
1676 do_fetch_cache:
1677 *num_entries = centry_uint32(centry);
1679 if (*num_entries == 0)
1680 goto do_cached;
1682 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1683 if (! (*info)) {
1684 smb_panic_fn("enum_dom_groups out of memory");
1686 for (i=0; i<(*num_entries); i++) {
1687 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1688 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1689 (*info)[i].rid = centry_uint32(centry);
1692 do_cached:
1694 /* If we are returning cached data and the domain controller
1695 is down then we don't know whether the data is up to date
1696 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1697 indicate this. */
1699 if (wcache_server_down(domain)) {
1700 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1701 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1702 } else
1703 status = centry->status;
1705 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1706 domain->name, nt_errstr(status) ));
1708 centry_free(centry);
1709 return status;
1711 do_query:
1712 *num_entries = 0;
1713 *info = NULL;
1715 /* Return status value returned by seq number check */
1717 if (!NT_STATUS_IS_OK(domain->last_status))
1718 return domain->last_status;
1720 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1721 domain->name ));
1723 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1725 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1726 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1727 if (!domain->internal && old_status) {
1728 set_domain_offline(domain);
1730 if (cache->tdb &&
1731 !domain->internal &&
1732 !domain->online &&
1733 old_status) {
1734 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1735 if (centry) {
1736 goto do_fetch_cache;
1740 /* and save it */
1741 refresh_sequence_number(domain, false);
1742 if (!NT_STATUS_IS_OK(status)) {
1743 return status;
1745 centry = centry_start(domain, status);
1746 if (!centry)
1747 goto skip_save;
1748 centry_put_uint32(centry, *num_entries);
1749 for (i=0; i<(*num_entries); i++) {
1750 centry_put_string(centry, (*info)[i].acct_name);
1751 centry_put_string(centry, (*info)[i].acct_desc);
1752 centry_put_uint32(centry, (*info)[i].rid);
1754 centry_end(centry, "GL/%s/local", domain->name);
1755 centry_free(centry);
1757 skip_save:
1758 return status;
1761 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1762 const char *domain_name,
1763 const char *name,
1764 struct dom_sid *sid,
1765 enum lsa_SidType *type)
1767 struct winbind_cache *cache = get_cache(domain);
1768 struct cache_entry *centry;
1769 NTSTATUS status;
1770 char *uname;
1772 if (cache->tdb == NULL) {
1773 return NT_STATUS_NOT_FOUND;
1776 uname = talloc_strdup_upper(talloc_tos(), name);
1777 if (uname == NULL) {
1778 return NT_STATUS_NO_MEMORY;
1781 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1782 TALLOC_FREE(uname);
1783 if (centry == NULL) {
1784 return NT_STATUS_NOT_FOUND;
1787 status = centry->status;
1788 if (NT_STATUS_IS_OK(status)) {
1789 *type = (enum lsa_SidType)centry_uint32(centry);
1790 centry_sid(centry, sid);
1793 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1794 "%s\n", domain->name, nt_errstr(status) ));
1796 centry_free(centry);
1797 return status;
1800 /* convert a single name to a sid in a domain */
1801 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1802 TALLOC_CTX *mem_ctx,
1803 const char *domain_name,
1804 const char *name,
1805 uint32_t flags,
1806 struct dom_sid *sid,
1807 enum lsa_SidType *type)
1809 NTSTATUS status;
1810 bool old_status;
1812 old_status = domain->online;
1814 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1815 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1816 return status;
1819 ZERO_STRUCTP(sid);
1821 /* If the seq number check indicated that there is a problem
1822 * with this DC, then return that status... except for
1823 * access_denied. This is special because the dc may be in
1824 * "restrict anonymous = 1" mode, in which case it will deny
1825 * most unauthenticated operations, but *will* allow the LSA
1826 * name-to-sid that we try as a fallback. */
1828 if (!(NT_STATUS_IS_OK(domain->last_status)
1829 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1830 return domain->last_status;
1832 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1833 domain->name ));
1835 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1836 name, flags, sid, type);
1838 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1839 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1840 if (!domain->internal && old_status) {
1841 set_domain_offline(domain);
1843 if (!domain->internal &&
1844 !domain->online &&
1845 old_status) {
1846 NTSTATUS cache_status;
1847 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1848 return cache_status;
1851 /* and save it */
1852 refresh_sequence_number(domain, false);
1854 if (domain->online &&
1855 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1856 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1858 /* Only save the reverse mapping if this was not a UPN */
1859 if (!strchr(name, '@')) {
1860 strupper_m(CONST_DISCARD(char *,domain_name));
1861 strlower_m(CONST_DISCARD(char *,name));
1862 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1866 return status;
1869 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1870 const struct dom_sid *sid,
1871 TALLOC_CTX *mem_ctx,
1872 char **domain_name,
1873 char **name,
1874 enum lsa_SidType *type)
1876 struct winbind_cache *cache = get_cache(domain);
1877 struct cache_entry *centry;
1878 char *sid_string;
1879 NTSTATUS status;
1881 if (cache->tdb == NULL) {
1882 return NT_STATUS_NOT_FOUND;
1885 sid_string = sid_string_tos(sid);
1886 if (sid_string == NULL) {
1887 return NT_STATUS_NO_MEMORY;
1890 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1891 TALLOC_FREE(sid_string);
1892 if (centry == NULL) {
1893 return NT_STATUS_NOT_FOUND;
1896 if (NT_STATUS_IS_OK(centry->status)) {
1897 *type = (enum lsa_SidType)centry_uint32(centry);
1898 *domain_name = centry_string(centry, mem_ctx);
1899 *name = centry_string(centry, mem_ctx);
1902 status = centry->status;
1903 centry_free(centry);
1905 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1906 "%s\n", domain->name, nt_errstr(status) ));
1908 return status;
1911 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1912 given */
1913 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1914 TALLOC_CTX *mem_ctx,
1915 const struct dom_sid *sid,
1916 char **domain_name,
1917 char **name,
1918 enum lsa_SidType *type)
1920 NTSTATUS status;
1921 bool old_status;
1923 old_status = domain->online;
1924 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1925 type);
1926 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1927 return status;
1930 *name = NULL;
1931 *domain_name = NULL;
1933 /* If the seq number check indicated that there is a problem
1934 * with this DC, then return that status... except for
1935 * access_denied. This is special because the dc may be in
1936 * "restrict anonymous = 1" mode, in which case it will deny
1937 * most unauthenticated operations, but *will* allow the LSA
1938 * sid-to-name that we try as a fallback. */
1940 if (!(NT_STATUS_IS_OK(domain->last_status)
1941 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1942 return domain->last_status;
1944 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1945 domain->name ));
1947 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1949 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1950 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1951 if (!domain->internal && old_status) {
1952 set_domain_offline(domain);
1954 if (!domain->internal &&
1955 !domain->online &&
1956 old_status) {
1957 NTSTATUS cache_status;
1958 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1959 domain_name, name, type);
1960 return cache_status;
1963 /* and save it */
1964 refresh_sequence_number(domain, false);
1965 if (!NT_STATUS_IS_OK(status)) {
1966 return status;
1968 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1970 /* We can't save the name to sid mapping here, as with sid history a
1971 * later name2sid would give the wrong sid. */
1973 return status;
1976 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1977 TALLOC_CTX *mem_ctx,
1978 const struct dom_sid *domain_sid,
1979 uint32 *rids,
1980 size_t num_rids,
1981 char **domain_name,
1982 char ***names,
1983 enum lsa_SidType **types)
1985 struct winbind_cache *cache = get_cache(domain);
1986 size_t i;
1987 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1988 bool have_mapped;
1989 bool have_unmapped;
1990 bool old_status;
1992 old_status = domain->online;
1993 *domain_name = NULL;
1994 *names = NULL;
1995 *types = NULL;
1997 if (!cache->tdb) {
1998 goto do_query;
2001 if (num_rids == 0) {
2002 return NT_STATUS_OK;
2005 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2006 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2008 if ((*names == NULL) || (*types == NULL)) {
2009 result = NT_STATUS_NO_MEMORY;
2010 goto error;
2013 have_mapped = have_unmapped = false;
2015 for (i=0; i<num_rids; i++) {
2016 struct dom_sid sid;
2017 struct cache_entry *centry;
2018 fstring tmp;
2020 if (!sid_compose(&sid, domain_sid, rids[i])) {
2021 result = NT_STATUS_INTERNAL_ERROR;
2022 goto error;
2025 centry = wcache_fetch(cache, domain, "SN/%s",
2026 sid_to_fstring(tmp, &sid));
2027 if (!centry) {
2028 goto do_query;
2031 (*types)[i] = SID_NAME_UNKNOWN;
2032 (*names)[i] = talloc_strdup(*names, "");
2034 if (NT_STATUS_IS_OK(centry->status)) {
2035 char *dom;
2036 have_mapped = true;
2037 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2039 dom = centry_string(centry, mem_ctx);
2040 if (*domain_name == NULL) {
2041 *domain_name = dom;
2042 } else {
2043 talloc_free(dom);
2046 (*names)[i] = centry_string(centry, *names);
2048 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2049 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2050 have_unmapped = true;
2052 } else {
2053 /* something's definitely wrong */
2054 result = centry->status;
2055 goto error;
2058 centry_free(centry);
2061 if (!have_mapped) {
2062 return NT_STATUS_NONE_MAPPED;
2064 if (!have_unmapped) {
2065 return NT_STATUS_OK;
2067 return STATUS_SOME_UNMAPPED;
2069 do_query:
2071 TALLOC_FREE(*names);
2072 TALLOC_FREE(*types);
2074 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2075 rids, num_rids, domain_name,
2076 names, types);
2078 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2079 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2080 if (!domain->internal && old_status) {
2081 set_domain_offline(domain);
2083 if (cache->tdb &&
2084 !domain->internal &&
2085 !domain->online &&
2086 old_status) {
2087 have_mapped = have_unmapped = false;
2089 for (i=0; i<num_rids; i++) {
2090 struct dom_sid sid;
2091 struct cache_entry *centry;
2092 fstring tmp;
2094 if (!sid_compose(&sid, domain_sid, rids[i])) {
2095 result = NT_STATUS_INTERNAL_ERROR;
2096 goto error;
2099 centry = wcache_fetch(cache, domain, "SN/%s",
2100 sid_to_fstring(tmp, &sid));
2101 if (!centry) {
2102 (*types)[i] = SID_NAME_UNKNOWN;
2103 (*names)[i] = talloc_strdup(*names, "");
2104 continue;
2107 (*types)[i] = SID_NAME_UNKNOWN;
2108 (*names)[i] = talloc_strdup(*names, "");
2110 if (NT_STATUS_IS_OK(centry->status)) {
2111 char *dom;
2112 have_mapped = true;
2113 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2115 dom = centry_string(centry, mem_ctx);
2116 if (*domain_name == NULL) {
2117 *domain_name = dom;
2118 } else {
2119 talloc_free(dom);
2122 (*names)[i] = centry_string(centry, *names);
2124 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2125 have_unmapped = true;
2127 } else {
2128 /* something's definitely wrong */
2129 result = centry->status;
2130 goto error;
2133 centry_free(centry);
2136 if (!have_mapped) {
2137 return NT_STATUS_NONE_MAPPED;
2139 if (!have_unmapped) {
2140 return NT_STATUS_OK;
2142 return STATUS_SOME_UNMAPPED;
2146 None of the queried rids has been found so save all negative entries
2148 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2149 for (i = 0; i < num_rids; i++) {
2150 struct dom_sid sid;
2151 const char *name = "";
2152 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2153 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2155 if (!sid_compose(&sid, domain_sid, rids[i])) {
2156 return NT_STATUS_INTERNAL_ERROR;
2159 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2160 name, type);
2163 return result;
2167 Some or all of the queried rids have been found.
2169 if (!NT_STATUS_IS_OK(result) &&
2170 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2171 return result;
2174 refresh_sequence_number(domain, false);
2176 for (i=0; i<num_rids; i++) {
2177 struct dom_sid sid;
2178 NTSTATUS status;
2180 if (!sid_compose(&sid, domain_sid, rids[i])) {
2181 result = NT_STATUS_INTERNAL_ERROR;
2182 goto error;
2185 status = (*types)[i] == SID_NAME_UNKNOWN ?
2186 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2188 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2189 (*names)[i], (*types)[i]);
2192 return result;
2194 error:
2195 TALLOC_FREE(*names);
2196 TALLOC_FREE(*types);
2197 return result;
2200 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2201 TALLOC_CTX *mem_ctx,
2202 const struct dom_sid *user_sid,
2203 struct wbint_userinfo *info)
2205 struct winbind_cache *cache = get_cache(domain);
2206 struct cache_entry *centry = NULL;
2207 NTSTATUS status;
2208 char *sid_string;
2210 if (cache->tdb == NULL) {
2211 return NT_STATUS_NOT_FOUND;
2214 sid_string = sid_string_tos(user_sid);
2215 if (sid_string == NULL) {
2216 return NT_STATUS_NO_MEMORY;
2219 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2220 TALLOC_FREE(sid_string);
2221 if (centry == NULL) {
2222 return NT_STATUS_NOT_FOUND;
2226 * If we have an access denied cache entry and a cached info3
2227 * in the samlogon cache then do a query. This will force the
2228 * rpc back end to return the info3 data.
2231 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2232 netsamlogon_cache_have(user_sid)) {
2233 DEBUG(10, ("query_user: cached access denied and have cached "
2234 "info3\n"));
2235 domain->last_status = NT_STATUS_OK;
2236 centry_free(centry);
2237 return NT_STATUS_NOT_FOUND;
2240 /* if status is not ok then this is a negative hit
2241 and the rest of the data doesn't matter */
2242 status = centry->status;
2243 if (NT_STATUS_IS_OK(status)) {
2244 info->acct_name = centry_string(centry, mem_ctx);
2245 info->full_name = centry_string(centry, mem_ctx);
2246 info->homedir = centry_string(centry, mem_ctx);
2247 info->shell = centry_string(centry, mem_ctx);
2248 info->primary_gid = centry_uint32(centry);
2249 centry_sid(centry, &info->user_sid);
2250 centry_sid(centry, &info->group_sid);
2253 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2254 "%s\n", domain->name, nt_errstr(status) ));
2256 centry_free(centry);
2257 return status;
2260 /* Lookup user information from a rid */
2261 static NTSTATUS query_user(struct winbindd_domain *domain,
2262 TALLOC_CTX *mem_ctx,
2263 const struct dom_sid *user_sid,
2264 struct wbint_userinfo *info)
2266 NTSTATUS status;
2267 bool old_status;
2269 old_status = domain->online;
2270 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2271 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2272 return status;
2275 ZERO_STRUCTP(info);
2277 /* Return status value returned by seq number check */
2279 if (!NT_STATUS_IS_OK(domain->last_status))
2280 return domain->last_status;
2282 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2283 domain->name ));
2285 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2287 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2288 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2289 if (!domain->internal && old_status) {
2290 set_domain_offline(domain);
2292 if (!domain->internal &&
2293 !domain->online &&
2294 old_status) {
2295 NTSTATUS cache_status;
2296 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2297 return cache_status;
2300 /* and save it */
2301 refresh_sequence_number(domain, false);
2302 if (!NT_STATUS_IS_OK(status)) {
2303 return status;
2305 wcache_save_user(domain, status, info);
2307 return status;
2310 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2311 TALLOC_CTX *mem_ctx,
2312 const struct dom_sid *user_sid,
2313 uint32_t *pnum_sids,
2314 struct dom_sid **psids)
2316 struct winbind_cache *cache = get_cache(domain);
2317 struct cache_entry *centry = NULL;
2318 NTSTATUS status;
2319 uint32_t i, num_sids;
2320 struct dom_sid *sids;
2321 fstring sid_string;
2323 if (cache->tdb == NULL) {
2324 return NT_STATUS_NOT_FOUND;
2327 centry = wcache_fetch(cache, domain, "UG/%s",
2328 sid_to_fstring(sid_string, user_sid));
2329 if (centry == NULL) {
2330 return NT_STATUS_NOT_FOUND;
2333 /* If we have an access denied cache entry and a cached info3 in the
2334 samlogon cache then do a query. This will force the rpc back end
2335 to return the info3 data. */
2337 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2338 && netsamlogon_cache_have(user_sid)) {
2339 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2340 "cached info3\n"));
2341 domain->last_status = NT_STATUS_OK;
2342 centry_free(centry);
2343 return NT_STATUS_NOT_FOUND;
2346 num_sids = centry_uint32(centry);
2347 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2348 if (sids == NULL) {
2349 centry_free(centry);
2350 return NT_STATUS_NO_MEMORY;
2353 for (i=0; i<num_sids; i++) {
2354 centry_sid(centry, &sids[i]);
2357 status = centry->status;
2359 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2360 "status: %s\n", domain->name, nt_errstr(status)));
2362 centry_free(centry);
2364 *pnum_sids = num_sids;
2365 *psids = sids;
2366 return status;
2369 /* Lookup groups a user is a member of. */
2370 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2371 TALLOC_CTX *mem_ctx,
2372 const struct dom_sid *user_sid,
2373 uint32 *num_groups, struct dom_sid **user_gids)
2375 struct cache_entry *centry = NULL;
2376 NTSTATUS status;
2377 unsigned int i;
2378 fstring sid_string;
2379 bool old_status;
2381 old_status = domain->online;
2382 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2383 num_groups, user_gids);
2384 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2385 return status;
2388 (*num_groups) = 0;
2389 (*user_gids) = NULL;
2391 /* Return status value returned by seq number check */
2393 if (!NT_STATUS_IS_OK(domain->last_status))
2394 return domain->last_status;
2396 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2397 domain->name ));
2399 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2401 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2402 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2403 if (!domain->internal && old_status) {
2404 set_domain_offline(domain);
2406 if (!domain->internal &&
2407 !domain->online &&
2408 old_status) {
2409 NTSTATUS cache_status;
2410 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2411 num_groups, user_gids);
2412 return cache_status;
2415 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2416 goto skip_save;
2418 /* and save it */
2419 refresh_sequence_number(domain, false);
2420 if (!NT_STATUS_IS_OK(status)) {
2421 return status;
2423 centry = centry_start(domain, status);
2424 if (!centry)
2425 goto skip_save;
2427 centry_put_uint32(centry, *num_groups);
2428 for (i=0; i<(*num_groups); i++) {
2429 centry_put_sid(centry, &(*user_gids)[i]);
2432 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2433 centry_free(centry);
2435 skip_save:
2436 return status;
2439 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2440 const struct dom_sid *sids)
2442 uint32_t i;
2443 char *sidlist;
2445 sidlist = talloc_strdup(mem_ctx, "");
2446 if (sidlist == NULL) {
2447 return NULL;
2449 for (i=0; i<num_sids; i++) {
2450 fstring tmp;
2451 sidlist = talloc_asprintf_append_buffer(
2452 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2453 if (sidlist == NULL) {
2454 return NULL;
2457 return sidlist;
2460 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2461 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2462 const struct dom_sid *sids,
2463 uint32_t *pnum_aliases, uint32_t **paliases)
2465 struct winbind_cache *cache = get_cache(domain);
2466 struct cache_entry *centry = NULL;
2467 uint32_t num_aliases;
2468 uint32_t *aliases;
2469 NTSTATUS status;
2470 char *sidlist;
2471 int i;
2473 if (cache->tdb == NULL) {
2474 return NT_STATUS_NOT_FOUND;
2477 if (num_sids == 0) {
2478 *pnum_aliases = 0;
2479 *paliases = NULL;
2480 return NT_STATUS_OK;
2483 /* We need to cache indexed by the whole list of SIDs, the aliases
2484 * resulting might come from any of the SIDs. */
2486 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2487 if (sidlist == NULL) {
2488 return NT_STATUS_NO_MEMORY;
2491 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2492 TALLOC_FREE(sidlist);
2493 if (centry == NULL) {
2494 return NT_STATUS_NOT_FOUND;
2497 num_aliases = centry_uint32(centry);
2498 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2499 if (aliases == NULL) {
2500 centry_free(centry);
2501 return NT_STATUS_NO_MEMORY;
2504 for (i=0; i<num_aliases; i++) {
2505 aliases[i] = centry_uint32(centry);
2508 status = centry->status;
2510 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2511 "status %s\n", domain->name, nt_errstr(status)));
2513 centry_free(centry);
2515 *pnum_aliases = num_aliases;
2516 *paliases = aliases;
2518 return status;
2521 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2522 TALLOC_CTX *mem_ctx,
2523 uint32 num_sids, const struct dom_sid *sids,
2524 uint32 *num_aliases, uint32 **alias_rids)
2526 struct cache_entry *centry = NULL;
2527 NTSTATUS status;
2528 char *sidlist;
2529 int i;
2530 bool old_status;
2532 old_status = domain->online;
2533 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2534 num_aliases, alias_rids);
2535 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2536 return status;
2539 (*num_aliases) = 0;
2540 (*alias_rids) = NULL;
2542 if (!NT_STATUS_IS_OK(domain->last_status))
2543 return domain->last_status;
2545 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2546 "for domain %s\n", domain->name ));
2548 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2549 if (sidlist == NULL) {
2550 return NT_STATUS_NO_MEMORY;
2553 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2554 num_sids, sids,
2555 num_aliases, alias_rids);
2557 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2558 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2559 if (!domain->internal && old_status) {
2560 set_domain_offline(domain);
2562 if (!domain->internal &&
2563 !domain->online &&
2564 old_status) {
2565 NTSTATUS cache_status;
2566 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2567 sids, num_aliases, alias_rids);
2568 return cache_status;
2571 /* and save it */
2572 refresh_sequence_number(domain, false);
2573 if (!NT_STATUS_IS_OK(status)) {
2574 return status;
2576 centry = centry_start(domain, status);
2577 if (!centry)
2578 goto skip_save;
2579 centry_put_uint32(centry, *num_aliases);
2580 for (i=0; i<(*num_aliases); i++)
2581 centry_put_uint32(centry, (*alias_rids)[i]);
2582 centry_end(centry, "UA%s", sidlist);
2583 centry_free(centry);
2585 skip_save:
2586 return status;
2589 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2590 TALLOC_CTX *mem_ctx,
2591 const struct dom_sid *group_sid,
2592 uint32_t *num_names,
2593 struct dom_sid **sid_mem, char ***names,
2594 uint32_t **name_types)
2596 struct winbind_cache *cache = get_cache(domain);
2597 struct cache_entry *centry = NULL;
2598 NTSTATUS status;
2599 unsigned int i;
2600 char *sid_string;
2602 if (cache->tdb == NULL) {
2603 return NT_STATUS_NOT_FOUND;
2606 sid_string = sid_string_tos(group_sid);
2607 if (sid_string == NULL) {
2608 return NT_STATUS_NO_MEMORY;
2611 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2612 TALLOC_FREE(sid_string);
2613 if (centry == NULL) {
2614 return NT_STATUS_NOT_FOUND;
2617 *sid_mem = NULL;
2618 *names = NULL;
2619 *name_types = NULL;
2621 *num_names = centry_uint32(centry);
2622 if (*num_names == 0) {
2623 centry_free(centry);
2624 return NT_STATUS_OK;
2627 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2628 *names = talloc_array(mem_ctx, char *, *num_names);
2629 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2631 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2632 TALLOC_FREE(*sid_mem);
2633 TALLOC_FREE(*names);
2634 TALLOC_FREE(*name_types);
2635 centry_free(centry);
2636 return NT_STATUS_NO_MEMORY;
2639 for (i=0; i<(*num_names); i++) {
2640 centry_sid(centry, &(*sid_mem)[i]);
2641 (*names)[i] = centry_string(centry, mem_ctx);
2642 (*name_types)[i] = centry_uint32(centry);
2645 status = centry->status;
2647 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2648 "status: %s\n", domain->name, nt_errstr(status)));
2650 centry_free(centry);
2651 return status;
2654 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2655 TALLOC_CTX *mem_ctx,
2656 const struct dom_sid *group_sid,
2657 enum lsa_SidType type,
2658 uint32 *num_names,
2659 struct dom_sid **sid_mem, char ***names,
2660 uint32 **name_types)
2662 struct cache_entry *centry = NULL;
2663 NTSTATUS status;
2664 unsigned int i;
2665 fstring sid_string;
2666 bool old_status;
2668 old_status = domain->online;
2669 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2670 sid_mem, names, name_types);
2671 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2672 return status;
2675 (*num_names) = 0;
2676 (*sid_mem) = NULL;
2677 (*names) = NULL;
2678 (*name_types) = NULL;
2680 /* Return status value returned by seq number check */
2682 if (!NT_STATUS_IS_OK(domain->last_status))
2683 return domain->last_status;
2685 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2686 domain->name ));
2688 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2689 type, num_names,
2690 sid_mem, names, name_types);
2692 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2693 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2694 if (!domain->internal && old_status) {
2695 set_domain_offline(domain);
2697 if (!domain->internal &&
2698 !domain->online &&
2699 old_status) {
2700 NTSTATUS cache_status;
2701 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2702 num_names, sid_mem, names,
2703 name_types);
2704 return cache_status;
2707 /* and save it */
2708 refresh_sequence_number(domain, false);
2709 if (!NT_STATUS_IS_OK(status)) {
2710 return status;
2712 centry = centry_start(domain, status);
2713 if (!centry)
2714 goto skip_save;
2715 centry_put_uint32(centry, *num_names);
2716 for (i=0; i<(*num_names); i++) {
2717 centry_put_sid(centry, &(*sid_mem)[i]);
2718 centry_put_string(centry, (*names)[i]);
2719 centry_put_uint32(centry, (*name_types)[i]);
2721 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2722 centry_free(centry);
2724 skip_save:
2725 return status;
2728 /* find the sequence number for a domain */
2729 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2731 refresh_sequence_number(domain, false);
2733 *seq = domain->sequence_number;
2735 return NT_STATUS_OK;
2738 /* enumerate trusted domains
2739 * (we need to have the list of trustdoms in the cache when we go offline) -
2740 * Guenther */
2741 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2742 TALLOC_CTX *mem_ctx,
2743 struct netr_DomainTrustList *trusts)
2745 NTSTATUS status;
2746 struct winbind_cache *cache;
2747 struct winbindd_tdc_domain *dom_list = NULL;
2748 size_t num_domains = 0;
2749 bool retval = false;
2750 int i;
2751 bool old_status;
2753 old_status = domain->online;
2754 trusts->count = 0;
2755 trusts->array = NULL;
2757 cache = get_cache(domain);
2758 if (!cache || !cache->tdb) {
2759 goto do_query;
2762 if (domain->online) {
2763 goto do_query;
2766 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2767 if (!retval || !num_domains || !dom_list) {
2768 TALLOC_FREE(dom_list);
2769 goto do_query;
2772 do_fetch_cache:
2773 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2774 if (!trusts->array) {
2775 TALLOC_FREE(dom_list);
2776 return NT_STATUS_NO_MEMORY;
2779 for (i = 0; i < num_domains; i++) {
2780 struct netr_DomainTrust *trust;
2781 struct dom_sid *sid;
2782 struct winbindd_domain *dom;
2784 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2785 if (dom && dom->internal) {
2786 continue;
2789 trust = &trusts->array[trusts->count];
2790 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2791 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2792 sid = talloc(trusts->array, struct dom_sid);
2793 if (!trust->netbios_name || !trust->dns_name ||
2794 !sid) {
2795 TALLOC_FREE(dom_list);
2796 TALLOC_FREE(trusts->array);
2797 return NT_STATUS_NO_MEMORY;
2800 trust->trust_flags = dom_list[i].trust_flags;
2801 trust->trust_attributes = dom_list[i].trust_attribs;
2802 trust->trust_type = dom_list[i].trust_type;
2803 sid_copy(sid, &dom_list[i].sid);
2804 trust->sid = sid;
2805 trusts->count++;
2808 TALLOC_FREE(dom_list);
2809 return NT_STATUS_OK;
2811 do_query:
2812 /* Return status value returned by seq number check */
2814 if (!NT_STATUS_IS_OK(domain->last_status))
2815 return domain->last_status;
2817 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2818 domain->name ));
2820 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2822 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2823 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2824 if (!domain->internal && old_status) {
2825 set_domain_offline(domain);
2827 if (!domain->internal &&
2828 !domain->online &&
2829 old_status) {
2830 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2831 if (retval && num_domains && dom_list) {
2832 TALLOC_FREE(trusts->array);
2833 trusts->count = 0;
2834 goto do_fetch_cache;
2838 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2839 * so that the generic centry handling still applies correctly -
2840 * Guenther*/
2842 if (!NT_STATUS_IS_ERR(status)) {
2843 status = NT_STATUS_OK;
2845 return status;
2848 /* get lockout policy */
2849 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2850 TALLOC_CTX *mem_ctx,
2851 struct samr_DomInfo12 *policy)
2853 struct winbind_cache *cache = get_cache(domain);
2854 struct cache_entry *centry = NULL;
2855 NTSTATUS status;
2856 bool old_status;
2858 old_status = domain->online;
2859 if (!cache->tdb)
2860 goto do_query;
2862 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2864 if (!centry)
2865 goto do_query;
2867 do_fetch_cache:
2868 policy->lockout_duration = centry_nttime(centry);
2869 policy->lockout_window = centry_nttime(centry);
2870 policy->lockout_threshold = centry_uint16(centry);
2872 status = centry->status;
2874 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2875 domain->name, nt_errstr(status) ));
2877 centry_free(centry);
2878 return status;
2880 do_query:
2881 ZERO_STRUCTP(policy);
2883 /* Return status value returned by seq number check */
2885 if (!NT_STATUS_IS_OK(domain->last_status))
2886 return domain->last_status;
2888 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2889 domain->name ));
2891 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2893 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2894 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2895 if (!domain->internal && old_status) {
2896 set_domain_offline(domain);
2898 if (cache->tdb &&
2899 !domain->internal &&
2900 !domain->online &&
2901 old_status) {
2902 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2903 if (centry) {
2904 goto do_fetch_cache;
2908 /* and save it */
2909 refresh_sequence_number(domain, false);
2910 if (!NT_STATUS_IS_OK(status)) {
2911 return status;
2913 wcache_save_lockout_policy(domain, status, policy);
2915 return status;
2918 /* get password policy */
2919 static NTSTATUS password_policy(struct winbindd_domain *domain,
2920 TALLOC_CTX *mem_ctx,
2921 struct samr_DomInfo1 *policy)
2923 struct winbind_cache *cache = get_cache(domain);
2924 struct cache_entry *centry = NULL;
2925 NTSTATUS status;
2926 bool old_status;
2928 old_status = domain->online;
2929 if (!cache->tdb)
2930 goto do_query;
2932 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2934 if (!centry)
2935 goto do_query;
2937 do_fetch_cache:
2938 policy->min_password_length = centry_uint16(centry);
2939 policy->password_history_length = centry_uint16(centry);
2940 policy->password_properties = centry_uint32(centry);
2941 policy->max_password_age = centry_nttime(centry);
2942 policy->min_password_age = centry_nttime(centry);
2944 status = centry->status;
2946 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2947 domain->name, nt_errstr(status) ));
2949 centry_free(centry);
2950 return status;
2952 do_query:
2953 ZERO_STRUCTP(policy);
2955 /* Return status value returned by seq number check */
2957 if (!NT_STATUS_IS_OK(domain->last_status))
2958 return domain->last_status;
2960 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2961 domain->name ));
2963 status = domain->backend->password_policy(domain, mem_ctx, policy);
2965 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2966 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2967 if (!domain->internal && old_status) {
2968 set_domain_offline(domain);
2970 if (cache->tdb &&
2971 !domain->internal &&
2972 !domain->online &&
2973 old_status) {
2974 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2975 if (centry) {
2976 goto do_fetch_cache;
2980 /* and save it */
2981 refresh_sequence_number(domain, false);
2982 if (!NT_STATUS_IS_OK(status)) {
2983 return status;
2985 wcache_save_password_policy(domain, status, policy);
2987 return status;
2991 /* Invalidate cached user and group lists coherently */
2993 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2994 void *state)
2996 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2997 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2998 tdb_delete(the_tdb, kbuf);
3000 return 0;
3003 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3005 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3006 const struct dom_sid *sid)
3008 fstring key_str, sid_string;
3009 struct winbind_cache *cache;
3011 /* dont clear cached U/SID and UG/SID entries when we want to logon
3012 * offline - gd */
3014 if (lp_winbind_offline_logon()) {
3015 return;
3018 if (!domain)
3019 return;
3021 cache = get_cache(domain);
3023 if (!cache->tdb) {
3024 return;
3027 /* Clear U/SID cache entry */
3028 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3029 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3030 tdb_delete(cache->tdb, string_tdb_data(key_str));
3032 /* Clear UG/SID cache entry */
3033 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3034 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3035 tdb_delete(cache->tdb, string_tdb_data(key_str));
3037 /* Samba/winbindd never needs this. */
3038 netsamlogon_clear_cached_user(sid);
3041 bool wcache_invalidate_cache(void)
3043 struct winbindd_domain *domain;
3045 for (domain = domain_list(); domain; domain = domain->next) {
3046 struct winbind_cache *cache = get_cache(domain);
3048 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3049 "entries for %s\n", domain->name));
3050 if (cache) {
3051 if (cache->tdb) {
3052 tdb_traverse(cache->tdb, traverse_fn, NULL);
3053 } else {
3054 return false;
3058 return true;
3061 bool wcache_invalidate_cache_noinit(void)
3063 struct winbindd_domain *domain;
3065 for (domain = domain_list(); domain; domain = domain->next) {
3066 struct winbind_cache *cache;
3068 /* Skip uninitialized domains. */
3069 if (!domain->initialized && !domain->internal) {
3070 continue;
3073 cache = get_cache(domain);
3075 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3076 "entries for %s\n", domain->name));
3077 if (cache) {
3078 if (cache->tdb) {
3079 tdb_traverse(cache->tdb, traverse_fn, NULL);
3081 * Flushing cache has nothing to with domains.
3082 * return here if we successfully flushed once.
3083 * To avoid unnecessary traversing the cache.
3085 return true;
3086 } else {
3087 return false;
3091 return true;
3094 bool init_wcache(void)
3096 if (wcache == NULL) {
3097 wcache = SMB_XMALLOC_P(struct winbind_cache);
3098 ZERO_STRUCTP(wcache);
3101 if (wcache->tdb != NULL)
3102 return true;
3104 /* when working offline we must not clear the cache on restart */
3105 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3106 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3107 TDB_INCOMPATIBLE_HASH |
3108 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3109 O_RDWR|O_CREAT, 0600);
3111 if (wcache->tdb == NULL) {
3112 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3113 return false;
3116 return true;
3119 /************************************************************************
3120 This is called by the parent to initialize the cache file.
3121 We don't need sophisticated locking here as we know we're the
3122 only opener.
3123 ************************************************************************/
3125 bool initialize_winbindd_cache(void)
3127 bool cache_bad = true;
3128 uint32 vers;
3130 if (!init_wcache()) {
3131 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3132 return false;
3135 /* Check version number. */
3136 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3137 vers == WINBINDD_CACHE_VERSION) {
3138 cache_bad = false;
3141 if (cache_bad) {
3142 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3143 "and re-creating with version number %d\n",
3144 WINBINDD_CACHE_VERSION ));
3146 tdb_close(wcache->tdb);
3147 wcache->tdb = NULL;
3149 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3150 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3151 cache_path("winbindd_cache.tdb"),
3152 strerror(errno) ));
3153 return false;
3155 if (!init_wcache()) {
3156 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3157 "init_wcache failed.\n"));
3158 return false;
3161 /* Write the version. */
3162 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3163 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3164 tdb_errorstr(wcache->tdb) ));
3165 return false;
3169 tdb_close(wcache->tdb);
3170 wcache->tdb = NULL;
3171 return true;
3174 void close_winbindd_cache(void)
3176 if (!wcache) {
3177 return;
3179 if (wcache->tdb) {
3180 tdb_close(wcache->tdb);
3181 wcache->tdb = NULL;
3185 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3186 char **domain_name, char **name,
3187 enum lsa_SidType *type)
3189 struct winbindd_domain *domain;
3190 NTSTATUS status;
3192 domain = find_lookup_domain_from_sid(sid);
3193 if (domain == NULL) {
3194 return false;
3196 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3197 type);
3198 return NT_STATUS_IS_OK(status);
3201 bool lookup_cached_name(const char *domain_name,
3202 const char *name,
3203 struct dom_sid *sid,
3204 enum lsa_SidType *type)
3206 struct winbindd_domain *domain;
3207 NTSTATUS status;
3208 bool original_online_state;
3210 domain = find_lookup_domain_from_name(domain_name);
3211 if (domain == NULL) {
3212 return false;
3215 /* If we are doing a cached logon, temporarily set the domain
3216 offline so the cache won't expire the entry */
3218 original_online_state = domain->online;
3219 domain->online = false;
3220 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3221 domain->online = original_online_state;
3223 return NT_STATUS_IS_OK(status);
3226 void cache_name2sid(struct winbindd_domain *domain,
3227 const char *domain_name, const char *name,
3228 enum lsa_SidType type, const struct dom_sid *sid)
3230 refresh_sequence_number(domain, false);
3231 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3232 sid, type);
3236 * The original idea that this cache only contains centries has
3237 * been blurred - now other stuff gets put in here. Ensure we
3238 * ignore these things on cleanup.
3241 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3242 TDB_DATA dbuf, void *state)
3244 struct cache_entry *centry;
3246 if (is_non_centry_key(kbuf)) {
3247 return 0;
3250 centry = wcache_fetch_raw((char *)kbuf.dptr);
3251 if (!centry) {
3252 return 0;
3255 if (!NT_STATUS_IS_OK(centry->status)) {
3256 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3257 tdb_delete(the_tdb, kbuf);
3260 centry_free(centry);
3261 return 0;
3264 /* flush the cache */
3265 void wcache_flush_cache(void)
3267 if (!wcache)
3268 return;
3269 if (wcache->tdb) {
3270 tdb_close(wcache->tdb);
3271 wcache->tdb = NULL;
3273 if (!winbindd_use_cache()) {
3274 return;
3277 /* when working offline we must not clear the cache on restart */
3278 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3279 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3280 TDB_INCOMPATIBLE_HASH |
3281 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3282 O_RDWR|O_CREAT, 0600);
3284 if (!wcache->tdb) {
3285 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3286 return;
3289 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3291 DEBUG(10,("wcache_flush_cache success\n"));
3294 /* Count cached creds */
3296 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3297 void *state)
3299 int *cred_count = (int*)state;
3301 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3302 (*cred_count)++;
3304 return 0;
3307 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3309 struct winbind_cache *cache = get_cache(domain);
3311 *count = 0;
3313 if (!cache->tdb) {
3314 return NT_STATUS_INTERNAL_DB_ERROR;
3317 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3319 return NT_STATUS_OK;
3322 struct cred_list {
3323 struct cred_list *prev, *next;
3324 TDB_DATA key;
3325 fstring name;
3326 time_t created;
3328 static struct cred_list *wcache_cred_list;
3330 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3331 void *state)
3333 struct cred_list *cred;
3335 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3337 cred = SMB_MALLOC_P(struct cred_list);
3338 if (cred == NULL) {
3339 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3340 return -1;
3343 ZERO_STRUCTP(cred);
3345 /* save a copy of the key */
3347 fstrcpy(cred->name, (const char *)kbuf.dptr);
3348 DLIST_ADD(wcache_cred_list, cred);
3351 return 0;
3354 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3356 struct winbind_cache *cache = get_cache(domain);
3357 NTSTATUS status;
3358 int ret;
3359 struct cred_list *cred, *oldest = NULL;
3361 if (!cache->tdb) {
3362 return NT_STATUS_INTERNAL_DB_ERROR;
3365 /* we possibly already have an entry */
3366 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3368 fstring key_str, tmp;
3370 DEBUG(11,("we already have an entry, deleting that\n"));
3372 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3374 tdb_delete(cache->tdb, string_tdb_data(key_str));
3376 return NT_STATUS_OK;
3379 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3380 if (ret == 0) {
3381 return NT_STATUS_OK;
3382 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3383 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3386 ZERO_STRUCTP(oldest);
3388 for (cred = wcache_cred_list; cred; cred = cred->next) {
3390 TDB_DATA data;
3391 time_t t;
3393 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3394 if (!data.dptr) {
3395 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3396 cred->name));
3397 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3398 goto done;
3401 t = IVAL(data.dptr, 0);
3402 SAFE_FREE(data.dptr);
3404 if (!oldest) {
3405 oldest = SMB_MALLOC_P(struct cred_list);
3406 if (oldest == NULL) {
3407 status = NT_STATUS_NO_MEMORY;
3408 goto done;
3411 fstrcpy(oldest->name, cred->name);
3412 oldest->created = t;
3413 continue;
3416 if (t < oldest->created) {
3417 fstrcpy(oldest->name, cred->name);
3418 oldest->created = t;
3422 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3423 status = NT_STATUS_OK;
3424 } else {
3425 status = NT_STATUS_UNSUCCESSFUL;
3427 done:
3428 SAFE_FREE(wcache_cred_list);
3429 SAFE_FREE(oldest);
3431 return status;
3434 /* Change the global online/offline state. */
3435 bool set_global_winbindd_state_offline(void)
3437 TDB_DATA data;
3439 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3441 /* Only go offline if someone has created
3442 the key "WINBINDD_OFFLINE" in the cache tdb. */
3444 if (wcache == NULL || wcache->tdb == NULL) {
3445 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3446 return false;
3449 if (!lp_winbind_offline_logon()) {
3450 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3451 return false;
3454 if (global_winbindd_offline_state) {
3455 /* Already offline. */
3456 return true;
3459 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3461 if (!data.dptr || data.dsize != 4) {
3462 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3463 SAFE_FREE(data.dptr);
3464 return false;
3465 } else {
3466 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3467 global_winbindd_offline_state = true;
3468 SAFE_FREE(data.dptr);
3469 return true;
3473 void set_global_winbindd_state_online(void)
3475 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3477 if (!lp_winbind_offline_logon()) {
3478 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3479 return;
3482 if (!global_winbindd_offline_state) {
3483 /* Already online. */
3484 return;
3486 global_winbindd_offline_state = false;
3488 if (!wcache->tdb) {
3489 return;
3492 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3493 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3496 bool get_global_winbindd_state_offline(void)
3498 return global_winbindd_offline_state;
3501 /***********************************************************************
3502 Validate functions for all possible cache tdb keys.
3503 ***********************************************************************/
3505 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3506 struct tdb_validation_status *state)
3508 struct cache_entry *centry;
3510 centry = SMB_XMALLOC_P(struct cache_entry);
3511 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3512 if (!centry->data) {
3513 SAFE_FREE(centry);
3514 return NULL;
3516 centry->len = data.dsize;
3517 centry->ofs = 0;
3519 if (centry->len < 16) {
3520 /* huh? corrupt cache? */
3521 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3522 "(len < 16) ?\n", kstr));
3523 centry_free(centry);
3524 state->bad_entry = true;
3525 state->success = false;
3526 return NULL;
3529 centry->status = NT_STATUS(centry_uint32(centry));
3530 centry->sequence_number = centry_uint32(centry);
3531 centry->timeout = centry_uint64_t(centry);
3532 return centry;
3535 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3536 struct tdb_validation_status *state)
3538 if (dbuf.dsize != 8) {
3539 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3540 keystr, (unsigned int)dbuf.dsize ));
3541 state->bad_entry = true;
3542 return 1;
3544 return 0;
3547 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3548 struct tdb_validation_status *state)
3550 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3551 if (!centry) {
3552 return 1;
3555 (void)centry_uint32(centry);
3556 if (NT_STATUS_IS_OK(centry->status)) {
3557 struct dom_sid sid;
3558 (void)centry_sid(centry, &sid);
3561 centry_free(centry);
3563 if (!(state->success)) {
3564 return 1;
3566 DEBUG(10,("validate_ns: %s ok\n", keystr));
3567 return 0;
3570 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3571 struct tdb_validation_status *state)
3573 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3574 if (!centry) {
3575 return 1;
3578 if (NT_STATUS_IS_OK(centry->status)) {
3579 (void)centry_uint32(centry);
3580 (void)centry_string(centry, mem_ctx);
3581 (void)centry_string(centry, mem_ctx);
3584 centry_free(centry);
3586 if (!(state->success)) {
3587 return 1;
3589 DEBUG(10,("validate_sn: %s ok\n", keystr));
3590 return 0;
3593 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3594 struct tdb_validation_status *state)
3596 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3597 struct dom_sid sid;
3599 if (!centry) {
3600 return 1;
3603 (void)centry_string(centry, mem_ctx);
3604 (void)centry_string(centry, mem_ctx);
3605 (void)centry_string(centry, mem_ctx);
3606 (void)centry_string(centry, mem_ctx);
3607 (void)centry_uint32(centry);
3608 (void)centry_sid(centry, &sid);
3609 (void)centry_sid(centry, &sid);
3611 centry_free(centry);
3613 if (!(state->success)) {
3614 return 1;
3616 DEBUG(10,("validate_u: %s ok\n", keystr));
3617 return 0;
3620 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3621 struct tdb_validation_status *state)
3623 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3625 if (!centry) {
3626 return 1;
3629 (void)centry_nttime(centry);
3630 (void)centry_nttime(centry);
3631 (void)centry_uint16(centry);
3633 centry_free(centry);
3635 if (!(state->success)) {
3636 return 1;
3638 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3639 return 0;
3642 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3643 struct tdb_validation_status *state)
3645 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3647 if (!centry) {
3648 return 1;
3651 (void)centry_uint16(centry);
3652 (void)centry_uint16(centry);
3653 (void)centry_uint32(centry);
3654 (void)centry_nttime(centry);
3655 (void)centry_nttime(centry);
3657 centry_free(centry);
3659 if (!(state->success)) {
3660 return 1;
3662 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3663 return 0;
3666 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3667 struct tdb_validation_status *state)
3669 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3671 if (!centry) {
3672 return 1;
3675 (void)centry_time(centry);
3676 (void)centry_hash16(centry, mem_ctx);
3678 /* We only have 17 bytes more data in the salted cred case. */
3679 if (centry->len - centry->ofs == 17) {
3680 (void)centry_hash16(centry, mem_ctx);
3683 centry_free(centry);
3685 if (!(state->success)) {
3686 return 1;
3688 DEBUG(10,("validate_cred: %s ok\n", keystr));
3689 return 0;
3692 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3693 struct tdb_validation_status *state)
3695 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3696 int32 num_entries, i;
3698 if (!centry) {
3699 return 1;
3702 num_entries = (int32)centry_uint32(centry);
3704 for (i=0; i< num_entries; i++) {
3705 struct dom_sid sid;
3706 (void)centry_string(centry, mem_ctx);
3707 (void)centry_string(centry, mem_ctx);
3708 (void)centry_string(centry, mem_ctx);
3709 (void)centry_string(centry, mem_ctx);
3710 (void)centry_sid(centry, &sid);
3711 (void)centry_sid(centry, &sid);
3714 centry_free(centry);
3716 if (!(state->success)) {
3717 return 1;
3719 DEBUG(10,("validate_ul: %s ok\n", keystr));
3720 return 0;
3723 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3724 struct tdb_validation_status *state)
3726 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3727 int32 num_entries, i;
3729 if (!centry) {
3730 return 1;
3733 num_entries = centry_uint32(centry);
3735 for (i=0; i< num_entries; i++) {
3736 (void)centry_string(centry, mem_ctx);
3737 (void)centry_string(centry, mem_ctx);
3738 (void)centry_uint32(centry);
3741 centry_free(centry);
3743 if (!(state->success)) {
3744 return 1;
3746 DEBUG(10,("validate_gl: %s ok\n", keystr));
3747 return 0;
3750 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3751 struct tdb_validation_status *state)
3753 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3754 int32 num_groups, i;
3756 if (!centry) {
3757 return 1;
3760 num_groups = centry_uint32(centry);
3762 for (i=0; i< num_groups; i++) {
3763 struct dom_sid sid;
3764 centry_sid(centry, &sid);
3767 centry_free(centry);
3769 if (!(state->success)) {
3770 return 1;
3772 DEBUG(10,("validate_ug: %s ok\n", keystr));
3773 return 0;
3776 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3777 struct tdb_validation_status *state)
3779 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3780 int32 num_aliases, i;
3782 if (!centry) {
3783 return 1;
3786 num_aliases = centry_uint32(centry);
3788 for (i=0; i < num_aliases; i++) {
3789 (void)centry_uint32(centry);
3792 centry_free(centry);
3794 if (!(state->success)) {
3795 return 1;
3797 DEBUG(10,("validate_ua: %s ok\n", keystr));
3798 return 0;
3801 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3802 struct tdb_validation_status *state)
3804 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3805 int32 num_names, i;
3807 if (!centry) {
3808 return 1;
3811 num_names = centry_uint32(centry);
3813 for (i=0; i< num_names; i++) {
3814 struct dom_sid sid;
3815 centry_sid(centry, &sid);
3816 (void)centry_string(centry, mem_ctx);
3817 (void)centry_uint32(centry);
3820 centry_free(centry);
3822 if (!(state->success)) {
3823 return 1;
3825 DEBUG(10,("validate_gm: %s ok\n", keystr));
3826 return 0;
3829 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3830 struct tdb_validation_status *state)
3832 /* Can't say anything about this other than must be nonzero. */
3833 if (dbuf.dsize == 0) {
3834 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3835 keystr));
3836 state->bad_entry = true;
3837 state->success = false;
3838 return 1;
3841 DEBUG(10,("validate_dr: %s ok\n", keystr));
3842 return 0;
3845 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3846 struct tdb_validation_status *state)
3848 /* Can't say anything about this other than must be nonzero. */
3849 if (dbuf.dsize == 0) {
3850 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3851 keystr));
3852 state->bad_entry = true;
3853 state->success = false;
3854 return 1;
3857 DEBUG(10,("validate_de: %s ok\n", keystr));
3858 return 0;
3861 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3862 TDB_DATA dbuf, struct tdb_validation_status *state)
3864 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3866 if (!centry) {
3867 return 1;
3870 (void)centry_string(centry, mem_ctx);
3871 (void)centry_string(centry, mem_ctx);
3872 (void)centry_string(centry, mem_ctx);
3873 (void)centry_uint32(centry);
3875 centry_free(centry);
3877 if (!(state->success)) {
3878 return 1;
3880 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3881 return 0;
3884 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3885 TDB_DATA dbuf,
3886 struct tdb_validation_status *state)
3888 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3890 if (!centry) {
3891 return 1;
3894 (void)centry_string( centry, mem_ctx );
3896 centry_free(centry);
3898 if (!(state->success)) {
3899 return 1;
3901 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3902 return 0;
3905 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3906 TDB_DATA dbuf,
3907 struct tdb_validation_status *state)
3909 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3911 if (!centry) {
3912 return 1;
3915 (void)centry_string( centry, mem_ctx );
3917 centry_free(centry);
3919 if (!(state->success)) {
3920 return 1;
3922 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3923 return 0;
3926 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3927 TDB_DATA dbuf,
3928 struct tdb_validation_status *state)
3930 if (dbuf.dsize == 0) {
3931 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3932 "key %s (len ==0) ?\n", keystr));
3933 state->bad_entry = true;
3934 state->success = false;
3935 return 1;
3938 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3939 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3940 return 0;
3943 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3944 struct tdb_validation_status *state)
3946 if (dbuf.dsize != 4) {
3947 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3948 keystr, (unsigned int)dbuf.dsize ));
3949 state->bad_entry = true;
3950 state->success = false;
3951 return 1;
3953 DEBUG(10,("validate_offline: %s ok\n", keystr));
3954 return 0;
3957 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3958 struct tdb_validation_status *state)
3961 * Ignore validation for now. The proper way to do this is with a
3962 * checksum. Just pure parsing does not really catch much.
3964 return 0;
3967 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3968 struct tdb_validation_status *state)
3970 if (dbuf.dsize != 4) {
3971 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3972 "key %s (len %u != 4) ?\n",
3973 keystr, (unsigned int)dbuf.dsize));
3974 state->bad_entry = true;
3975 state->success = false;
3976 return 1;
3979 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3980 return 0;
3983 /***********************************************************************
3984 A list of all possible cache tdb keys with associated validation
3985 functions.
3986 ***********************************************************************/
3988 struct key_val_struct {
3989 const char *keyname;
3990 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3991 } key_val[] = {
3992 {"SEQNUM/", validate_seqnum},
3993 {"NS/", validate_ns},
3994 {"SN/", validate_sn},
3995 {"U/", validate_u},
3996 {"LOC_POL/", validate_loc_pol},
3997 {"PWD_POL/", validate_pwd_pol},
3998 {"CRED/", validate_cred},
3999 {"UL/", validate_ul},
4000 {"GL/", validate_gl},
4001 {"UG/", validate_ug},
4002 {"UA", validate_ua},
4003 {"GM/", validate_gm},
4004 {"DR/", validate_dr},
4005 {"DE/", validate_de},
4006 {"NSS/PWINFO/", validate_pwinfo},
4007 {"TRUSTDOMCACHE/", validate_trustdomcache},
4008 {"NSS/NA/", validate_nss_na},
4009 {"NSS/AN/", validate_nss_an},
4010 {"WINBINDD_OFFLINE", validate_offline},
4011 {"NDR/", validate_ndr},
4012 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4013 {NULL, NULL}
4016 /***********************************************************************
4017 Function to look at every entry in the tdb and validate it as far as
4018 possible.
4019 ***********************************************************************/
4021 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4023 int i;
4024 unsigned int max_key_len = 1024;
4025 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4027 /* Paranoia check. */
4028 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4029 max_key_len = 1024 * 1024;
4031 if (kbuf.dsize > max_key_len) {
4032 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4033 "(%u) > (%u)\n\n",
4034 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4035 return 1;
4038 for (i = 0; key_val[i].keyname; i++) {
4039 size_t namelen = strlen(key_val[i].keyname);
4040 if (kbuf.dsize >= namelen && (
4041 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4042 TALLOC_CTX *mem_ctx;
4043 char *keystr;
4044 int ret;
4046 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4047 if (!keystr) {
4048 return 1;
4050 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4051 keystr[kbuf.dsize] = '\0';
4053 mem_ctx = talloc_init("validate_ctx");
4054 if (!mem_ctx) {
4055 SAFE_FREE(keystr);
4056 return 1;
4059 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4060 v_state);
4062 SAFE_FREE(keystr);
4063 talloc_destroy(mem_ctx);
4064 return ret;
4068 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4069 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4070 DEBUG(0,("data :\n"));
4071 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4072 v_state->unknown_key = true;
4073 v_state->success = false;
4074 return 1; /* terminate. */
4077 static void validate_panic(const char *const why)
4079 DEBUG(0,("validating cache: would panic %s\n", why ));
4080 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4081 exit(47);
4084 /***********************************************************************
4085 Try and validate every entry in the winbindd cache. If we fail here,
4086 delete the cache tdb and return non-zero.
4087 ***********************************************************************/
4089 int winbindd_validate_cache(void)
4091 int ret = -1;
4092 const char *tdb_path = cache_path("winbindd_cache.tdb");
4093 TDB_CONTEXT *tdb = NULL;
4095 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4096 smb_panic_fn = validate_panic;
4099 tdb = tdb_open_log(tdb_path,
4100 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4101 TDB_INCOMPATIBLE_HASH |
4102 ( lp_winbind_offline_logon()
4103 ? TDB_DEFAULT
4104 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4105 O_RDWR|O_CREAT,
4106 0600);
4107 if (!tdb) {
4108 DEBUG(0, ("winbindd_validate_cache: "
4109 "error opening/initializing tdb\n"));
4110 goto done;
4112 tdb_close(tdb);
4114 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4116 if (ret != 0) {
4117 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4118 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4119 unlink(tdb_path);
4122 done:
4123 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4124 smb_panic_fn = smb_panic;
4125 return ret;
4128 /***********************************************************************
4129 Try and validate every entry in the winbindd cache.
4130 ***********************************************************************/
4132 int winbindd_validate_cache_nobackup(void)
4134 int ret = -1;
4135 const char *tdb_path = cache_path("winbindd_cache.tdb");
4137 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4138 smb_panic_fn = validate_panic;
4141 if (wcache == NULL || wcache->tdb == NULL) {
4142 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4143 } else {
4144 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4147 if (ret != 0) {
4148 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4149 "successful.\n"));
4152 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4153 "function\n"));
4154 smb_panic_fn = smb_panic;
4155 return ret;
4158 bool winbindd_cache_validate_and_initialize(void)
4160 close_winbindd_cache();
4162 if (lp_winbind_offline_logon()) {
4163 if (winbindd_validate_cache() < 0) {
4164 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4165 "could be restored.\n"));
4169 return initialize_winbindd_cache();
4172 /*********************************************************************
4173 ********************************************************************/
4175 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4176 struct winbindd_tdc_domain **domains,
4177 size_t *num_domains )
4179 struct winbindd_tdc_domain *list = NULL;
4180 size_t idx;
4181 int i;
4182 bool set_only = false;
4184 /* don't allow duplicates */
4186 idx = *num_domains;
4187 list = *domains;
4189 for ( i=0; i< (*num_domains); i++ ) {
4190 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4191 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4192 new_dom->name));
4193 idx = i;
4194 set_only = true;
4196 break;
4200 if ( !set_only ) {
4201 if ( !*domains ) {
4202 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4203 idx = 0;
4204 } else {
4205 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4206 struct winbindd_tdc_domain,
4207 (*num_domains)+1);
4208 idx = *num_domains;
4211 ZERO_STRUCT( list[idx] );
4214 if ( !list )
4215 return false;
4217 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4218 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4220 if ( !is_null_sid( &new_dom->sid ) ) {
4221 sid_copy( &list[idx].sid, &new_dom->sid );
4222 } else {
4223 sid_copy(&list[idx].sid, &global_sid_NULL);
4226 if ( new_dom->domain_flags != 0x0 )
4227 list[idx].trust_flags = new_dom->domain_flags;
4229 if ( new_dom->domain_type != 0x0 )
4230 list[idx].trust_type = new_dom->domain_type;
4232 if ( new_dom->domain_trust_attribs != 0x0 )
4233 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4235 if ( !set_only ) {
4236 *domains = list;
4237 *num_domains = idx + 1;
4240 return true;
4243 /*********************************************************************
4244 ********************************************************************/
4246 static TDB_DATA make_tdc_key( const char *domain_name )
4248 char *keystr = NULL;
4249 TDB_DATA key = { NULL, 0 };
4251 if ( !domain_name ) {
4252 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4253 return key;
4256 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4257 return key;
4259 key = string_term_tdb_data(keystr);
4261 return key;
4264 /*********************************************************************
4265 ********************************************************************/
4267 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4268 size_t num_domains,
4269 unsigned char **buf )
4271 unsigned char *buffer = NULL;
4272 int len = 0;
4273 int buflen = 0;
4274 int i = 0;
4276 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4277 (int)num_domains));
4279 buflen = 0;
4281 again:
4282 len = 0;
4284 /* Store the number of array items first */
4285 len += tdb_pack( buffer+len, buflen-len, "d",
4286 num_domains );
4288 /* now pack each domain trust record */
4289 for ( i=0; i<num_domains; i++ ) {
4291 fstring tmp;
4293 if ( buflen > 0 ) {
4294 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4295 domains[i].domain_name,
4296 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4299 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4300 domains[i].domain_name,
4301 domains[i].dns_name,
4302 sid_to_fstring(tmp, &domains[i].sid),
4303 domains[i].trust_flags,
4304 domains[i].trust_attribs,
4305 domains[i].trust_type );
4308 if ( buflen < len ) {
4309 SAFE_FREE(buffer);
4310 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4311 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4312 buflen = -1;
4313 goto done;
4315 buflen = len;
4316 goto again;
4319 *buf = buffer;
4321 done:
4322 return buflen;
4325 /*********************************************************************
4326 ********************************************************************/
4328 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4329 struct winbindd_tdc_domain **domains )
4331 fstring domain_name, dns_name, sid_string;
4332 uint32 type, attribs, flags;
4333 int num_domains;
4334 int len = 0;
4335 int i;
4336 struct winbindd_tdc_domain *list = NULL;
4338 /* get the number of domains */
4339 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4340 if ( len == -1 ) {
4341 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4342 return 0;
4345 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4346 if ( !list ) {
4347 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4348 return 0;
4351 for ( i=0; i<num_domains; i++ ) {
4352 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4353 domain_name,
4354 dns_name,
4355 sid_string,
4356 &flags,
4357 &attribs,
4358 &type );
4360 if ( len == -1 ) {
4361 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4362 TALLOC_FREE( list );
4363 return 0;
4366 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4367 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4368 domain_name, dns_name, sid_string,
4369 flags, attribs, type));
4371 list[i].domain_name = talloc_strdup( list, domain_name );
4372 list[i].dns_name = talloc_strdup( list, dns_name );
4373 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4374 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4375 domain_name));
4377 list[i].trust_flags = flags;
4378 list[i].trust_attribs = attribs;
4379 list[i].trust_type = type;
4382 *domains = list;
4384 return num_domains;
4387 /*********************************************************************
4388 ********************************************************************/
4390 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4392 TDB_DATA key = make_tdc_key( lp_workgroup() );
4393 TDB_DATA data = { NULL, 0 };
4394 int ret;
4396 if ( !key.dptr )
4397 return false;
4399 /* See if we were asked to delete the cache entry */
4401 if ( !domains ) {
4402 ret = tdb_delete( wcache->tdb, key );
4403 goto done;
4406 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4408 if ( !data.dptr ) {
4409 ret = -1;
4410 goto done;
4413 ret = tdb_store( wcache->tdb, key, data, 0 );
4415 done:
4416 SAFE_FREE( data.dptr );
4417 SAFE_FREE( key.dptr );
4419 return ( ret != -1 );
4422 /*********************************************************************
4423 ********************************************************************/
4425 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4427 TDB_DATA key = make_tdc_key( lp_workgroup() );
4428 TDB_DATA data = { NULL, 0 };
4430 *domains = NULL;
4431 *num_domains = 0;
4433 if ( !key.dptr )
4434 return false;
4436 data = tdb_fetch( wcache->tdb, key );
4438 SAFE_FREE( key.dptr );
4440 if ( !data.dptr )
4441 return false;
4443 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4445 SAFE_FREE( data.dptr );
4447 if ( !*domains )
4448 return false;
4450 return true;
4453 /*********************************************************************
4454 ********************************************************************/
4456 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4458 struct winbindd_tdc_domain *dom_list = NULL;
4459 size_t num_domains = 0;
4460 bool ret = false;
4462 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4463 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4464 domain->name, domain->alt_name,
4465 sid_string_dbg(&domain->sid),
4466 domain->domain_flags,
4467 domain->domain_trust_attribs,
4468 domain->domain_type));
4470 if ( !init_wcache() ) {
4471 return false;
4474 /* fetch the list */
4476 wcache_tdc_fetch_list( &dom_list, &num_domains );
4478 /* add the new domain */
4480 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4481 goto done;
4484 /* pack the domain */
4486 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4487 goto done;
4490 /* Success */
4492 ret = true;
4493 done:
4494 TALLOC_FREE( dom_list );
4496 return ret;
4499 /*********************************************************************
4500 ********************************************************************/
4502 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4504 struct winbindd_tdc_domain *dom_list = NULL;
4505 size_t num_domains = 0;
4506 int i;
4507 struct winbindd_tdc_domain *d = NULL;
4509 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4511 if ( !init_wcache() ) {
4512 return false;
4515 /* fetch the list */
4517 wcache_tdc_fetch_list( &dom_list, &num_domains );
4519 for ( i=0; i<num_domains; i++ ) {
4520 if ( strequal(name, dom_list[i].domain_name) ||
4521 strequal(name, dom_list[i].dns_name) )
4523 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4524 name));
4526 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4527 if ( !d )
4528 break;
4530 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4531 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4532 sid_copy( &d->sid, &dom_list[i].sid );
4533 d->trust_flags = dom_list[i].trust_flags;
4534 d->trust_type = dom_list[i].trust_type;
4535 d->trust_attribs = dom_list[i].trust_attribs;
4537 break;
4541 TALLOC_FREE( dom_list );
4543 return d;
4546 /*********************************************************************
4547 ********************************************************************/
4549 struct winbindd_tdc_domain*
4550 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4551 const struct dom_sid *sid)
4553 struct winbindd_tdc_domain *dom_list = NULL;
4554 size_t num_domains = 0;
4555 int i;
4556 struct winbindd_tdc_domain *d = NULL;
4558 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4559 sid_string_dbg(sid)));
4561 if (!init_wcache()) {
4562 return false;
4565 /* fetch the list */
4567 wcache_tdc_fetch_list(&dom_list, &num_domains);
4569 for (i = 0; i<num_domains; i++) {
4570 if (sid_equal(sid, &(dom_list[i].sid))) {
4571 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4572 "Found domain %s for SID %s\n",
4573 dom_list[i].domain_name,
4574 sid_string_dbg(sid)));
4576 d = TALLOC_P(ctx, struct winbindd_tdc_domain);
4577 if (!d)
4578 break;
4580 d->domain_name = talloc_strdup(d,
4581 dom_list[i].domain_name);
4583 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4584 sid_copy(&d->sid, &dom_list[i].sid);
4585 d->trust_flags = dom_list[i].trust_flags;
4586 d->trust_type = dom_list[i].trust_type;
4587 d->trust_attribs = dom_list[i].trust_attribs;
4589 break;
4593 TALLOC_FREE(dom_list);
4595 return d;
4599 /*********************************************************************
4600 ********************************************************************/
4602 void wcache_tdc_clear( void )
4604 if ( !init_wcache() )
4605 return;
4607 wcache_tdc_store_list( NULL, 0 );
4609 return;
4613 /*********************************************************************
4614 ********************************************************************/
4616 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4617 NTSTATUS status,
4618 const struct dom_sid *user_sid,
4619 const char *homedir,
4620 const char *shell,
4621 const char *gecos,
4622 uint32 gid)
4624 struct cache_entry *centry;
4625 fstring tmp;
4627 if ( (centry = centry_start(domain, status)) == NULL )
4628 return;
4630 centry_put_string( centry, homedir );
4631 centry_put_string( centry, shell );
4632 centry_put_string( centry, gecos );
4633 centry_put_uint32( centry, gid );
4635 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4637 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4639 centry_free(centry);
4642 #ifdef HAVE_ADS
4644 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4645 const struct dom_sid *user_sid,
4646 TALLOC_CTX *ctx,
4647 const char **homedir, const char **shell,
4648 const char **gecos, gid_t *p_gid)
4650 struct winbind_cache *cache = get_cache(domain);
4651 struct cache_entry *centry = NULL;
4652 NTSTATUS nt_status;
4653 fstring tmp;
4655 if (!cache->tdb)
4656 goto do_query;
4658 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4659 sid_to_fstring(tmp, user_sid));
4661 if (!centry)
4662 goto do_query;
4664 *homedir = centry_string( centry, ctx );
4665 *shell = centry_string( centry, ctx );
4666 *gecos = centry_string( centry, ctx );
4667 *p_gid = centry_uint32( centry );
4669 centry_free(centry);
4671 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4672 sid_string_dbg(user_sid)));
4674 return NT_STATUS_OK;
4676 do_query:
4678 nt_status = nss_get_info( domain->name, user_sid, ctx,
4679 homedir, shell, gecos, p_gid );
4681 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4683 if ( NT_STATUS_IS_OK(nt_status) ) {
4684 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4685 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4686 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4687 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4689 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4690 *homedir, *shell, *gecos, *p_gid );
4693 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4694 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4695 domain->name ));
4696 set_domain_offline( domain );
4699 return nt_status;
4702 #endif
4704 /* the cache backend methods are exposed via this structure */
4705 struct winbindd_methods cache_methods = {
4706 true,
4707 query_user_list,
4708 enum_dom_groups,
4709 enum_local_groups,
4710 name_to_sid,
4711 sid_to_name,
4712 rids_to_names,
4713 query_user,
4714 lookup_usergroups,
4715 lookup_useraliases,
4716 lookup_groupmem,
4717 sequence_number,
4718 lockout_policy,
4719 password_policy,
4720 trusted_domains
4723 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4724 uint32_t opnum, const DATA_BLOB *req,
4725 TDB_DATA *pkey)
4727 char *key;
4728 size_t keylen;
4730 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4731 if (key == NULL) {
4732 return false;
4734 keylen = talloc_get_size(key) - 1;
4736 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4737 if (key == NULL) {
4738 return false;
4740 memcpy(key + keylen, req->data, req->length);
4742 pkey->dptr = (uint8_t *)key;
4743 pkey->dsize = talloc_get_size(key);
4744 return true;
4747 static bool wcache_opnum_cacheable(uint32_t opnum)
4749 switch (opnum) {
4750 case NDR_WBINT_PING:
4751 case NDR_WBINT_QUERYSEQUENCENUMBER:
4752 case NDR_WBINT_ALLOCATEUID:
4753 case NDR_WBINT_ALLOCATEGID:
4754 case NDR_WBINT_CHECKMACHINEACCOUNT:
4755 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4756 case NDR_WBINT_PINGDC:
4757 return false;
4759 return true;
4762 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4763 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4765 TDB_DATA key, data;
4766 bool ret = false;
4768 if (!wcache_opnum_cacheable(opnum) ||
4769 is_my_own_sam_domain(domain) ||
4770 is_builtin_domain(domain)) {
4771 return false;
4774 if (wcache->tdb == NULL) {
4775 return false;
4778 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4779 return false;
4781 data = tdb_fetch(wcache->tdb, key);
4782 TALLOC_FREE(key.dptr);
4784 if (data.dptr == NULL) {
4785 return false;
4787 if (data.dsize < 12) {
4788 goto fail;
4791 if (!is_domain_offline(domain)) {
4792 uint32_t entry_seqnum, dom_seqnum, last_check;
4793 uint64_t entry_timeout;
4795 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4796 &last_check)) {
4797 goto fail;
4799 entry_seqnum = IVAL(data.dptr, 0);
4800 if (entry_seqnum != dom_seqnum) {
4801 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4802 (int)entry_seqnum));
4803 goto fail;
4805 entry_timeout = BVAL(data.dptr, 4);
4806 if (entry_timeout > time(NULL)) {
4807 DEBUG(10, ("Entry has timed out\n"));
4808 goto fail;
4812 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4813 data.dsize - 12);
4814 if (resp->data == NULL) {
4815 DEBUG(10, ("talloc failed\n"));
4816 goto fail;
4818 resp->length = data.dsize - 12;
4820 ret = true;
4821 fail:
4822 SAFE_FREE(data.dptr);
4823 return ret;
4826 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4827 const DATA_BLOB *req, const DATA_BLOB *resp)
4829 TDB_DATA key, data;
4830 uint32_t dom_seqnum, last_check;
4831 uint64_t timeout;
4833 if (!wcache_opnum_cacheable(opnum) ||
4834 is_my_own_sam_domain(domain) ||
4835 is_builtin_domain(domain)) {
4836 return;
4839 if (wcache->tdb == NULL) {
4840 return;
4843 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4844 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4845 domain->name));
4846 return;
4849 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4850 return;
4853 timeout = time(NULL) + lp_winbind_cache_time();
4855 data.dsize = resp->length + 12;
4856 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4857 if (data.dptr == NULL) {
4858 goto done;
4861 SIVAL(data.dptr, 0, dom_seqnum);
4862 SBVAL(data.dptr, 4, timeout);
4863 memcpy(data.dptr + 12, resp->data, resp->length);
4865 tdb_store(wcache->tdb, key, data, 0);
4867 done:
4868 TALLOC_FREE(key.dptr);
4869 return;