s3-torture/denytest.c: replace cli_read_old() with cli_read()
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob58a8e710f6f6b336212bb199109147bf7509f15b
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 != 0) {
488 DEBUG(10, ("tdb_store_bystring failed: %s\n",
489 tdb_errorstr_compat(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_compat(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_compat(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 (!winbindd_use_cache()) {
1300 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1303 if (!cache->tdb) {
1304 return NT_STATUS_INTERNAL_DB_ERROR;
1307 if (is_null_sid(sid)) {
1308 return NT_STATUS_INVALID_SID;
1311 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1312 return NT_STATUS_INVALID_SID;
1315 /* Try and get a salted cred first. If we can't
1316 fall back to an unsalted cred. */
1318 centry = wcache_fetch(cache, domain, "CRED/%s",
1319 sid_to_fstring(tmp, sid));
1320 if (!centry) {
1321 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1322 sid_string_dbg(sid)));
1323 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1326 t = centry_time(centry);
1328 /* In the salted case this isn't actually the nt_hash itself,
1329 but the MD5 of the salt + nt_hash. Let the caller
1330 sort this out. It can tell as we only return the cached_salt
1331 if we are returning a salted cred. */
1333 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1334 if (*cached_nt_pass == NULL) {
1335 fstring sidstr;
1337 sid_to_fstring(sidstr, sid);
1339 /* Bad (old) cred cache. Delete and pretend we
1340 don't have it. */
1341 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1342 sidstr));
1343 wcache_delete("CRED/%s", sidstr);
1344 centry_free(centry);
1345 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1348 /* We only have 17 bytes more data in the salted cred case. */
1349 if (centry->len - centry->ofs == 17) {
1350 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1351 } else {
1352 *cached_salt = NULL;
1355 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1356 if (*cached_salt) {
1357 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1360 status = centry->status;
1362 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1363 sid_string_dbg(sid), nt_errstr(status) ));
1365 centry_free(centry);
1366 return status;
1369 /* Store creds for a SID - only writes out new salted ones. */
1371 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1372 const struct dom_sid *sid,
1373 const uint8 nt_pass[NT_HASH_LEN])
1375 struct cache_entry *centry;
1376 fstring sid_string;
1377 uint32 rid;
1378 uint8 cred_salt[NT_HASH_LEN];
1379 uint8 salted_hash[NT_HASH_LEN];
1381 if (is_null_sid(sid)) {
1382 return NT_STATUS_INVALID_SID;
1385 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1386 return NT_STATUS_INVALID_SID;
1389 centry = centry_start(domain, NT_STATUS_OK);
1390 if (!centry) {
1391 return NT_STATUS_INTERNAL_DB_ERROR;
1394 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1396 centry_put_time(centry, time(NULL));
1398 /* Create a salt and then salt the hash. */
1399 generate_random_buffer(cred_salt, NT_HASH_LEN);
1400 E_md5hash(cred_salt, nt_pass, salted_hash);
1402 centry_put_hash16(centry, salted_hash);
1403 centry_put_hash16(centry, cred_salt);
1404 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1406 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1408 centry_free(centry);
1410 return NT_STATUS_OK;
1414 /* Query display info. This is the basic user list fn */
1415 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1416 TALLOC_CTX *mem_ctx,
1417 uint32 *num_entries,
1418 struct wbint_userinfo **info)
1420 struct winbind_cache *cache = get_cache(domain);
1421 struct cache_entry *centry = NULL;
1422 NTSTATUS status;
1423 unsigned int i, retry;
1424 bool old_status = domain->online;
1426 if (!cache->tdb)
1427 goto do_query;
1429 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1430 if (!centry)
1431 goto do_query;
1433 do_fetch_cache:
1434 *num_entries = centry_uint32(centry);
1436 if (*num_entries == 0)
1437 goto do_cached;
1439 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1440 if (! (*info)) {
1441 smb_panic_fn("query_user_list out of memory");
1443 for (i=0; i<(*num_entries); i++) {
1444 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1445 (*info)[i].full_name = centry_string(centry, mem_ctx);
1446 (*info)[i].homedir = centry_string(centry, mem_ctx);
1447 (*info)[i].shell = centry_string(centry, mem_ctx);
1448 centry_sid(centry, &(*info)[i].user_sid);
1449 centry_sid(centry, &(*info)[i].group_sid);
1452 do_cached:
1453 status = centry->status;
1455 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1456 domain->name, nt_errstr(status) ));
1458 centry_free(centry);
1459 return status;
1461 do_query:
1462 *num_entries = 0;
1463 *info = NULL;
1465 /* Return status value returned by seq number check */
1467 if (!NT_STATUS_IS_OK(domain->last_status))
1468 return domain->last_status;
1470 /* Put the query_user_list() in a retry loop. There appears to be
1471 * some bug either with Windows 2000 or Samba's handling of large
1472 * rpc replies. This manifests itself as sudden disconnection
1473 * at a random point in the enumeration of a large (60k) user list.
1474 * The retry loop simply tries the operation again. )-: It's not
1475 * pretty but an acceptable workaround until we work out what the
1476 * real problem is. */
1478 retry = 0;
1479 do {
1481 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1482 domain->name ));
1484 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1485 if (!NT_STATUS_IS_OK(status)) {
1486 DEBUG(3, ("query_user_list: returned 0x%08x, "
1487 "retrying\n", NT_STATUS_V(status)));
1489 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1490 DEBUG(3, ("query_user_list: flushing "
1491 "connection cache\n"));
1492 invalidate_cm_connection(&domain->conn);
1494 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1495 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1496 if (!domain->internal && old_status) {
1497 set_domain_offline(domain);
1499 /* store partial response. */
1500 if (*num_entries > 0) {
1502 * humm, what about the status used for cache?
1503 * Should it be NT_STATUS_OK?
1505 break;
1508 * domain is offline now, and there is no user entries,
1509 * try to fetch from cache again.
1511 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1512 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1513 /* partial response... */
1514 if (!centry) {
1515 goto skip_save;
1516 } else {
1517 goto do_fetch_cache;
1519 } else {
1520 goto skip_save;
1524 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1525 (retry++ < 5));
1527 /* and save it */
1528 refresh_sequence_number(domain, false);
1529 if (!NT_STATUS_IS_OK(status)) {
1530 return status;
1532 centry = centry_start(domain, status);
1533 if (!centry)
1534 goto skip_save;
1535 centry_put_uint32(centry, *num_entries);
1536 for (i=0; i<(*num_entries); i++) {
1537 centry_put_string(centry, (*info)[i].acct_name);
1538 centry_put_string(centry, (*info)[i].full_name);
1539 centry_put_string(centry, (*info)[i].homedir);
1540 centry_put_string(centry, (*info)[i].shell);
1541 centry_put_sid(centry, &(*info)[i].user_sid);
1542 centry_put_sid(centry, &(*info)[i].group_sid);
1543 if (domain->backend && domain->backend->consistent) {
1544 /* when the backend is consistent we can pre-prime some mappings */
1545 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1546 domain->name,
1547 (*info)[i].acct_name,
1548 &(*info)[i].user_sid,
1549 SID_NAME_USER);
1550 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1551 &(*info)[i].user_sid,
1552 domain->name,
1553 (*info)[i].acct_name,
1554 SID_NAME_USER);
1555 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1558 centry_end(centry, "UL/%s", domain->name);
1559 centry_free(centry);
1561 skip_save:
1562 return status;
1565 /* list all domain groups */
1566 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1567 TALLOC_CTX *mem_ctx,
1568 uint32 *num_entries,
1569 struct wb_acct_info **info)
1571 struct winbind_cache *cache = get_cache(domain);
1572 struct cache_entry *centry = NULL;
1573 NTSTATUS status;
1574 unsigned int i;
1575 bool old_status;
1577 old_status = domain->online;
1578 if (!cache->tdb)
1579 goto do_query;
1581 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1582 if (!centry)
1583 goto do_query;
1585 do_fetch_cache:
1586 *num_entries = centry_uint32(centry);
1588 if (*num_entries == 0)
1589 goto do_cached;
1591 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1592 if (! (*info)) {
1593 smb_panic_fn("enum_dom_groups out of memory");
1595 for (i=0; i<(*num_entries); i++) {
1596 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1597 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1598 (*info)[i].rid = centry_uint32(centry);
1601 do_cached:
1602 status = centry->status;
1604 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1605 domain->name, nt_errstr(status) ));
1607 centry_free(centry);
1608 return status;
1610 do_query:
1611 *num_entries = 0;
1612 *info = NULL;
1614 /* Return status value returned by seq number check */
1616 if (!NT_STATUS_IS_OK(domain->last_status))
1617 return domain->last_status;
1619 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1620 domain->name ));
1622 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1624 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1625 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1626 if (!domain->internal && old_status) {
1627 set_domain_offline(domain);
1629 if (cache->tdb &&
1630 !domain->online &&
1631 !domain->internal &&
1632 old_status) {
1633 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1634 if (centry) {
1635 goto do_fetch_cache;
1639 /* and save it */
1640 refresh_sequence_number(domain, false);
1641 if (!NT_STATUS_IS_OK(status)) {
1642 return status;
1644 centry = centry_start(domain, status);
1645 if (!centry)
1646 goto skip_save;
1647 centry_put_uint32(centry, *num_entries);
1648 for (i=0; i<(*num_entries); i++) {
1649 centry_put_string(centry, (*info)[i].acct_name);
1650 centry_put_string(centry, (*info)[i].acct_desc);
1651 centry_put_uint32(centry, (*info)[i].rid);
1653 centry_end(centry, "GL/%s/domain", domain->name);
1654 centry_free(centry);
1656 skip_save:
1657 return status;
1660 /* list all domain groups */
1661 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1662 TALLOC_CTX *mem_ctx,
1663 uint32 *num_entries,
1664 struct wb_acct_info **info)
1666 struct winbind_cache *cache = get_cache(domain);
1667 struct cache_entry *centry = NULL;
1668 NTSTATUS status;
1669 unsigned int i;
1670 bool old_status;
1672 old_status = domain->online;
1673 if (!cache->tdb)
1674 goto do_query;
1676 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1677 if (!centry)
1678 goto do_query;
1680 do_fetch_cache:
1681 *num_entries = centry_uint32(centry);
1683 if (*num_entries == 0)
1684 goto do_cached;
1686 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1687 if (! (*info)) {
1688 smb_panic_fn("enum_dom_groups out of memory");
1690 for (i=0; i<(*num_entries); i++) {
1691 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1692 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1693 (*info)[i].rid = centry_uint32(centry);
1696 do_cached:
1698 /* If we are returning cached data and the domain controller
1699 is down then we don't know whether the data is up to date
1700 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1701 indicate this. */
1703 if (wcache_server_down(domain)) {
1704 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1705 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1706 } else
1707 status = centry->status;
1709 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1710 domain->name, nt_errstr(status) ));
1712 centry_free(centry);
1713 return status;
1715 do_query:
1716 *num_entries = 0;
1717 *info = NULL;
1719 /* Return status value returned by seq number check */
1721 if (!NT_STATUS_IS_OK(domain->last_status))
1722 return domain->last_status;
1724 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1725 domain->name ));
1727 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1729 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1730 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1731 if (!domain->internal && old_status) {
1732 set_domain_offline(domain);
1734 if (cache->tdb &&
1735 !domain->internal &&
1736 !domain->online &&
1737 old_status) {
1738 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1739 if (centry) {
1740 goto do_fetch_cache;
1744 /* and save it */
1745 refresh_sequence_number(domain, false);
1746 if (!NT_STATUS_IS_OK(status)) {
1747 return status;
1749 centry = centry_start(domain, status);
1750 if (!centry)
1751 goto skip_save;
1752 centry_put_uint32(centry, *num_entries);
1753 for (i=0; i<(*num_entries); i++) {
1754 centry_put_string(centry, (*info)[i].acct_name);
1755 centry_put_string(centry, (*info)[i].acct_desc);
1756 centry_put_uint32(centry, (*info)[i].rid);
1758 centry_end(centry, "GL/%s/local", domain->name);
1759 centry_free(centry);
1761 skip_save:
1762 return status;
1765 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1766 const char *domain_name,
1767 const char *name,
1768 struct dom_sid *sid,
1769 enum lsa_SidType *type)
1771 struct winbind_cache *cache = get_cache(domain);
1772 struct cache_entry *centry;
1773 NTSTATUS status;
1774 char *uname;
1776 if (cache->tdb == NULL) {
1777 return NT_STATUS_NOT_FOUND;
1780 uname = talloc_strdup_upper(talloc_tos(), name);
1781 if (uname == NULL) {
1782 return NT_STATUS_NO_MEMORY;
1785 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1786 TALLOC_FREE(uname);
1787 if (centry == NULL) {
1788 return NT_STATUS_NOT_FOUND;
1791 status = centry->status;
1792 if (NT_STATUS_IS_OK(status)) {
1793 *type = (enum lsa_SidType)centry_uint32(centry);
1794 centry_sid(centry, sid);
1797 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1798 "%s\n", domain->name, nt_errstr(status) ));
1800 centry_free(centry);
1801 return status;
1804 /* convert a single name to a sid in a domain */
1805 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1806 TALLOC_CTX *mem_ctx,
1807 const char *domain_name,
1808 const char *name,
1809 uint32_t flags,
1810 struct dom_sid *sid,
1811 enum lsa_SidType *type)
1813 NTSTATUS status;
1814 bool old_status;
1816 old_status = domain->online;
1818 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1819 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1820 return status;
1823 ZERO_STRUCTP(sid);
1825 /* If the seq number check indicated that there is a problem
1826 * with this DC, then return that status... except for
1827 * access_denied. This is special because the dc may be in
1828 * "restrict anonymous = 1" mode, in which case it will deny
1829 * most unauthenticated operations, but *will* allow the LSA
1830 * name-to-sid that we try as a fallback. */
1832 if (!(NT_STATUS_IS_OK(domain->last_status)
1833 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1834 return domain->last_status;
1836 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1837 domain->name ));
1839 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1840 name, flags, sid, type);
1842 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1843 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1844 if (!domain->internal && old_status) {
1845 set_domain_offline(domain);
1847 if (!domain->internal &&
1848 !domain->online &&
1849 old_status) {
1850 NTSTATUS cache_status;
1851 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1852 return cache_status;
1855 /* and save it */
1856 refresh_sequence_number(domain, false);
1858 if (domain->online &&
1859 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1860 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1862 /* Only save the reverse mapping if this was not a UPN */
1863 if (!strchr(name, '@')) {
1864 strupper_m(discard_const_p(char, domain_name));
1865 strlower_m(discard_const_p(char, name));
1866 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1870 return status;
1873 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1874 const struct dom_sid *sid,
1875 TALLOC_CTX *mem_ctx,
1876 char **domain_name,
1877 char **name,
1878 enum lsa_SidType *type)
1880 struct winbind_cache *cache = get_cache(domain);
1881 struct cache_entry *centry;
1882 char *sid_string;
1883 NTSTATUS status;
1885 if (cache->tdb == NULL) {
1886 return NT_STATUS_NOT_FOUND;
1889 sid_string = sid_string_tos(sid);
1890 if (sid_string == NULL) {
1891 return NT_STATUS_NO_MEMORY;
1894 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1895 TALLOC_FREE(sid_string);
1896 if (centry == NULL) {
1897 return NT_STATUS_NOT_FOUND;
1900 if (NT_STATUS_IS_OK(centry->status)) {
1901 *type = (enum lsa_SidType)centry_uint32(centry);
1902 *domain_name = centry_string(centry, mem_ctx);
1903 *name = centry_string(centry, mem_ctx);
1906 status = centry->status;
1907 centry_free(centry);
1909 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1910 "%s\n", domain->name, nt_errstr(status) ));
1912 return status;
1915 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1916 given */
1917 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1918 TALLOC_CTX *mem_ctx,
1919 const struct dom_sid *sid,
1920 char **domain_name,
1921 char **name,
1922 enum lsa_SidType *type)
1924 NTSTATUS status;
1925 bool old_status;
1927 old_status = domain->online;
1928 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1929 type);
1930 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1931 return status;
1934 *name = NULL;
1935 *domain_name = NULL;
1937 /* If the seq number check indicated that there is a problem
1938 * with this DC, then return that status... except for
1939 * access_denied. This is special because the dc may be in
1940 * "restrict anonymous = 1" mode, in which case it will deny
1941 * most unauthenticated operations, but *will* allow the LSA
1942 * sid-to-name that we try as a fallback. */
1944 if (!(NT_STATUS_IS_OK(domain->last_status)
1945 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1946 return domain->last_status;
1948 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1949 domain->name ));
1951 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1953 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1954 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1955 if (!domain->internal && old_status) {
1956 set_domain_offline(domain);
1958 if (!domain->internal &&
1959 !domain->online &&
1960 old_status) {
1961 NTSTATUS cache_status;
1962 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1963 domain_name, name, type);
1964 return cache_status;
1967 /* and save it */
1968 refresh_sequence_number(domain, false);
1969 if (!NT_STATUS_IS_OK(status)) {
1970 return status;
1972 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1974 /* We can't save the name to sid mapping here, as with sid history a
1975 * later name2sid would give the wrong sid. */
1977 return status;
1980 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1981 TALLOC_CTX *mem_ctx,
1982 const struct dom_sid *domain_sid,
1983 uint32 *rids,
1984 size_t num_rids,
1985 char **domain_name,
1986 char ***names,
1987 enum lsa_SidType **types)
1989 struct winbind_cache *cache = get_cache(domain);
1990 size_t i;
1991 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1992 bool have_mapped;
1993 bool have_unmapped;
1994 bool old_status;
1996 old_status = domain->online;
1997 *domain_name = NULL;
1998 *names = NULL;
1999 *types = NULL;
2001 if (!cache->tdb) {
2002 goto do_query;
2005 if (num_rids == 0) {
2006 return NT_STATUS_OK;
2009 *names = talloc_array(mem_ctx, char *, num_rids);
2010 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2012 if ((*names == NULL) || (*types == NULL)) {
2013 result = NT_STATUS_NO_MEMORY;
2014 goto error;
2017 have_mapped = have_unmapped = false;
2019 for (i=0; i<num_rids; i++) {
2020 struct dom_sid sid;
2021 struct cache_entry *centry;
2022 fstring tmp;
2024 if (!sid_compose(&sid, domain_sid, rids[i])) {
2025 result = NT_STATUS_INTERNAL_ERROR;
2026 goto error;
2029 centry = wcache_fetch(cache, domain, "SN/%s",
2030 sid_to_fstring(tmp, &sid));
2031 if (!centry) {
2032 goto do_query;
2035 (*types)[i] = SID_NAME_UNKNOWN;
2036 (*names)[i] = talloc_strdup(*names, "");
2038 if (NT_STATUS_IS_OK(centry->status)) {
2039 char *dom;
2040 have_mapped = true;
2041 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2043 dom = centry_string(centry, mem_ctx);
2044 if (*domain_name == NULL) {
2045 *domain_name = dom;
2046 } else {
2047 talloc_free(dom);
2050 (*names)[i] = centry_string(centry, *names);
2052 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2053 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2054 have_unmapped = true;
2056 } else {
2057 /* something's definitely wrong */
2058 result = centry->status;
2059 goto error;
2062 centry_free(centry);
2065 if (!have_mapped) {
2066 return NT_STATUS_NONE_MAPPED;
2068 if (!have_unmapped) {
2069 return NT_STATUS_OK;
2071 return STATUS_SOME_UNMAPPED;
2073 do_query:
2075 TALLOC_FREE(*names);
2076 TALLOC_FREE(*types);
2078 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2079 rids, num_rids, domain_name,
2080 names, types);
2082 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2083 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2084 if (!domain->internal && old_status) {
2085 set_domain_offline(domain);
2087 if (cache->tdb &&
2088 !domain->internal &&
2089 !domain->online &&
2090 old_status) {
2091 have_mapped = have_unmapped = false;
2093 for (i=0; i<num_rids; i++) {
2094 struct dom_sid sid;
2095 struct cache_entry *centry;
2096 fstring tmp;
2098 if (!sid_compose(&sid, domain_sid, rids[i])) {
2099 result = NT_STATUS_INTERNAL_ERROR;
2100 goto error;
2103 centry = wcache_fetch(cache, domain, "SN/%s",
2104 sid_to_fstring(tmp, &sid));
2105 if (!centry) {
2106 (*types)[i] = SID_NAME_UNKNOWN;
2107 (*names)[i] = talloc_strdup(*names, "");
2108 continue;
2111 (*types)[i] = SID_NAME_UNKNOWN;
2112 (*names)[i] = talloc_strdup(*names, "");
2114 if (NT_STATUS_IS_OK(centry->status)) {
2115 char *dom;
2116 have_mapped = true;
2117 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2119 dom = centry_string(centry, mem_ctx);
2120 if (*domain_name == NULL) {
2121 *domain_name = dom;
2122 } else {
2123 talloc_free(dom);
2126 (*names)[i] = centry_string(centry, *names);
2128 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2129 have_unmapped = true;
2131 } else {
2132 /* something's definitely wrong */
2133 result = centry->status;
2134 goto error;
2137 centry_free(centry);
2140 if (!have_mapped) {
2141 return NT_STATUS_NONE_MAPPED;
2143 if (!have_unmapped) {
2144 return NT_STATUS_OK;
2146 return STATUS_SOME_UNMAPPED;
2150 None of the queried rids has been found so save all negative entries
2152 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2153 for (i = 0; i < num_rids; i++) {
2154 struct dom_sid sid;
2155 const char *name = "";
2156 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2157 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2159 if (!sid_compose(&sid, domain_sid, rids[i])) {
2160 return NT_STATUS_INTERNAL_ERROR;
2163 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2164 name, type);
2167 return result;
2171 Some or all of the queried rids have been found.
2173 if (!NT_STATUS_IS_OK(result) &&
2174 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2175 return result;
2178 refresh_sequence_number(domain, false);
2180 for (i=0; i<num_rids; i++) {
2181 struct dom_sid sid;
2182 NTSTATUS status;
2184 if (!sid_compose(&sid, domain_sid, rids[i])) {
2185 result = NT_STATUS_INTERNAL_ERROR;
2186 goto error;
2189 status = (*types)[i] == SID_NAME_UNKNOWN ?
2190 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2192 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2193 (*names)[i], (*types)[i]);
2196 return result;
2198 error:
2199 TALLOC_FREE(*names);
2200 TALLOC_FREE(*types);
2201 return result;
2204 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2205 TALLOC_CTX *mem_ctx,
2206 const struct dom_sid *user_sid,
2207 struct wbint_userinfo *info)
2209 struct winbind_cache *cache = get_cache(domain);
2210 struct cache_entry *centry = NULL;
2211 NTSTATUS status;
2212 char *sid_string;
2214 if (cache->tdb == NULL) {
2215 return NT_STATUS_NOT_FOUND;
2218 sid_string = sid_string_tos(user_sid);
2219 if (sid_string == NULL) {
2220 return NT_STATUS_NO_MEMORY;
2223 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2224 TALLOC_FREE(sid_string);
2225 if (centry == NULL) {
2226 return NT_STATUS_NOT_FOUND;
2230 * If we have an access denied cache entry and a cached info3
2231 * in the samlogon cache then do a query. This will force the
2232 * rpc back end to return the info3 data.
2235 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2236 netsamlogon_cache_have(user_sid)) {
2237 DEBUG(10, ("query_user: cached access denied and have cached "
2238 "info3\n"));
2239 domain->last_status = NT_STATUS_OK;
2240 centry_free(centry);
2241 return NT_STATUS_NOT_FOUND;
2244 /* if status is not ok then this is a negative hit
2245 and the rest of the data doesn't matter */
2246 status = centry->status;
2247 if (NT_STATUS_IS_OK(status)) {
2248 info->acct_name = centry_string(centry, mem_ctx);
2249 info->full_name = centry_string(centry, mem_ctx);
2250 info->homedir = centry_string(centry, mem_ctx);
2251 info->shell = centry_string(centry, mem_ctx);
2252 info->primary_gid = centry_uint32(centry);
2253 centry_sid(centry, &info->user_sid);
2254 centry_sid(centry, &info->group_sid);
2257 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2258 "%s\n", domain->name, nt_errstr(status) ));
2260 centry_free(centry);
2261 return status;
2264 /* Lookup user information from a rid */
2265 static NTSTATUS query_user(struct winbindd_domain *domain,
2266 TALLOC_CTX *mem_ctx,
2267 const struct dom_sid *user_sid,
2268 struct wbint_userinfo *info)
2270 NTSTATUS status;
2271 bool old_status;
2273 old_status = domain->online;
2274 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2275 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2276 return status;
2279 ZERO_STRUCTP(info);
2281 /* Return status value returned by seq number check */
2283 if (!NT_STATUS_IS_OK(domain->last_status))
2284 return domain->last_status;
2286 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2287 domain->name ));
2289 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2291 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2292 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2293 if (!domain->internal && old_status) {
2294 set_domain_offline(domain);
2296 if (!domain->internal &&
2297 !domain->online &&
2298 old_status) {
2299 NTSTATUS cache_status;
2300 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2301 return cache_status;
2304 /* and save it */
2305 refresh_sequence_number(domain, false);
2306 if (!NT_STATUS_IS_OK(status)) {
2307 return status;
2309 wcache_save_user(domain, status, info);
2311 return status;
2314 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2315 TALLOC_CTX *mem_ctx,
2316 const struct dom_sid *user_sid,
2317 uint32_t *pnum_sids,
2318 struct dom_sid **psids)
2320 struct winbind_cache *cache = get_cache(domain);
2321 struct cache_entry *centry = NULL;
2322 NTSTATUS status;
2323 uint32_t i, num_sids;
2324 struct dom_sid *sids;
2325 fstring sid_string;
2327 if (cache->tdb == NULL) {
2328 return NT_STATUS_NOT_FOUND;
2331 centry = wcache_fetch(cache, domain, "UG/%s",
2332 sid_to_fstring(sid_string, user_sid));
2333 if (centry == NULL) {
2334 return NT_STATUS_NOT_FOUND;
2337 /* If we have an access denied cache entry and a cached info3 in the
2338 samlogon cache then do a query. This will force the rpc back end
2339 to return the info3 data. */
2341 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2342 && netsamlogon_cache_have(user_sid)) {
2343 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2344 "cached info3\n"));
2345 domain->last_status = NT_STATUS_OK;
2346 centry_free(centry);
2347 return NT_STATUS_NOT_FOUND;
2350 num_sids = centry_uint32(centry);
2351 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2352 if (sids == NULL) {
2353 centry_free(centry);
2354 return NT_STATUS_NO_MEMORY;
2357 for (i=0; i<num_sids; i++) {
2358 centry_sid(centry, &sids[i]);
2361 status = centry->status;
2363 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2364 "status: %s\n", domain->name, nt_errstr(status)));
2366 centry_free(centry);
2368 *pnum_sids = num_sids;
2369 *psids = sids;
2370 return status;
2373 /* Lookup groups a user is a member of. */
2374 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2375 TALLOC_CTX *mem_ctx,
2376 const struct dom_sid *user_sid,
2377 uint32 *num_groups, struct dom_sid **user_gids)
2379 struct cache_entry *centry = NULL;
2380 NTSTATUS status;
2381 unsigned int i;
2382 fstring sid_string;
2383 bool old_status;
2385 old_status = domain->online;
2386 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2387 num_groups, user_gids);
2388 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2389 return status;
2392 (*num_groups) = 0;
2393 (*user_gids) = NULL;
2395 /* Return status value returned by seq number check */
2397 if (!NT_STATUS_IS_OK(domain->last_status))
2398 return domain->last_status;
2400 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2401 domain->name ));
2403 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2405 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2406 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2407 if (!domain->internal && old_status) {
2408 set_domain_offline(domain);
2410 if (!domain->internal &&
2411 !domain->online &&
2412 old_status) {
2413 NTSTATUS cache_status;
2414 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2415 num_groups, user_gids);
2416 return cache_status;
2419 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2420 goto skip_save;
2422 /* and save it */
2423 refresh_sequence_number(domain, false);
2424 if (!NT_STATUS_IS_OK(status)) {
2425 return status;
2427 centry = centry_start(domain, status);
2428 if (!centry)
2429 goto skip_save;
2431 centry_put_uint32(centry, *num_groups);
2432 for (i=0; i<(*num_groups); i++) {
2433 centry_put_sid(centry, &(*user_gids)[i]);
2436 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2437 centry_free(centry);
2439 skip_save:
2440 return status;
2443 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2444 const struct dom_sid *sids)
2446 uint32_t i;
2447 char *sidlist;
2449 sidlist = talloc_strdup(mem_ctx, "");
2450 if (sidlist == NULL) {
2451 return NULL;
2453 for (i=0; i<num_sids; i++) {
2454 fstring tmp;
2455 sidlist = talloc_asprintf_append_buffer(
2456 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2457 if (sidlist == NULL) {
2458 return NULL;
2461 return sidlist;
2464 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2465 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2466 const struct dom_sid *sids,
2467 uint32_t *pnum_aliases, uint32_t **paliases)
2469 struct winbind_cache *cache = get_cache(domain);
2470 struct cache_entry *centry = NULL;
2471 uint32_t num_aliases;
2472 uint32_t *aliases;
2473 NTSTATUS status;
2474 char *sidlist;
2475 int i;
2477 if (cache->tdb == NULL) {
2478 return NT_STATUS_NOT_FOUND;
2481 if (num_sids == 0) {
2482 *pnum_aliases = 0;
2483 *paliases = NULL;
2484 return NT_STATUS_OK;
2487 /* We need to cache indexed by the whole list of SIDs, the aliases
2488 * resulting might come from any of the SIDs. */
2490 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2491 if (sidlist == NULL) {
2492 return NT_STATUS_NO_MEMORY;
2495 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2496 TALLOC_FREE(sidlist);
2497 if (centry == NULL) {
2498 return NT_STATUS_NOT_FOUND;
2501 num_aliases = centry_uint32(centry);
2502 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2503 if (aliases == NULL) {
2504 centry_free(centry);
2505 return NT_STATUS_NO_MEMORY;
2508 for (i=0; i<num_aliases; i++) {
2509 aliases[i] = centry_uint32(centry);
2512 status = centry->status;
2514 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2515 "status %s\n", domain->name, nt_errstr(status)));
2517 centry_free(centry);
2519 *pnum_aliases = num_aliases;
2520 *paliases = aliases;
2522 return status;
2525 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2526 TALLOC_CTX *mem_ctx,
2527 uint32 num_sids, const struct dom_sid *sids,
2528 uint32 *num_aliases, uint32 **alias_rids)
2530 struct cache_entry *centry = NULL;
2531 NTSTATUS status;
2532 char *sidlist;
2533 int i;
2534 bool old_status;
2536 old_status = domain->online;
2537 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2538 num_aliases, alias_rids);
2539 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2540 return status;
2543 (*num_aliases) = 0;
2544 (*alias_rids) = NULL;
2546 if (!NT_STATUS_IS_OK(domain->last_status))
2547 return domain->last_status;
2549 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2550 "for domain %s\n", domain->name ));
2552 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2553 if (sidlist == NULL) {
2554 return NT_STATUS_NO_MEMORY;
2557 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2558 num_sids, sids,
2559 num_aliases, alias_rids);
2561 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2562 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2563 if (!domain->internal && old_status) {
2564 set_domain_offline(domain);
2566 if (!domain->internal &&
2567 !domain->online &&
2568 old_status) {
2569 NTSTATUS cache_status;
2570 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2571 sids, num_aliases, alias_rids);
2572 return cache_status;
2575 /* and save it */
2576 refresh_sequence_number(domain, false);
2577 if (!NT_STATUS_IS_OK(status)) {
2578 return status;
2580 centry = centry_start(domain, status);
2581 if (!centry)
2582 goto skip_save;
2583 centry_put_uint32(centry, *num_aliases);
2584 for (i=0; i<(*num_aliases); i++)
2585 centry_put_uint32(centry, (*alias_rids)[i]);
2586 centry_end(centry, "UA%s", sidlist);
2587 centry_free(centry);
2589 skip_save:
2590 return status;
2593 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2594 TALLOC_CTX *mem_ctx,
2595 const struct dom_sid *group_sid,
2596 uint32_t *num_names,
2597 struct dom_sid **sid_mem, char ***names,
2598 uint32_t **name_types)
2600 struct winbind_cache *cache = get_cache(domain);
2601 struct cache_entry *centry = NULL;
2602 NTSTATUS status;
2603 unsigned int i;
2604 char *sid_string;
2606 if (cache->tdb == NULL) {
2607 return NT_STATUS_NOT_FOUND;
2610 sid_string = sid_string_tos(group_sid);
2611 if (sid_string == NULL) {
2612 return NT_STATUS_NO_MEMORY;
2615 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2616 TALLOC_FREE(sid_string);
2617 if (centry == NULL) {
2618 return NT_STATUS_NOT_FOUND;
2621 *sid_mem = NULL;
2622 *names = NULL;
2623 *name_types = NULL;
2625 *num_names = centry_uint32(centry);
2626 if (*num_names == 0) {
2627 centry_free(centry);
2628 return NT_STATUS_OK;
2631 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2632 *names = talloc_array(mem_ctx, char *, *num_names);
2633 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2635 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2636 TALLOC_FREE(*sid_mem);
2637 TALLOC_FREE(*names);
2638 TALLOC_FREE(*name_types);
2639 centry_free(centry);
2640 return NT_STATUS_NO_MEMORY;
2643 for (i=0; i<(*num_names); i++) {
2644 centry_sid(centry, &(*sid_mem)[i]);
2645 (*names)[i] = centry_string(centry, mem_ctx);
2646 (*name_types)[i] = centry_uint32(centry);
2649 status = centry->status;
2651 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2652 "status: %s\n", domain->name, nt_errstr(status)));
2654 centry_free(centry);
2655 return status;
2658 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2659 TALLOC_CTX *mem_ctx,
2660 const struct dom_sid *group_sid,
2661 enum lsa_SidType type,
2662 uint32 *num_names,
2663 struct dom_sid **sid_mem, char ***names,
2664 uint32 **name_types)
2666 struct cache_entry *centry = NULL;
2667 NTSTATUS status;
2668 unsigned int i;
2669 fstring sid_string;
2670 bool old_status;
2672 old_status = domain->online;
2673 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2674 sid_mem, names, name_types);
2675 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2676 return status;
2679 (*num_names) = 0;
2680 (*sid_mem) = NULL;
2681 (*names) = NULL;
2682 (*name_types) = NULL;
2684 /* Return status value returned by seq number check */
2686 if (!NT_STATUS_IS_OK(domain->last_status))
2687 return domain->last_status;
2689 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2690 domain->name ));
2692 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2693 type, num_names,
2694 sid_mem, names, name_types);
2696 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2697 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2698 if (!domain->internal && old_status) {
2699 set_domain_offline(domain);
2701 if (!domain->internal &&
2702 !domain->online &&
2703 old_status) {
2704 NTSTATUS cache_status;
2705 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2706 num_names, sid_mem, names,
2707 name_types);
2708 return cache_status;
2711 /* and save it */
2712 refresh_sequence_number(domain, false);
2713 if (!NT_STATUS_IS_OK(status)) {
2714 return status;
2716 centry = centry_start(domain, status);
2717 if (!centry)
2718 goto skip_save;
2719 centry_put_uint32(centry, *num_names);
2720 for (i=0; i<(*num_names); i++) {
2721 centry_put_sid(centry, &(*sid_mem)[i]);
2722 centry_put_string(centry, (*names)[i]);
2723 centry_put_uint32(centry, (*name_types)[i]);
2725 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2726 centry_free(centry);
2728 skip_save:
2729 return status;
2732 /* find the sequence number for a domain */
2733 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2735 refresh_sequence_number(domain, false);
2737 *seq = domain->sequence_number;
2739 return NT_STATUS_OK;
2742 /* enumerate trusted domains
2743 * (we need to have the list of trustdoms in the cache when we go offline) -
2744 * Guenther */
2745 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2746 TALLOC_CTX *mem_ctx,
2747 struct netr_DomainTrustList *trusts)
2749 NTSTATUS status;
2750 struct winbind_cache *cache;
2751 struct winbindd_tdc_domain *dom_list = NULL;
2752 size_t num_domains = 0;
2753 bool retval = false;
2754 int i;
2755 bool old_status;
2757 old_status = domain->online;
2758 trusts->count = 0;
2759 trusts->array = NULL;
2761 cache = get_cache(domain);
2762 if (!cache || !cache->tdb) {
2763 goto do_query;
2766 if (domain->online) {
2767 goto do_query;
2770 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2771 if (!retval || !num_domains || !dom_list) {
2772 TALLOC_FREE(dom_list);
2773 goto do_query;
2776 do_fetch_cache:
2777 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2778 if (!trusts->array) {
2779 TALLOC_FREE(dom_list);
2780 return NT_STATUS_NO_MEMORY;
2783 for (i = 0; i < num_domains; i++) {
2784 struct netr_DomainTrust *trust;
2785 struct dom_sid *sid;
2786 struct winbindd_domain *dom;
2788 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2789 if (dom && dom->internal) {
2790 continue;
2793 trust = &trusts->array[trusts->count];
2794 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2795 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2796 sid = talloc(trusts->array, struct dom_sid);
2797 if (!trust->netbios_name || !trust->dns_name ||
2798 !sid) {
2799 TALLOC_FREE(dom_list);
2800 TALLOC_FREE(trusts->array);
2801 return NT_STATUS_NO_MEMORY;
2804 trust->trust_flags = dom_list[i].trust_flags;
2805 trust->trust_attributes = dom_list[i].trust_attribs;
2806 trust->trust_type = dom_list[i].trust_type;
2807 sid_copy(sid, &dom_list[i].sid);
2808 trust->sid = sid;
2809 trusts->count++;
2812 TALLOC_FREE(dom_list);
2813 return NT_STATUS_OK;
2815 do_query:
2816 /* Return status value returned by seq number check */
2818 if (!NT_STATUS_IS_OK(domain->last_status))
2819 return domain->last_status;
2821 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2822 domain->name ));
2824 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2826 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2827 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2828 if (!domain->internal && old_status) {
2829 set_domain_offline(domain);
2831 if (!domain->internal &&
2832 !domain->online &&
2833 old_status) {
2834 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2835 if (retval && num_domains && dom_list) {
2836 TALLOC_FREE(trusts->array);
2837 trusts->count = 0;
2838 goto do_fetch_cache;
2842 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2843 * so that the generic centry handling still applies correctly -
2844 * Guenther*/
2846 if (!NT_STATUS_IS_ERR(status)) {
2847 status = NT_STATUS_OK;
2849 return status;
2852 /* get lockout policy */
2853 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2854 TALLOC_CTX *mem_ctx,
2855 struct samr_DomInfo12 *policy)
2857 struct winbind_cache *cache = get_cache(domain);
2858 struct cache_entry *centry = NULL;
2859 NTSTATUS status;
2860 bool old_status;
2862 old_status = domain->online;
2863 if (!cache->tdb)
2864 goto do_query;
2866 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2868 if (!centry)
2869 goto do_query;
2871 do_fetch_cache:
2872 policy->lockout_duration = centry_nttime(centry);
2873 policy->lockout_window = centry_nttime(centry);
2874 policy->lockout_threshold = centry_uint16(centry);
2876 status = centry->status;
2878 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2879 domain->name, nt_errstr(status) ));
2881 centry_free(centry);
2882 return status;
2884 do_query:
2885 ZERO_STRUCTP(policy);
2887 /* Return status value returned by seq number check */
2889 if (!NT_STATUS_IS_OK(domain->last_status))
2890 return domain->last_status;
2892 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2893 domain->name ));
2895 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2897 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2898 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2899 if (!domain->internal && old_status) {
2900 set_domain_offline(domain);
2902 if (cache->tdb &&
2903 !domain->internal &&
2904 !domain->online &&
2905 old_status) {
2906 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2907 if (centry) {
2908 goto do_fetch_cache;
2912 /* and save it */
2913 refresh_sequence_number(domain, false);
2914 if (!NT_STATUS_IS_OK(status)) {
2915 return status;
2917 wcache_save_lockout_policy(domain, status, policy);
2919 return status;
2922 /* get password policy */
2923 static NTSTATUS password_policy(struct winbindd_domain *domain,
2924 TALLOC_CTX *mem_ctx,
2925 struct samr_DomInfo1 *policy)
2927 struct winbind_cache *cache = get_cache(domain);
2928 struct cache_entry *centry = NULL;
2929 NTSTATUS status;
2930 bool old_status;
2932 old_status = domain->online;
2933 if (!cache->tdb)
2934 goto do_query;
2936 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2938 if (!centry)
2939 goto do_query;
2941 do_fetch_cache:
2942 policy->min_password_length = centry_uint16(centry);
2943 policy->password_history_length = centry_uint16(centry);
2944 policy->password_properties = centry_uint32(centry);
2945 policy->max_password_age = centry_nttime(centry);
2946 policy->min_password_age = centry_nttime(centry);
2948 status = centry->status;
2950 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2951 domain->name, nt_errstr(status) ));
2953 centry_free(centry);
2954 return status;
2956 do_query:
2957 ZERO_STRUCTP(policy);
2959 /* Return status value returned by seq number check */
2961 if (!NT_STATUS_IS_OK(domain->last_status))
2962 return domain->last_status;
2964 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2965 domain->name ));
2967 status = domain->backend->password_policy(domain, mem_ctx, policy);
2969 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2970 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2971 if (!domain->internal && old_status) {
2972 set_domain_offline(domain);
2974 if (cache->tdb &&
2975 !domain->internal &&
2976 !domain->online &&
2977 old_status) {
2978 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2979 if (centry) {
2980 goto do_fetch_cache;
2984 /* and save it */
2985 refresh_sequence_number(domain, false);
2986 if (!NT_STATUS_IS_OK(status)) {
2987 return status;
2989 wcache_save_password_policy(domain, status, policy);
2991 return status;
2995 /* Invalidate cached user and group lists coherently */
2997 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2998 void *state)
3000 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3001 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3002 tdb_delete(the_tdb, kbuf);
3004 return 0;
3007 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3009 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3010 const struct dom_sid *sid)
3012 fstring key_str, sid_string;
3013 struct winbind_cache *cache;
3015 /* dont clear cached U/SID and UG/SID entries when we want to logon
3016 * offline - gd */
3018 if (lp_winbind_offline_logon()) {
3019 return;
3022 if (!domain)
3023 return;
3025 cache = get_cache(domain);
3027 if (!cache->tdb) {
3028 return;
3031 /* Clear U/SID cache entry */
3032 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3033 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3034 tdb_delete(cache->tdb, string_tdb_data(key_str));
3036 /* Clear UG/SID cache entry */
3037 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3038 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3039 tdb_delete(cache->tdb, string_tdb_data(key_str));
3041 /* Samba/winbindd never needs this. */
3042 netsamlogon_clear_cached_user(sid);
3045 bool wcache_invalidate_cache(void)
3047 struct winbindd_domain *domain;
3049 for (domain = domain_list(); domain; domain = domain->next) {
3050 struct winbind_cache *cache = get_cache(domain);
3052 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3053 "entries for %s\n", domain->name));
3054 if (cache) {
3055 if (cache->tdb) {
3056 tdb_traverse(cache->tdb, traverse_fn, NULL);
3057 } else {
3058 return false;
3062 return true;
3065 bool wcache_invalidate_cache_noinit(void)
3067 struct winbindd_domain *domain;
3069 for (domain = domain_list(); domain; domain = domain->next) {
3070 struct winbind_cache *cache;
3072 /* Skip uninitialized domains. */
3073 if (!domain->initialized && !domain->internal) {
3074 continue;
3077 cache = get_cache(domain);
3079 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3080 "entries for %s\n", domain->name));
3081 if (cache) {
3082 if (cache->tdb) {
3083 tdb_traverse(cache->tdb, traverse_fn, NULL);
3085 * Flushing cache has nothing to with domains.
3086 * return here if we successfully flushed once.
3087 * To avoid unnecessary traversing the cache.
3089 return true;
3090 } else {
3091 return false;
3095 return true;
3098 bool init_wcache(void)
3100 if (wcache == NULL) {
3101 wcache = SMB_XMALLOC_P(struct winbind_cache);
3102 ZERO_STRUCTP(wcache);
3105 if (wcache->tdb != NULL)
3106 return true;
3108 /* when working offline we must not clear the cache on restart */
3109 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3110 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3111 TDB_INCOMPATIBLE_HASH |
3112 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3113 O_RDWR|O_CREAT, 0600);
3115 if (wcache->tdb == NULL) {
3116 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3117 return false;
3120 return true;
3123 /************************************************************************
3124 This is called by the parent to initialize the cache file.
3125 We don't need sophisticated locking here as we know we're the
3126 only opener.
3127 ************************************************************************/
3129 bool initialize_winbindd_cache(void)
3131 bool cache_bad = true;
3132 uint32 vers;
3134 if (!init_wcache()) {
3135 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3136 return false;
3139 /* Check version number. */
3140 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3141 vers == WINBINDD_CACHE_VERSION) {
3142 cache_bad = false;
3145 if (cache_bad) {
3146 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3147 "and re-creating with version number %d\n",
3148 WINBINDD_CACHE_VERSION ));
3150 tdb_close(wcache->tdb);
3151 wcache->tdb = NULL;
3153 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3154 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3155 cache_path("winbindd_cache.tdb"),
3156 strerror(errno) ));
3157 return false;
3159 if (!init_wcache()) {
3160 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3161 "init_wcache failed.\n"));
3162 return false;
3165 /* Write the version. */
3166 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3167 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3168 tdb_errorstr_compat(wcache->tdb) ));
3169 return false;
3173 tdb_close(wcache->tdb);
3174 wcache->tdb = NULL;
3175 return true;
3178 void close_winbindd_cache(void)
3180 if (!wcache) {
3181 return;
3183 if (wcache->tdb) {
3184 tdb_close(wcache->tdb);
3185 wcache->tdb = NULL;
3189 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3190 char **domain_name, char **name,
3191 enum lsa_SidType *type)
3193 struct winbindd_domain *domain;
3194 NTSTATUS status;
3196 domain = find_lookup_domain_from_sid(sid);
3197 if (domain == NULL) {
3198 return false;
3200 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3201 type);
3202 return NT_STATUS_IS_OK(status);
3205 bool lookup_cached_name(const char *domain_name,
3206 const char *name,
3207 struct dom_sid *sid,
3208 enum lsa_SidType *type)
3210 struct winbindd_domain *domain;
3211 NTSTATUS status;
3212 bool original_online_state;
3214 domain = find_lookup_domain_from_name(domain_name);
3215 if (domain == NULL) {
3216 return false;
3219 /* If we are doing a cached logon, temporarily set the domain
3220 offline so the cache won't expire the entry */
3222 original_online_state = domain->online;
3223 domain->online = false;
3224 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3225 domain->online = original_online_state;
3227 return NT_STATUS_IS_OK(status);
3230 void cache_name2sid(struct winbindd_domain *domain,
3231 const char *domain_name, const char *name,
3232 enum lsa_SidType type, const struct dom_sid *sid)
3234 refresh_sequence_number(domain, false);
3235 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3236 sid, type);
3240 * The original idea that this cache only contains centries has
3241 * been blurred - now other stuff gets put in here. Ensure we
3242 * ignore these things on cleanup.
3245 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3246 TDB_DATA dbuf, void *state)
3248 struct cache_entry *centry;
3250 if (is_non_centry_key(kbuf)) {
3251 return 0;
3254 centry = wcache_fetch_raw((char *)kbuf.dptr);
3255 if (!centry) {
3256 return 0;
3259 if (!NT_STATUS_IS_OK(centry->status)) {
3260 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3261 tdb_delete(the_tdb, kbuf);
3264 centry_free(centry);
3265 return 0;
3268 /* flush the cache */
3269 void wcache_flush_cache(void)
3271 if (!wcache)
3272 return;
3273 if (wcache->tdb) {
3274 tdb_close(wcache->tdb);
3275 wcache->tdb = NULL;
3277 if (!winbindd_use_cache()) {
3278 return;
3281 /* when working offline we must not clear the cache on restart */
3282 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3283 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3284 TDB_INCOMPATIBLE_HASH |
3285 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3286 O_RDWR|O_CREAT, 0600);
3288 if (!wcache->tdb) {
3289 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3290 return;
3293 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3295 DEBUG(10,("wcache_flush_cache success\n"));
3298 /* Count cached creds */
3300 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3301 void *state)
3303 int *cred_count = (int*)state;
3305 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3306 (*cred_count)++;
3308 return 0;
3311 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3313 struct winbind_cache *cache = get_cache(domain);
3315 *count = 0;
3317 if (!cache->tdb) {
3318 return NT_STATUS_INTERNAL_DB_ERROR;
3321 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3323 return NT_STATUS_OK;
3326 struct cred_list {
3327 struct cred_list *prev, *next;
3328 TDB_DATA key;
3329 fstring name;
3330 time_t created;
3332 static struct cred_list *wcache_cred_list;
3334 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3335 void *state)
3337 struct cred_list *cred;
3339 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3341 cred = SMB_MALLOC_P(struct cred_list);
3342 if (cred == NULL) {
3343 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3344 return -1;
3347 ZERO_STRUCTP(cred);
3349 /* save a copy of the key */
3351 fstrcpy(cred->name, (const char *)kbuf.dptr);
3352 DLIST_ADD(wcache_cred_list, cred);
3355 return 0;
3358 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3360 struct winbind_cache *cache = get_cache(domain);
3361 NTSTATUS status;
3362 int ret;
3363 struct cred_list *cred, *oldest = NULL;
3365 if (!cache->tdb) {
3366 return NT_STATUS_INTERNAL_DB_ERROR;
3369 /* we possibly already have an entry */
3370 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3372 fstring key_str, tmp;
3374 DEBUG(11,("we already have an entry, deleting that\n"));
3376 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3378 tdb_delete(cache->tdb, string_tdb_data(key_str));
3380 return NT_STATUS_OK;
3383 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3384 if (ret == 0) {
3385 return NT_STATUS_OK;
3386 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3387 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3390 ZERO_STRUCTP(oldest);
3392 for (cred = wcache_cred_list; cred; cred = cred->next) {
3394 TDB_DATA data;
3395 time_t t;
3397 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3398 if (!data.dptr) {
3399 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3400 cred->name));
3401 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3402 goto done;
3405 t = IVAL(data.dptr, 0);
3406 SAFE_FREE(data.dptr);
3408 if (!oldest) {
3409 oldest = SMB_MALLOC_P(struct cred_list);
3410 if (oldest == NULL) {
3411 status = NT_STATUS_NO_MEMORY;
3412 goto done;
3415 fstrcpy(oldest->name, cred->name);
3416 oldest->created = t;
3417 continue;
3420 if (t < oldest->created) {
3421 fstrcpy(oldest->name, cred->name);
3422 oldest->created = t;
3426 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3427 status = NT_STATUS_OK;
3428 } else {
3429 status = NT_STATUS_UNSUCCESSFUL;
3431 done:
3432 SAFE_FREE(wcache_cred_list);
3433 SAFE_FREE(oldest);
3435 return status;
3438 /* Change the global online/offline state. */
3439 bool set_global_winbindd_state_offline(void)
3441 TDB_DATA data;
3443 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3445 /* Only go offline if someone has created
3446 the key "WINBINDD_OFFLINE" in the cache tdb. */
3448 if (wcache == NULL || wcache->tdb == NULL) {
3449 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3450 return false;
3453 if (!lp_winbind_offline_logon()) {
3454 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3455 return false;
3458 if (global_winbindd_offline_state) {
3459 /* Already offline. */
3460 return true;
3463 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3465 if (!data.dptr || data.dsize != 4) {
3466 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3467 SAFE_FREE(data.dptr);
3468 return false;
3469 } else {
3470 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3471 global_winbindd_offline_state = true;
3472 SAFE_FREE(data.dptr);
3473 return true;
3477 void set_global_winbindd_state_online(void)
3479 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3481 if (!lp_winbind_offline_logon()) {
3482 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3483 return;
3486 if (!global_winbindd_offline_state) {
3487 /* Already online. */
3488 return;
3490 global_winbindd_offline_state = false;
3492 if (!wcache->tdb) {
3493 return;
3496 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3497 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3500 bool get_global_winbindd_state_offline(void)
3502 return global_winbindd_offline_state;
3505 /***********************************************************************
3506 Validate functions for all possible cache tdb keys.
3507 ***********************************************************************/
3509 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3510 struct tdb_validation_status *state)
3512 struct cache_entry *centry;
3514 centry = SMB_XMALLOC_P(struct cache_entry);
3515 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3516 if (!centry->data) {
3517 SAFE_FREE(centry);
3518 return NULL;
3520 centry->len = data.dsize;
3521 centry->ofs = 0;
3523 if (centry->len < 16) {
3524 /* huh? corrupt cache? */
3525 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3526 "(len < 16) ?\n", kstr));
3527 centry_free(centry);
3528 state->bad_entry = true;
3529 state->success = false;
3530 return NULL;
3533 centry->status = NT_STATUS(centry_uint32(centry));
3534 centry->sequence_number = centry_uint32(centry);
3535 centry->timeout = centry_uint64_t(centry);
3536 return centry;
3539 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3540 struct tdb_validation_status *state)
3542 if (dbuf.dsize != 8) {
3543 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3544 keystr, (unsigned int)dbuf.dsize ));
3545 state->bad_entry = true;
3546 return 1;
3548 return 0;
3551 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3552 struct tdb_validation_status *state)
3554 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3555 if (!centry) {
3556 return 1;
3559 (void)centry_uint32(centry);
3560 if (NT_STATUS_IS_OK(centry->status)) {
3561 struct dom_sid sid;
3562 (void)centry_sid(centry, &sid);
3565 centry_free(centry);
3567 if (!(state->success)) {
3568 return 1;
3570 DEBUG(10,("validate_ns: %s ok\n", keystr));
3571 return 0;
3574 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3575 struct tdb_validation_status *state)
3577 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3578 if (!centry) {
3579 return 1;
3582 if (NT_STATUS_IS_OK(centry->status)) {
3583 (void)centry_uint32(centry);
3584 (void)centry_string(centry, mem_ctx);
3585 (void)centry_string(centry, mem_ctx);
3588 centry_free(centry);
3590 if (!(state->success)) {
3591 return 1;
3593 DEBUG(10,("validate_sn: %s ok\n", keystr));
3594 return 0;
3597 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3598 struct tdb_validation_status *state)
3600 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3601 struct dom_sid sid;
3603 if (!centry) {
3604 return 1;
3607 (void)centry_string(centry, mem_ctx);
3608 (void)centry_string(centry, mem_ctx);
3609 (void)centry_string(centry, mem_ctx);
3610 (void)centry_string(centry, mem_ctx);
3611 (void)centry_uint32(centry);
3612 (void)centry_sid(centry, &sid);
3613 (void)centry_sid(centry, &sid);
3615 centry_free(centry);
3617 if (!(state->success)) {
3618 return 1;
3620 DEBUG(10,("validate_u: %s ok\n", keystr));
3621 return 0;
3624 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3625 struct tdb_validation_status *state)
3627 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3629 if (!centry) {
3630 return 1;
3633 (void)centry_nttime(centry);
3634 (void)centry_nttime(centry);
3635 (void)centry_uint16(centry);
3637 centry_free(centry);
3639 if (!(state->success)) {
3640 return 1;
3642 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3643 return 0;
3646 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3647 struct tdb_validation_status *state)
3649 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3651 if (!centry) {
3652 return 1;
3655 (void)centry_uint16(centry);
3656 (void)centry_uint16(centry);
3657 (void)centry_uint32(centry);
3658 (void)centry_nttime(centry);
3659 (void)centry_nttime(centry);
3661 centry_free(centry);
3663 if (!(state->success)) {
3664 return 1;
3666 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3667 return 0;
3670 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3671 struct tdb_validation_status *state)
3673 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3675 if (!centry) {
3676 return 1;
3679 (void)centry_time(centry);
3680 (void)centry_hash16(centry, mem_ctx);
3682 /* We only have 17 bytes more data in the salted cred case. */
3683 if (centry->len - centry->ofs == 17) {
3684 (void)centry_hash16(centry, mem_ctx);
3687 centry_free(centry);
3689 if (!(state->success)) {
3690 return 1;
3692 DEBUG(10,("validate_cred: %s ok\n", keystr));
3693 return 0;
3696 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3697 struct tdb_validation_status *state)
3699 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3700 int32 num_entries, i;
3702 if (!centry) {
3703 return 1;
3706 num_entries = (int32)centry_uint32(centry);
3708 for (i=0; i< num_entries; i++) {
3709 struct dom_sid sid;
3710 (void)centry_string(centry, mem_ctx);
3711 (void)centry_string(centry, mem_ctx);
3712 (void)centry_string(centry, mem_ctx);
3713 (void)centry_string(centry, mem_ctx);
3714 (void)centry_sid(centry, &sid);
3715 (void)centry_sid(centry, &sid);
3718 centry_free(centry);
3720 if (!(state->success)) {
3721 return 1;
3723 DEBUG(10,("validate_ul: %s ok\n", keystr));
3724 return 0;
3727 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3728 struct tdb_validation_status *state)
3730 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3731 int32 num_entries, i;
3733 if (!centry) {
3734 return 1;
3737 num_entries = centry_uint32(centry);
3739 for (i=0; i< num_entries; i++) {
3740 (void)centry_string(centry, mem_ctx);
3741 (void)centry_string(centry, mem_ctx);
3742 (void)centry_uint32(centry);
3745 centry_free(centry);
3747 if (!(state->success)) {
3748 return 1;
3750 DEBUG(10,("validate_gl: %s ok\n", keystr));
3751 return 0;
3754 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3755 struct tdb_validation_status *state)
3757 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3758 int32 num_groups, i;
3760 if (!centry) {
3761 return 1;
3764 num_groups = centry_uint32(centry);
3766 for (i=0; i< num_groups; i++) {
3767 struct dom_sid sid;
3768 centry_sid(centry, &sid);
3771 centry_free(centry);
3773 if (!(state->success)) {
3774 return 1;
3776 DEBUG(10,("validate_ug: %s ok\n", keystr));
3777 return 0;
3780 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3781 struct tdb_validation_status *state)
3783 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3784 int32 num_aliases, i;
3786 if (!centry) {
3787 return 1;
3790 num_aliases = centry_uint32(centry);
3792 for (i=0; i < num_aliases; i++) {
3793 (void)centry_uint32(centry);
3796 centry_free(centry);
3798 if (!(state->success)) {
3799 return 1;
3801 DEBUG(10,("validate_ua: %s ok\n", keystr));
3802 return 0;
3805 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3806 struct tdb_validation_status *state)
3808 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3809 int32 num_names, i;
3811 if (!centry) {
3812 return 1;
3815 num_names = centry_uint32(centry);
3817 for (i=0; i< num_names; i++) {
3818 struct dom_sid sid;
3819 centry_sid(centry, &sid);
3820 (void)centry_string(centry, mem_ctx);
3821 (void)centry_uint32(centry);
3824 centry_free(centry);
3826 if (!(state->success)) {
3827 return 1;
3829 DEBUG(10,("validate_gm: %s ok\n", keystr));
3830 return 0;
3833 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3834 struct tdb_validation_status *state)
3836 /* Can't say anything about this other than must be nonzero. */
3837 if (dbuf.dsize == 0) {
3838 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3839 keystr));
3840 state->bad_entry = true;
3841 state->success = false;
3842 return 1;
3845 DEBUG(10,("validate_dr: %s ok\n", keystr));
3846 return 0;
3849 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3850 struct tdb_validation_status *state)
3852 /* Can't say anything about this other than must be nonzero. */
3853 if (dbuf.dsize == 0) {
3854 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3855 keystr));
3856 state->bad_entry = true;
3857 state->success = false;
3858 return 1;
3861 DEBUG(10,("validate_de: %s ok\n", keystr));
3862 return 0;
3865 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3866 TDB_DATA dbuf, struct tdb_validation_status *state)
3868 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3870 if (!centry) {
3871 return 1;
3874 (void)centry_string(centry, mem_ctx);
3875 (void)centry_string(centry, mem_ctx);
3876 (void)centry_string(centry, mem_ctx);
3877 (void)centry_uint32(centry);
3879 centry_free(centry);
3881 if (!(state->success)) {
3882 return 1;
3884 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3885 return 0;
3888 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3889 TDB_DATA dbuf,
3890 struct tdb_validation_status *state)
3892 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3894 if (!centry) {
3895 return 1;
3898 (void)centry_string( centry, mem_ctx );
3900 centry_free(centry);
3902 if (!(state->success)) {
3903 return 1;
3905 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3906 return 0;
3909 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3910 TDB_DATA dbuf,
3911 struct tdb_validation_status *state)
3913 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3915 if (!centry) {
3916 return 1;
3919 (void)centry_string( centry, mem_ctx );
3921 centry_free(centry);
3923 if (!(state->success)) {
3924 return 1;
3926 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3927 return 0;
3930 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3931 TDB_DATA dbuf,
3932 struct tdb_validation_status *state)
3934 if (dbuf.dsize == 0) {
3935 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3936 "key %s (len ==0) ?\n", keystr));
3937 state->bad_entry = true;
3938 state->success = false;
3939 return 1;
3942 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3943 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3944 return 0;
3947 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3948 struct tdb_validation_status *state)
3950 if (dbuf.dsize != 4) {
3951 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3952 keystr, (unsigned int)dbuf.dsize ));
3953 state->bad_entry = true;
3954 state->success = false;
3955 return 1;
3957 DEBUG(10,("validate_offline: %s ok\n", keystr));
3958 return 0;
3961 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3962 struct tdb_validation_status *state)
3965 * Ignore validation for now. The proper way to do this is with a
3966 * checksum. Just pure parsing does not really catch much.
3968 return 0;
3971 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3972 struct tdb_validation_status *state)
3974 if (dbuf.dsize != 4) {
3975 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3976 "key %s (len %u != 4) ?\n",
3977 keystr, (unsigned int)dbuf.dsize));
3978 state->bad_entry = true;
3979 state->success = false;
3980 return 1;
3983 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3984 return 0;
3987 /***********************************************************************
3988 A list of all possible cache tdb keys with associated validation
3989 functions.
3990 ***********************************************************************/
3992 struct key_val_struct {
3993 const char *keyname;
3994 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3995 } key_val[] = {
3996 {"SEQNUM/", validate_seqnum},
3997 {"NS/", validate_ns},
3998 {"SN/", validate_sn},
3999 {"U/", validate_u},
4000 {"LOC_POL/", validate_loc_pol},
4001 {"PWD_POL/", validate_pwd_pol},
4002 {"CRED/", validate_cred},
4003 {"UL/", validate_ul},
4004 {"GL/", validate_gl},
4005 {"UG/", validate_ug},
4006 {"UA", validate_ua},
4007 {"GM/", validate_gm},
4008 {"DR/", validate_dr},
4009 {"DE/", validate_de},
4010 {"NSS/PWINFO/", validate_pwinfo},
4011 {"TRUSTDOMCACHE/", validate_trustdomcache},
4012 {"NSS/NA/", validate_nss_na},
4013 {"NSS/AN/", validate_nss_an},
4014 {"WINBINDD_OFFLINE", validate_offline},
4015 {"NDR/", validate_ndr},
4016 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4017 {NULL, NULL}
4020 /***********************************************************************
4021 Function to look at every entry in the tdb and validate it as far as
4022 possible.
4023 ***********************************************************************/
4025 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4027 int i;
4028 unsigned int max_key_len = 1024;
4029 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4031 /* Paranoia check. */
4032 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4033 max_key_len = 1024 * 1024;
4035 if (kbuf.dsize > max_key_len) {
4036 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4037 "(%u) > (%u)\n\n",
4038 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4039 return 1;
4042 for (i = 0; key_val[i].keyname; i++) {
4043 size_t namelen = strlen(key_val[i].keyname);
4044 if (kbuf.dsize >= namelen && (
4045 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4046 TALLOC_CTX *mem_ctx;
4047 char *keystr;
4048 int ret;
4050 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4051 if (!keystr) {
4052 return 1;
4054 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4055 keystr[kbuf.dsize] = '\0';
4057 mem_ctx = talloc_init("validate_ctx");
4058 if (!mem_ctx) {
4059 SAFE_FREE(keystr);
4060 return 1;
4063 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4064 v_state);
4066 SAFE_FREE(keystr);
4067 talloc_destroy(mem_ctx);
4068 return ret;
4072 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4073 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4074 DEBUG(0,("data :\n"));
4075 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4076 v_state->unknown_key = true;
4077 v_state->success = false;
4078 return 1; /* terminate. */
4081 static void validate_panic(const char *const why)
4083 DEBUG(0,("validating cache: would panic %s\n", why ));
4084 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4085 exit(47);
4088 /***********************************************************************
4089 Try and validate every entry in the winbindd cache. If we fail here,
4090 delete the cache tdb and return non-zero.
4091 ***********************************************************************/
4093 int winbindd_validate_cache(void)
4095 int ret = -1;
4096 const char *tdb_path = cache_path("winbindd_cache.tdb");
4097 TDB_CONTEXT *tdb = NULL;
4099 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4100 smb_panic_fn = validate_panic;
4103 tdb = tdb_open_log(tdb_path,
4104 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4105 TDB_INCOMPATIBLE_HASH |
4106 ( lp_winbind_offline_logon()
4107 ? TDB_DEFAULT
4108 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4109 O_RDWR|O_CREAT,
4110 0600);
4111 if (!tdb) {
4112 DEBUG(0, ("winbindd_validate_cache: "
4113 "error opening/initializing tdb\n"));
4114 goto done;
4116 tdb_close(tdb);
4118 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4120 if (ret != 0) {
4121 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4122 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4123 unlink(tdb_path);
4126 done:
4127 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4128 smb_panic_fn = smb_panic;
4129 return ret;
4132 /***********************************************************************
4133 Try and validate every entry in the winbindd cache.
4134 ***********************************************************************/
4136 int winbindd_validate_cache_nobackup(void)
4138 int ret = -1;
4139 const char *tdb_path = cache_path("winbindd_cache.tdb");
4141 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4142 smb_panic_fn = validate_panic;
4145 if (wcache == NULL || wcache->tdb == NULL) {
4146 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4147 } else {
4148 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4151 if (ret != 0) {
4152 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4153 "successful.\n"));
4156 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4157 "function\n"));
4158 smb_panic_fn = smb_panic;
4159 return ret;
4162 bool winbindd_cache_validate_and_initialize(void)
4164 close_winbindd_cache();
4166 if (lp_winbind_offline_logon()) {
4167 if (winbindd_validate_cache() < 0) {
4168 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4169 "could be restored.\n"));
4173 return initialize_winbindd_cache();
4176 /*********************************************************************
4177 ********************************************************************/
4179 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4180 struct winbindd_tdc_domain **domains,
4181 size_t *num_domains )
4183 struct winbindd_tdc_domain *list = NULL;
4184 size_t idx;
4185 int i;
4186 bool set_only = false;
4188 /* don't allow duplicates */
4190 idx = *num_domains;
4191 list = *domains;
4193 for ( i=0; i< (*num_domains); i++ ) {
4194 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4195 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4196 new_dom->name));
4197 idx = i;
4198 set_only = true;
4200 break;
4204 if ( !set_only ) {
4205 if ( !*domains ) {
4206 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4207 idx = 0;
4208 } else {
4209 list = talloc_realloc( *domains, *domains,
4210 struct winbindd_tdc_domain,
4211 (*num_domains)+1);
4212 idx = *num_domains;
4215 ZERO_STRUCT( list[idx] );
4218 if ( !list )
4219 return false;
4221 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4222 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4224 if ( !is_null_sid( &new_dom->sid ) ) {
4225 sid_copy( &list[idx].sid, &new_dom->sid );
4226 } else {
4227 sid_copy(&list[idx].sid, &global_sid_NULL);
4230 if ( new_dom->domain_flags != 0x0 )
4231 list[idx].trust_flags = new_dom->domain_flags;
4233 if ( new_dom->domain_type != 0x0 )
4234 list[idx].trust_type = new_dom->domain_type;
4236 if ( new_dom->domain_trust_attribs != 0x0 )
4237 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4239 if ( !set_only ) {
4240 *domains = list;
4241 *num_domains = idx + 1;
4244 return true;
4247 /*********************************************************************
4248 ********************************************************************/
4250 static TDB_DATA make_tdc_key( const char *domain_name )
4252 char *keystr = NULL;
4253 TDB_DATA key = { NULL, 0 };
4255 if ( !domain_name ) {
4256 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4257 return key;
4260 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4261 return key;
4263 key = string_term_tdb_data(keystr);
4265 return key;
4268 /*********************************************************************
4269 ********************************************************************/
4271 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4272 size_t num_domains,
4273 unsigned char **buf )
4275 unsigned char *buffer = NULL;
4276 int len = 0;
4277 int buflen = 0;
4278 int i = 0;
4280 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4281 (int)num_domains));
4283 buflen = 0;
4285 again:
4286 len = 0;
4288 /* Store the number of array items first */
4289 len += tdb_pack( buffer+len, buflen-len, "d",
4290 num_domains );
4292 /* now pack each domain trust record */
4293 for ( i=0; i<num_domains; i++ ) {
4295 fstring tmp;
4297 if ( buflen > 0 ) {
4298 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4299 domains[i].domain_name,
4300 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4303 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4304 domains[i].domain_name,
4305 domains[i].dns_name,
4306 sid_to_fstring(tmp, &domains[i].sid),
4307 domains[i].trust_flags,
4308 domains[i].trust_attribs,
4309 domains[i].trust_type );
4312 if ( buflen < len ) {
4313 SAFE_FREE(buffer);
4314 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4315 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4316 buflen = -1;
4317 goto done;
4319 buflen = len;
4320 goto again;
4323 *buf = buffer;
4325 done:
4326 return buflen;
4329 /*********************************************************************
4330 ********************************************************************/
4332 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4333 struct winbindd_tdc_domain **domains )
4335 fstring domain_name, dns_name, sid_string;
4336 uint32 type, attribs, flags;
4337 int num_domains;
4338 int len = 0;
4339 int i;
4340 struct winbindd_tdc_domain *list = NULL;
4342 /* get the number of domains */
4343 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4344 if ( len == -1 ) {
4345 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4346 return 0;
4349 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4350 if ( !list ) {
4351 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4352 return 0;
4355 for ( i=0; i<num_domains; i++ ) {
4356 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4357 domain_name,
4358 dns_name,
4359 sid_string,
4360 &flags,
4361 &attribs,
4362 &type );
4364 if ( len == -1 ) {
4365 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4366 TALLOC_FREE( list );
4367 return 0;
4370 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4371 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4372 domain_name, dns_name, sid_string,
4373 flags, attribs, type));
4375 list[i].domain_name = talloc_strdup( list, domain_name );
4376 list[i].dns_name = talloc_strdup( list, dns_name );
4377 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4378 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4379 domain_name));
4381 list[i].trust_flags = flags;
4382 list[i].trust_attribs = attribs;
4383 list[i].trust_type = type;
4386 *domains = list;
4388 return num_domains;
4391 /*********************************************************************
4392 ********************************************************************/
4394 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4396 TDB_DATA key = make_tdc_key( lp_workgroup() );
4397 TDB_DATA data = { NULL, 0 };
4398 int ret;
4400 if ( !key.dptr )
4401 return false;
4403 /* See if we were asked to delete the cache entry */
4405 if ( !domains ) {
4406 ret = tdb_delete( wcache->tdb, key );
4407 goto done;
4410 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4412 if ( !data.dptr ) {
4413 ret = -1;
4414 goto done;
4417 ret = tdb_store( wcache->tdb, key, data, 0 );
4419 done:
4420 SAFE_FREE( data.dptr );
4421 SAFE_FREE( key.dptr );
4423 return ( ret == 0 );
4426 /*********************************************************************
4427 ********************************************************************/
4429 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4431 TDB_DATA key = make_tdc_key( lp_workgroup() );
4432 TDB_DATA data = { NULL, 0 };
4434 *domains = NULL;
4435 *num_domains = 0;
4437 if ( !key.dptr )
4438 return false;
4440 data = tdb_fetch_compat( wcache->tdb, key );
4442 SAFE_FREE( key.dptr );
4444 if ( !data.dptr )
4445 return false;
4447 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4449 SAFE_FREE( data.dptr );
4451 if ( !*domains )
4452 return false;
4454 return true;
4457 /*********************************************************************
4458 ********************************************************************/
4460 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4462 struct winbindd_tdc_domain *dom_list = NULL;
4463 size_t num_domains = 0;
4464 bool ret = false;
4466 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4467 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4468 domain->name, domain->alt_name,
4469 sid_string_dbg(&domain->sid),
4470 domain->domain_flags,
4471 domain->domain_trust_attribs,
4472 domain->domain_type));
4474 if ( !init_wcache() ) {
4475 return false;
4478 /* fetch the list */
4480 wcache_tdc_fetch_list( &dom_list, &num_domains );
4482 /* add the new domain */
4484 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4485 goto done;
4488 /* pack the domain */
4490 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4491 goto done;
4494 /* Success */
4496 ret = true;
4497 done:
4498 TALLOC_FREE( dom_list );
4500 return ret;
4503 /*********************************************************************
4504 ********************************************************************/
4506 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4508 struct winbindd_tdc_domain *dom_list = NULL;
4509 size_t num_domains = 0;
4510 int i;
4511 struct winbindd_tdc_domain *d = NULL;
4513 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4515 if ( !init_wcache() ) {
4516 return false;
4519 /* fetch the list */
4521 wcache_tdc_fetch_list( &dom_list, &num_domains );
4523 for ( i=0; i<num_domains; i++ ) {
4524 if ( strequal(name, dom_list[i].domain_name) ||
4525 strequal(name, dom_list[i].dns_name) )
4527 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4528 name));
4530 d = talloc( ctx, struct winbindd_tdc_domain );
4531 if ( !d )
4532 break;
4534 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4535 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4536 sid_copy( &d->sid, &dom_list[i].sid );
4537 d->trust_flags = dom_list[i].trust_flags;
4538 d->trust_type = dom_list[i].trust_type;
4539 d->trust_attribs = dom_list[i].trust_attribs;
4541 break;
4545 TALLOC_FREE( dom_list );
4547 return d;
4550 /*********************************************************************
4551 ********************************************************************/
4553 struct winbindd_tdc_domain*
4554 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4555 const struct dom_sid *sid)
4557 struct winbindd_tdc_domain *dom_list = NULL;
4558 size_t num_domains = 0;
4559 int i;
4560 struct winbindd_tdc_domain *d = NULL;
4562 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4563 sid_string_dbg(sid)));
4565 if (!init_wcache()) {
4566 return false;
4569 /* fetch the list */
4571 wcache_tdc_fetch_list(&dom_list, &num_domains);
4573 for (i = 0; i<num_domains; i++) {
4574 if (sid_equal(sid, &(dom_list[i].sid))) {
4575 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4576 "Found domain %s for SID %s\n",
4577 dom_list[i].domain_name,
4578 sid_string_dbg(sid)));
4580 d = talloc(ctx, struct winbindd_tdc_domain);
4581 if (!d)
4582 break;
4584 d->domain_name = talloc_strdup(d,
4585 dom_list[i].domain_name);
4587 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4588 sid_copy(&d->sid, &dom_list[i].sid);
4589 d->trust_flags = dom_list[i].trust_flags;
4590 d->trust_type = dom_list[i].trust_type;
4591 d->trust_attribs = dom_list[i].trust_attribs;
4593 break;
4597 TALLOC_FREE(dom_list);
4599 return d;
4603 /*********************************************************************
4604 ********************************************************************/
4606 void wcache_tdc_clear( void )
4608 if ( !init_wcache() )
4609 return;
4611 wcache_tdc_store_list( NULL, 0 );
4613 return;
4617 /*********************************************************************
4618 ********************************************************************/
4620 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4621 NTSTATUS status,
4622 const struct dom_sid *user_sid,
4623 const char *homedir,
4624 const char *shell,
4625 const char *gecos,
4626 uint32 gid)
4628 struct cache_entry *centry;
4629 fstring tmp;
4631 if ( (centry = centry_start(domain, status)) == NULL )
4632 return;
4634 centry_put_string( centry, homedir );
4635 centry_put_string( centry, shell );
4636 centry_put_string( centry, gecos );
4637 centry_put_uint32( centry, gid );
4639 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4641 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4643 centry_free(centry);
4646 #ifdef HAVE_ADS
4648 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4649 const struct dom_sid *user_sid,
4650 TALLOC_CTX *ctx,
4651 const char **homedir, const char **shell,
4652 const char **gecos, gid_t *p_gid)
4654 struct winbind_cache *cache = get_cache(domain);
4655 struct cache_entry *centry = NULL;
4656 NTSTATUS nt_status;
4657 fstring tmp;
4659 if (!cache->tdb)
4660 goto do_query;
4662 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4663 sid_to_fstring(tmp, user_sid));
4665 if (!centry)
4666 goto do_query;
4668 *homedir = centry_string( centry, ctx );
4669 *shell = centry_string( centry, ctx );
4670 *gecos = centry_string( centry, ctx );
4671 *p_gid = centry_uint32( centry );
4673 centry_free(centry);
4675 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4676 sid_string_dbg(user_sid)));
4678 return NT_STATUS_OK;
4680 do_query:
4682 nt_status = nss_get_info( domain->name, user_sid, ctx,
4683 homedir, shell, gecos, p_gid );
4685 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4687 if ( NT_STATUS_IS_OK(nt_status) ) {
4688 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4689 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4690 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4691 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4693 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4694 *homedir, *shell, *gecos, *p_gid );
4697 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4698 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4699 domain->name ));
4700 set_domain_offline( domain );
4703 return nt_status;
4706 #endif
4708 /* the cache backend methods are exposed via this structure */
4709 struct winbindd_methods cache_methods = {
4710 true,
4711 query_user_list,
4712 enum_dom_groups,
4713 enum_local_groups,
4714 name_to_sid,
4715 sid_to_name,
4716 rids_to_names,
4717 query_user,
4718 lookup_usergroups,
4719 lookup_useraliases,
4720 lookup_groupmem,
4721 sequence_number,
4722 lockout_policy,
4723 password_policy,
4724 trusted_domains
4727 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4728 uint32_t opnum, const DATA_BLOB *req,
4729 TDB_DATA *pkey)
4731 char *key;
4732 size_t keylen;
4734 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4735 if (key == NULL) {
4736 return false;
4738 keylen = talloc_get_size(key) - 1;
4740 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4741 if (key == NULL) {
4742 return false;
4744 memcpy(key + keylen, req->data, req->length);
4746 pkey->dptr = (uint8_t *)key;
4747 pkey->dsize = talloc_get_size(key);
4748 return true;
4751 static bool wcache_opnum_cacheable(uint32_t opnum)
4753 switch (opnum) {
4754 case NDR_WBINT_PING:
4755 case NDR_WBINT_QUERYSEQUENCENUMBER:
4756 case NDR_WBINT_ALLOCATEUID:
4757 case NDR_WBINT_ALLOCATEGID:
4758 case NDR_WBINT_CHECKMACHINEACCOUNT:
4759 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4760 case NDR_WBINT_PINGDC:
4761 return false;
4763 return true;
4766 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4767 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4769 TDB_DATA key, data;
4770 bool ret = false;
4772 if (!wcache_opnum_cacheable(opnum) ||
4773 is_my_own_sam_domain(domain) ||
4774 is_builtin_domain(domain)) {
4775 return false;
4778 if (wcache->tdb == NULL) {
4779 return false;
4782 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4783 return false;
4785 data = tdb_fetch_compat(wcache->tdb, key);
4786 TALLOC_FREE(key.dptr);
4788 if (data.dptr == NULL) {
4789 return false;
4791 if (data.dsize < 12) {
4792 goto fail;
4795 if (!is_domain_offline(domain)) {
4796 uint32_t entry_seqnum, dom_seqnum, last_check;
4797 uint64_t entry_timeout;
4799 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4800 &last_check)) {
4801 goto fail;
4803 entry_seqnum = IVAL(data.dptr, 0);
4804 if (entry_seqnum != dom_seqnum) {
4805 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4806 (int)entry_seqnum));
4807 goto fail;
4809 entry_timeout = BVAL(data.dptr, 4);
4810 if (entry_timeout > time(NULL)) {
4811 DEBUG(10, ("Entry has timed out\n"));
4812 goto fail;
4816 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4817 data.dsize - 12);
4818 if (resp->data == NULL) {
4819 DEBUG(10, ("talloc failed\n"));
4820 goto fail;
4822 resp->length = data.dsize - 12;
4824 ret = true;
4825 fail:
4826 SAFE_FREE(data.dptr);
4827 return ret;
4830 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4831 const DATA_BLOB *req, const DATA_BLOB *resp)
4833 TDB_DATA key, data;
4834 uint32_t dom_seqnum, last_check;
4835 uint64_t timeout;
4837 if (!wcache_opnum_cacheable(opnum) ||
4838 is_my_own_sam_domain(domain) ||
4839 is_builtin_domain(domain)) {
4840 return;
4843 if (wcache->tdb == NULL) {
4844 return;
4847 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4848 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4849 domain->name));
4850 return;
4853 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4854 return;
4857 timeout = time(NULL) + lp_winbind_cache_time();
4859 data.dsize = resp->length + 12;
4860 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4861 if (data.dptr == NULL) {
4862 goto done;
4865 SIVAL(data.dptr, 0, dom_seqnum);
4866 SBVAL(data.dptr, 4, timeout);
4867 memcpy(data.dptr + 12, resp->data, resp->length);
4869 tdb_store(wcache->tdb, key, data, 0);
4871 done:
4872 TALLOC_FREE(key.dptr);
4873 return;