winbindd: fix horrible mis-indentation of toplvl braces in getgrsid_sid2gid_recv
[Samba.git] / source / winbindd / winbindd_cache.c
blobd3e47d08748582b4eb4b3a9ab025dce03669fb92
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 "winbindd.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods;
36 extern bool opt_nocache;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40 extern struct winbindd_methods builtin_passdb_methods;
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys[] = {
49 "SEQNUM/",
50 "DR/",
51 "DE/",
52 "WINBINDD_OFFLINE",
53 WINBINDD_CACHE_VERSION_KEYSTR,
54 NULL
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static bool is_non_centry_key(TDB_DATA kbuf)
63 int i;
65 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66 return false;
68 for (i = 0; non_centry_keys[i] != NULL; i++) {
69 size_t namelen = strlen(non_centry_keys[i]);
70 if (kbuf.dsize < namelen) {
71 continue;
73 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74 return true;
77 return false;
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static bool global_winbindd_offline_state;
86 struct winbind_cache {
87 TDB_CONTEXT *tdb;
90 struct cache_entry {
91 NTSTATUS status;
92 uint32 sequence_number;
93 uint8 *data;
94 uint32 len, ofs;
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache *wcache;
103 void winbindd_check_cache_size(time_t t)
105 static time_t last_check_time;
106 struct stat st;
108 if (last_check_time == (time_t)0)
109 last_check_time = t;
111 if (t - last_check_time < 60 && t - last_check_time > 0)
112 return;
114 if (wcache == NULL || wcache->tdb == NULL) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116 return;
119 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121 return;
124 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st.st_size,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
135 struct winbind_cache *ret = wcache;
137 /* We have to know what type of domain we are dealing with first. */
139 if (domain->internal) {
140 domain->backend = &builtin_passdb_methods;
141 domain->initialized = True;
143 if ( !domain->initialized ) {
144 init_dc_connection( domain );
148 OK. listen up becasue I'm only going to say this once.
149 We have the following scenarios to consider
150 (a) trusted AD domains on a Samba DC,
151 (b) trusted AD domains and we are joined to a non-kerberos domain
152 (c) trusted AD domains and we are joined to a kerberos (AD) domain
154 For (a) we can always contact the trusted domain using krb5
155 since we have the domain trust account password
157 For (b) we can only use RPC since we have no way of
158 getting a krb5 ticket in our own domain
160 For (c) we can always use krb5 since we have a kerberos trust
162 --jerry
165 if (!domain->backend) {
166 #ifdef HAVE_ADS
167 struct winbindd_domain *our_domain = domain;
169 /* find our domain first so we can figure out if we
170 are joined to a kerberized domain */
172 if ( !domain->primary )
173 our_domain = find_our_domain();
175 if ((our_domain->active_directory || IS_DC)
176 && domain->active_directory
177 && !lp_winbind_rpc_only()) {
178 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
179 domain->backend = &ads_methods;
180 } else {
181 #endif /* HAVE_ADS */
182 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
183 domain->backend = &reconnect_methods;
184 #ifdef HAVE_ADS
186 #endif /* HAVE_ADS */
189 if (ret)
190 return ret;
192 ret = SMB_XMALLOC_P(struct winbind_cache);
193 ZERO_STRUCTP(ret);
195 wcache = ret;
196 wcache_flush_cache();
198 return ret;
202 free a centry structure
204 static void centry_free(struct cache_entry *centry)
206 if (!centry)
207 return;
208 SAFE_FREE(centry->data);
209 free(centry);
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
214 if (centry->len - centry->ofs < nbytes) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes,
217 centry->len - centry->ofs));
218 return false;
220 return true;
224 pull a uint32 from a cache entry
226 static uint32 centry_uint32(struct cache_entry *centry)
228 uint32 ret;
230 if (!centry_check_bytes(centry, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret = IVAL(centry->data, centry->ofs);
234 centry->ofs += 4;
235 return ret;
239 pull a uint16 from a cache entry
241 static uint16 centry_uint16(struct cache_entry *centry)
243 uint16 ret;
244 if (!centry_check_bytes(centry, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 2;
249 return ret;
253 pull a uint8 from a cache entry
255 static uint8 centry_uint8(struct cache_entry *centry)
257 uint8 ret;
258 if (!centry_check_bytes(centry, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret = CVAL(centry->data, centry->ofs);
262 centry->ofs += 1;
263 return ret;
267 pull a NTTIME from a cache entry
269 static NTTIME centry_nttime(struct cache_entry *centry)
271 NTTIME ret;
272 if (!centry_check_bytes(centry, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret = IVAL(centry->data, centry->ofs);
276 centry->ofs += 4;
277 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278 centry->ofs += 4;
279 return ret;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry *centry)
287 return (time_t)centry_nttime(centry);
290 /* pull a string from a cache entry, using the supplied
291 talloc context
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 uint32 len;
296 char *ret;
298 len = centry_uint8(centry);
300 if (len == 0xFF) {
301 /* a deliberate NULL string */
302 return NULL;
305 if (!centry_check_bytes(centry, (size_t)len)) {
306 smb_panic_fn("centry_string");
309 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310 if (!ret) {
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (!centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret,centry->data + centry->ofs, 16);
344 centry->ofs += 16;
345 return ret;
348 /* pull a sid from a cache entry, using the supplied
349 talloc context
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
353 char *sid_string;
354 sid_string = centry_string(centry, mem_ctx);
355 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356 return false;
358 return true;
363 pull a NTSTATUS from a cache entry
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
367 NTSTATUS status;
369 status = NT_STATUS(centry_uint32(centry));
370 return status;
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain *domain)
377 bool ret;
379 if (!wcache->tdb)
380 return false;
382 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
384 if (ret)
385 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
386 domain->name ));
387 return ret;
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
392 TDB_DATA data;
393 fstring key;
394 uint32 time_diff;
396 if (!wcache->tdb) {
397 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398 return NT_STATUS_UNSUCCESSFUL;
401 fstr_sprintf( key, "SEQNUM/%s", domain->name );
403 data = tdb_fetch_bystring( wcache->tdb, key );
404 if ( !data.dptr || data.dsize!=8 ) {
405 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
406 return NT_STATUS_UNSUCCESSFUL;
409 domain->sequence_number = IVAL(data.dptr, 0);
410 domain->last_seq_check = IVAL(data.dptr, 4);
412 SAFE_FREE(data.dptr);
414 /* have we expired? */
416 time_diff = now - domain->last_seq_check;
417 if ( time_diff > lp_winbind_cache_time() ) {
418 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419 domain->name, domain->sequence_number,
420 (uint32)domain->last_seq_check));
421 return NT_STATUS_UNSUCCESSFUL;
424 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
425 domain->name, domain->sequence_number,
426 (uint32)domain->last_seq_check));
428 return NT_STATUS_OK;
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
433 TDB_DATA data;
434 fstring key_str;
435 uint8 buf[8];
437 if (!wcache->tdb) {
438 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439 return NT_STATUS_UNSUCCESSFUL;
442 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
444 SIVAL(buf, 0, domain->sequence_number);
445 SIVAL(buf, 4, domain->last_seq_check);
446 data.dptr = buf;
447 data.dsize = 8;
449 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
450 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
458 return NT_STATUS_OK;
462 refresh the domain sequence number. If force is true
463 then always refresh it, no matter how recently we fetched it
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
468 NTSTATUS status;
469 unsigned time_diff;
470 time_t t = time(NULL);
471 unsigned cache_time = lp_winbind_cache_time();
473 if ( IS_DOMAIN_OFFLINE(domain) ) {
474 return;
477 get_cache( domain );
479 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
480 /* trying to reconnect is expensive, don't do it too often */
481 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
482 cache_time *= 8;
484 #endif
486 time_diff = t - domain->last_seq_check;
488 /* see if we have to refetch the domain sequence number */
489 if (!force && (time_diff < cache_time)) {
490 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
491 goto done;
494 /* try to get the sequence number from the tdb cache first */
495 /* this will update the timestamp as well */
497 status = fetch_cache_seqnum( domain, t );
498 if ( NT_STATUS_IS_OK(status) )
499 goto done;
501 /* important! make sure that we know if this is a native
502 mode domain or not. And that we can contact it. */
504 if ( winbindd_can_contact_domain( domain ) ) {
505 struct winbindd_methods *orig_backend = domain->backend;
506 status = domain->backend->sequence_number(domain,
507 &domain->sequence_number);
508 if (domain->backend != orig_backend) {
509 /* Try again. */
510 status = domain->backend->sequence_number(domain,
511 &domain->sequence_number);
513 } else {
514 /* just use the current time */
515 status = NT_STATUS_OK;
516 domain->sequence_number = time(NULL);
520 /* the above call could have set our domain->backend to NULL when
521 * coming from offline to online mode, make sure to reinitialize the
522 * backend - Guenther */
523 get_cache( domain );
525 if (!NT_STATUS_IS_OK(status)) {
526 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
527 domain->sequence_number = DOM_SEQUENCE_NONE;
530 domain->last_status = status;
531 domain->last_seq_check = time(NULL);
533 /* save the new sequence number in the cache */
534 store_cache_seqnum( domain );
536 done:
537 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
538 domain->name, domain->sequence_number));
540 return;
544 decide if a cache entry has expired
546 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
548 /* If we've been told to be offline - stay in that state... */
549 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
550 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
551 keystr, domain->name ));
552 return false;
555 /* when the domain is offline return the cached entry.
556 * This deals with transient offline states... */
558 if (!domain->online) {
559 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
560 keystr, domain->name ));
561 return false;
564 /* if the server is OK and our cache entry came from when it was down then
565 the entry is invalid */
566 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
567 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
568 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
569 keystr, domain->name ));
570 return true;
573 /* if the server is down or the cache entry is not older than the
574 current sequence number then it is OK */
575 if (wcache_server_down(domain) ||
576 centry->sequence_number == domain->sequence_number) {
577 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
578 keystr, domain->name ));
579 return false;
582 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
583 keystr, domain->name ));
585 /* it's expired */
586 return true;
589 static struct cache_entry *wcache_fetch_raw(char *kstr)
591 TDB_DATA data;
592 struct cache_entry *centry;
593 TDB_DATA key;
595 key = string_tdb_data(kstr);
596 data = tdb_fetch(wcache->tdb, key);
597 if (!data.dptr) {
598 /* a cache miss */
599 return NULL;
602 centry = SMB_XMALLOC_P(struct cache_entry);
603 centry->data = (unsigned char *)data.dptr;
604 centry->len = data.dsize;
605 centry->ofs = 0;
607 if (centry->len < 8) {
608 /* huh? corrupt cache? */
609 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
610 centry_free(centry);
611 return NULL;
614 centry->status = centry_ntstatus(centry);
615 centry->sequence_number = centry_uint32(centry);
617 return centry;
621 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
622 number and return status
624 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
625 struct winbindd_domain *domain,
626 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
627 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
628 struct winbindd_domain *domain,
629 const char *format, ...)
631 va_list ap;
632 char *kstr;
633 struct cache_entry *centry;
635 if (opt_nocache) {
636 return NULL;
639 refresh_sequence_number(domain, false);
641 va_start(ap, format);
642 smb_xvasprintf(&kstr, format, ap);
643 va_end(ap);
645 centry = wcache_fetch_raw(kstr);
646 if (centry == NULL) {
647 free(kstr);
648 return NULL;
651 if (centry_expired(domain, kstr, centry)) {
653 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
654 kstr, domain->name ));
656 centry_free(centry);
657 free(kstr);
658 return NULL;
661 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
662 kstr, domain->name ));
664 free(kstr);
665 return centry;
668 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
669 static void wcache_delete(const char *format, ...)
671 va_list ap;
672 char *kstr;
673 TDB_DATA key;
675 va_start(ap, format);
676 smb_xvasprintf(&kstr, format, ap);
677 va_end(ap);
679 key = string_tdb_data(kstr);
681 tdb_delete(wcache->tdb, key);
682 free(kstr);
686 make sure we have at least len bytes available in a centry
688 static void centry_expand(struct cache_entry *centry, uint32 len)
690 if (centry->len - centry->ofs >= len)
691 return;
692 centry->len *= 2;
693 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
694 centry->len);
695 if (!centry->data) {
696 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
697 smb_panic_fn("out of memory in centry_expand");
702 push a uint32 into a centry
704 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
706 centry_expand(centry, 4);
707 SIVAL(centry->data, centry->ofs, v);
708 centry->ofs += 4;
712 push a uint16 into a centry
714 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
716 centry_expand(centry, 2);
717 SIVAL(centry->data, centry->ofs, v);
718 centry->ofs += 2;
722 push a uint8 into a centry
724 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
726 centry_expand(centry, 1);
727 SCVAL(centry->data, centry->ofs, v);
728 centry->ofs += 1;
732 push a string into a centry
734 static void centry_put_string(struct cache_entry *centry, const char *s)
736 int len;
738 if (!s) {
739 /* null strings are marked as len 0xFFFF */
740 centry_put_uint8(centry, 0xFF);
741 return;
744 len = strlen(s);
745 /* can't handle more than 254 char strings. Truncating is probably best */
746 if (len > 254) {
747 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
748 len = 254;
750 centry_put_uint8(centry, len);
751 centry_expand(centry, len);
752 memcpy(centry->data + centry->ofs, s, len);
753 centry->ofs += len;
757 push a 16 byte hash into a centry - treat as 16 byte string.
759 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
761 centry_put_uint8(centry, 16);
762 centry_expand(centry, 16);
763 memcpy(centry->data + centry->ofs, val, 16);
764 centry->ofs += 16;
767 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
769 fstring sid_string;
770 centry_put_string(centry, sid_to_fstring(sid_string, sid));
775 put NTSTATUS into a centry
777 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
779 uint32 status_value = NT_STATUS_V(status);
780 centry_put_uint32(centry, status_value);
785 push a NTTIME into a centry
787 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
789 centry_expand(centry, 8);
790 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
791 centry->ofs += 4;
792 SIVAL(centry->data, centry->ofs, nt >> 32);
793 centry->ofs += 4;
797 push a time_t into a centry - use a 64 bit size.
798 NTTIME here is being used as a convenient 64-bit size.
800 static void centry_put_time(struct cache_entry *centry, time_t t)
802 NTTIME nt = (NTTIME)t;
803 centry_put_nttime(centry, nt);
807 start a centry for output. When finished, call centry_end()
809 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
811 struct cache_entry *centry;
813 if (!wcache->tdb)
814 return NULL;
816 centry = SMB_XMALLOC_P(struct cache_entry);
818 centry->len = 8192; /* reasonable default */
819 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
820 centry->ofs = 0;
821 centry->sequence_number = domain->sequence_number;
822 centry_put_ntstatus(centry, status);
823 centry_put_uint32(centry, centry->sequence_number);
824 return centry;
828 finish a centry and write it to the tdb
830 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
831 static void centry_end(struct cache_entry *centry, const char *format, ...)
833 va_list ap;
834 char *kstr;
835 TDB_DATA key, data;
837 if (opt_nocache) {
838 return;
841 va_start(ap, format);
842 smb_xvasprintf(&kstr, format, ap);
843 va_end(ap);
845 key = string_tdb_data(kstr);
846 data.dptr = centry->data;
847 data.dsize = centry->ofs;
849 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
850 free(kstr);
853 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
854 NTSTATUS status, const char *domain_name,
855 const char *name, const DOM_SID *sid,
856 enum lsa_SidType type)
858 struct cache_entry *centry;
859 fstring uname;
861 centry = centry_start(domain, status);
862 if (!centry)
863 return;
864 centry_put_uint32(centry, type);
865 centry_put_sid(centry, sid);
866 fstrcpy(uname, name);
867 strupper_m(uname);
868 centry_end(centry, "NS/%s/%s", domain_name, uname);
869 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
870 uname, sid_string_dbg(sid), nt_errstr(status)));
871 centry_free(centry);
874 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
875 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
877 struct cache_entry *centry;
878 fstring sid_string;
880 centry = centry_start(domain, status);
881 if (!centry)
882 return;
884 if (NT_STATUS_IS_OK(status)) {
885 centry_put_uint32(centry, type);
886 centry_put_string(centry, domain_name);
887 centry_put_string(centry, name);
890 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
891 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
892 name, nt_errstr(status)));
893 centry_free(centry);
897 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
899 struct cache_entry *centry;
900 fstring sid_string;
902 if (is_null_sid(&info->user_sid)) {
903 return;
906 centry = centry_start(domain, status);
907 if (!centry)
908 return;
909 centry_put_string(centry, info->acct_name);
910 centry_put_string(centry, info->full_name);
911 centry_put_string(centry, info->homedir);
912 centry_put_string(centry, info->shell);
913 centry_put_uint32(centry, info->primary_gid);
914 centry_put_sid(centry, &info->user_sid);
915 centry_put_sid(centry, &info->group_sid);
916 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
917 &info->user_sid));
918 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
919 centry_free(centry);
922 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
923 NTSTATUS status,
924 struct samr_DomInfo12 *lockout_policy)
926 struct cache_entry *centry;
928 centry = centry_start(domain, status);
929 if (!centry)
930 return;
932 centry_put_nttime(centry, lockout_policy->lockout_duration);
933 centry_put_nttime(centry, lockout_policy->lockout_window);
934 centry_put_uint16(centry, lockout_policy->lockout_threshold);
936 centry_end(centry, "LOC_POL/%s", domain->name);
938 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
940 centry_free(centry);
943 static void wcache_save_password_policy(struct winbindd_domain *domain,
944 NTSTATUS status,
945 struct samr_DomInfo1 *policy)
947 struct cache_entry *centry;
949 centry = centry_start(domain, status);
950 if (!centry)
951 return;
953 centry_put_uint16(centry, policy->min_password_length);
954 centry_put_uint16(centry, policy->password_history_length);
955 centry_put_uint32(centry, policy->password_properties);
956 centry_put_nttime(centry, policy->max_password_age);
957 centry_put_nttime(centry, policy->min_password_age);
959 centry_end(centry, "PWD_POL/%s", domain->name);
961 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
963 centry_free(centry);
966 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
968 struct winbind_cache *cache = get_cache(domain);
969 TDB_DATA data;
970 fstring key_str, tmp;
971 uint32 rid;
973 if (!cache->tdb) {
974 return NT_STATUS_INTERNAL_DB_ERROR;
977 if (is_null_sid(sid)) {
978 return NT_STATUS_INVALID_SID;
981 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
982 return NT_STATUS_INVALID_SID;
985 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
987 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
988 if (!data.dptr) {
989 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
992 SAFE_FREE(data.dptr);
993 return NT_STATUS_OK;
996 /* Lookup creds for a SID - copes with old (unsalted) creds as well
997 as new salted ones. */
999 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1000 TALLOC_CTX *mem_ctx,
1001 const DOM_SID *sid,
1002 const uint8 **cached_nt_pass,
1003 const uint8 **cached_salt)
1005 struct winbind_cache *cache = get_cache(domain);
1006 struct cache_entry *centry = NULL;
1007 NTSTATUS status;
1008 time_t t;
1009 uint32 rid;
1010 fstring tmp;
1012 if (!cache->tdb) {
1013 return NT_STATUS_INTERNAL_DB_ERROR;
1016 if (is_null_sid(sid)) {
1017 return NT_STATUS_INVALID_SID;
1020 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1021 return NT_STATUS_INVALID_SID;
1024 /* Try and get a salted cred first. If we can't
1025 fall back to an unsalted cred. */
1027 centry = wcache_fetch(cache, domain, "CRED/%s",
1028 sid_to_fstring(tmp, sid));
1029 if (!centry) {
1030 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1031 sid_string_dbg(sid)));
1032 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1035 t = centry_time(centry);
1037 /* In the salted case this isn't actually the nt_hash itself,
1038 but the MD5 of the salt + nt_hash. Let the caller
1039 sort this out. It can tell as we only return the cached_salt
1040 if we are returning a salted cred. */
1042 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1043 if (*cached_nt_pass == NULL) {
1044 fstring sidstr;
1046 sid_to_fstring(sidstr, sid);
1048 /* Bad (old) cred cache. Delete and pretend we
1049 don't have it. */
1050 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1051 sidstr));
1052 wcache_delete("CRED/%s", sidstr);
1053 centry_free(centry);
1054 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1057 /* We only have 17 bytes more data in the salted cred case. */
1058 if (centry->len - centry->ofs == 17) {
1059 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1060 } else {
1061 *cached_salt = NULL;
1064 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1065 if (*cached_salt) {
1066 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1069 status = centry->status;
1071 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1072 sid_string_dbg(sid), nt_errstr(status) ));
1074 centry_free(centry);
1075 return status;
1078 /* Store creds for a SID - only writes out new salted ones. */
1080 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1081 TALLOC_CTX *mem_ctx,
1082 const DOM_SID *sid,
1083 const uint8 nt_pass[NT_HASH_LEN])
1085 struct cache_entry *centry;
1086 fstring sid_string;
1087 uint32 rid;
1088 uint8 cred_salt[NT_HASH_LEN];
1089 uint8 salted_hash[NT_HASH_LEN];
1091 if (is_null_sid(sid)) {
1092 return NT_STATUS_INVALID_SID;
1095 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1096 return NT_STATUS_INVALID_SID;
1099 centry = centry_start(domain, NT_STATUS_OK);
1100 if (!centry) {
1101 return NT_STATUS_INTERNAL_DB_ERROR;
1104 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1106 centry_put_time(centry, time(NULL));
1108 /* Create a salt and then salt the hash. */
1109 generate_random_buffer(cred_salt, NT_HASH_LEN);
1110 E_md5hash(cred_salt, nt_pass, salted_hash);
1112 centry_put_hash16(centry, salted_hash);
1113 centry_put_hash16(centry, cred_salt);
1114 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1116 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1118 centry_free(centry);
1120 return NT_STATUS_OK;
1124 /* Query display info. This is the basic user list fn */
1125 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1126 TALLOC_CTX *mem_ctx,
1127 uint32 *num_entries,
1128 WINBIND_USERINFO **info)
1130 struct winbind_cache *cache = get_cache(domain);
1131 struct cache_entry *centry = NULL;
1132 NTSTATUS status;
1133 unsigned int i, retry;
1135 if (!cache->tdb)
1136 goto do_query;
1138 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1139 if (!centry)
1140 goto do_query;
1142 *num_entries = centry_uint32(centry);
1144 if (*num_entries == 0)
1145 goto do_cached;
1147 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1148 if (! (*info)) {
1149 smb_panic_fn("query_user_list out of memory");
1151 for (i=0; i<(*num_entries); i++) {
1152 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1153 (*info)[i].full_name = centry_string(centry, mem_ctx);
1154 (*info)[i].homedir = centry_string(centry, mem_ctx);
1155 (*info)[i].shell = centry_string(centry, mem_ctx);
1156 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1157 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1160 do_cached:
1161 status = centry->status;
1163 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1164 domain->name, nt_errstr(status) ));
1166 centry_free(centry);
1167 return status;
1169 do_query:
1170 *num_entries = 0;
1171 *info = NULL;
1173 /* Return status value returned by seq number check */
1175 if (!NT_STATUS_IS_OK(domain->last_status))
1176 return domain->last_status;
1178 /* Put the query_user_list() in a retry loop. There appears to be
1179 * some bug either with Windows 2000 or Samba's handling of large
1180 * rpc replies. This manifests itself as sudden disconnection
1181 * at a random point in the enumeration of a large (60k) user list.
1182 * The retry loop simply tries the operation again. )-: It's not
1183 * pretty but an acceptable workaround until we work out what the
1184 * real problem is. */
1186 retry = 0;
1187 do {
1189 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1190 domain->name ));
1192 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1193 if (!NT_STATUS_IS_OK(status)) {
1194 DEBUG(3, ("query_user_list: returned 0x%08x, "
1195 "retrying\n", NT_STATUS_V(status)));
1197 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1198 DEBUG(3, ("query_user_list: flushing "
1199 "connection cache\n"));
1200 invalidate_cm_connection(&domain->conn);
1203 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1204 (retry++ < 5));
1206 /* and save it */
1207 refresh_sequence_number(domain, false);
1208 centry = centry_start(domain, status);
1209 if (!centry)
1210 goto skip_save;
1211 centry_put_uint32(centry, *num_entries);
1212 for (i=0; i<(*num_entries); i++) {
1213 centry_put_string(centry, (*info)[i].acct_name);
1214 centry_put_string(centry, (*info)[i].full_name);
1215 centry_put_string(centry, (*info)[i].homedir);
1216 centry_put_string(centry, (*info)[i].shell);
1217 centry_put_sid(centry, &(*info)[i].user_sid);
1218 centry_put_sid(centry, &(*info)[i].group_sid);
1219 if (domain->backend && domain->backend->consistent) {
1220 /* when the backend is consistent we can pre-prime some mappings */
1221 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1222 domain->name,
1223 (*info)[i].acct_name,
1224 &(*info)[i].user_sid,
1225 SID_NAME_USER);
1226 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1227 &(*info)[i].user_sid,
1228 domain->name,
1229 (*info)[i].acct_name,
1230 SID_NAME_USER);
1231 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1234 centry_end(centry, "UL/%s", domain->name);
1235 centry_free(centry);
1237 skip_save:
1238 return status;
1241 /* list all domain groups */
1242 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1243 TALLOC_CTX *mem_ctx,
1244 uint32 *num_entries,
1245 struct acct_info **info)
1247 struct winbind_cache *cache = get_cache(domain);
1248 struct cache_entry *centry = NULL;
1249 NTSTATUS status;
1250 unsigned int i;
1252 if (!cache->tdb)
1253 goto do_query;
1255 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1256 if (!centry)
1257 goto do_query;
1259 *num_entries = centry_uint32(centry);
1261 if (*num_entries == 0)
1262 goto do_cached;
1264 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1265 if (! (*info)) {
1266 smb_panic_fn("enum_dom_groups out of memory");
1268 for (i=0; i<(*num_entries); i++) {
1269 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1270 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1271 (*info)[i].rid = centry_uint32(centry);
1274 do_cached:
1275 status = centry->status;
1277 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1278 domain->name, nt_errstr(status) ));
1280 centry_free(centry);
1281 return status;
1283 do_query:
1284 *num_entries = 0;
1285 *info = NULL;
1287 /* Return status value returned by seq number check */
1289 if (!NT_STATUS_IS_OK(domain->last_status))
1290 return domain->last_status;
1292 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1293 domain->name ));
1295 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1297 /* and save it */
1298 refresh_sequence_number(domain, false);
1299 centry = centry_start(domain, status);
1300 if (!centry)
1301 goto skip_save;
1302 centry_put_uint32(centry, *num_entries);
1303 for (i=0; i<(*num_entries); i++) {
1304 centry_put_string(centry, (*info)[i].acct_name);
1305 centry_put_string(centry, (*info)[i].acct_desc);
1306 centry_put_uint32(centry, (*info)[i].rid);
1308 centry_end(centry, "GL/%s/domain", domain->name);
1309 centry_free(centry);
1311 skip_save:
1312 return status;
1315 /* list all domain groups */
1316 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1317 TALLOC_CTX *mem_ctx,
1318 uint32 *num_entries,
1319 struct acct_info **info)
1321 struct winbind_cache *cache = get_cache(domain);
1322 struct cache_entry *centry = NULL;
1323 NTSTATUS status;
1324 unsigned int i;
1326 if (!cache->tdb)
1327 goto do_query;
1329 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1330 if (!centry)
1331 goto do_query;
1333 *num_entries = centry_uint32(centry);
1335 if (*num_entries == 0)
1336 goto do_cached;
1338 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1339 if (! (*info)) {
1340 smb_panic_fn("enum_dom_groups out of memory");
1342 for (i=0; i<(*num_entries); i++) {
1343 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1344 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1345 (*info)[i].rid = centry_uint32(centry);
1348 do_cached:
1350 /* If we are returning cached data and the domain controller
1351 is down then we don't know whether the data is up to date
1352 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1353 indicate this. */
1355 if (wcache_server_down(domain)) {
1356 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1357 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1358 } else
1359 status = centry->status;
1361 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1362 domain->name, nt_errstr(status) ));
1364 centry_free(centry);
1365 return status;
1367 do_query:
1368 *num_entries = 0;
1369 *info = NULL;
1371 /* Return status value returned by seq number check */
1373 if (!NT_STATUS_IS_OK(domain->last_status))
1374 return domain->last_status;
1376 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1377 domain->name ));
1379 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1381 /* and save it */
1382 refresh_sequence_number(domain, false);
1383 centry = centry_start(domain, status);
1384 if (!centry)
1385 goto skip_save;
1386 centry_put_uint32(centry, *num_entries);
1387 for (i=0; i<(*num_entries); i++) {
1388 centry_put_string(centry, (*info)[i].acct_name);
1389 centry_put_string(centry, (*info)[i].acct_desc);
1390 centry_put_uint32(centry, (*info)[i].rid);
1392 centry_end(centry, "GL/%s/local", domain->name);
1393 centry_free(centry);
1395 skip_save:
1396 return status;
1399 /* convert a single name to a sid in a domain */
1400 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1401 TALLOC_CTX *mem_ctx,
1402 enum winbindd_cmd orig_cmd,
1403 const char *domain_name,
1404 const char *name,
1405 DOM_SID *sid,
1406 enum lsa_SidType *type)
1408 struct winbind_cache *cache = get_cache(domain);
1409 struct cache_entry *centry = NULL;
1410 NTSTATUS status;
1411 fstring uname;
1413 if (!cache->tdb)
1414 goto do_query;
1416 fstrcpy(uname, name);
1417 strupper_m(uname);
1418 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1419 if (!centry)
1420 goto do_query;
1422 status = centry->status;
1423 if (NT_STATUS_IS_OK(status)) {
1424 *type = (enum lsa_SidType)centry_uint32(centry);
1425 centry_sid(centry, mem_ctx, sid);
1428 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1429 domain->name, nt_errstr(status) ));
1431 centry_free(centry);
1432 return status;
1434 do_query:
1435 ZERO_STRUCTP(sid);
1437 /* If the seq number check indicated that there is a problem
1438 * with this DC, then return that status... except for
1439 * access_denied. This is special because the dc may be in
1440 * "restrict anonymous = 1" mode, in which case it will deny
1441 * most unauthenticated operations, but *will* allow the LSA
1442 * name-to-sid that we try as a fallback. */
1444 if (!(NT_STATUS_IS_OK(domain->last_status)
1445 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1446 return domain->last_status;
1448 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1449 domain->name ));
1451 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1452 domain_name, name, sid, type);
1454 /* and save it */
1455 refresh_sequence_number(domain, false);
1457 if (domain->online &&
1458 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1459 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1461 /* Only save the reverse mapping if this was not a UPN */
1462 if (!strchr(name, '@')) {
1463 strupper_m(CONST_DISCARD(char *,domain_name));
1464 strlower_m(CONST_DISCARD(char *,name));
1465 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1469 return status;
1472 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1473 given */
1474 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1475 TALLOC_CTX *mem_ctx,
1476 const DOM_SID *sid,
1477 char **domain_name,
1478 char **name,
1479 enum lsa_SidType *type)
1481 struct winbind_cache *cache = get_cache(domain);
1482 struct cache_entry *centry = NULL;
1483 NTSTATUS status;
1484 fstring sid_string;
1486 if (!cache->tdb)
1487 goto do_query;
1489 centry = wcache_fetch(cache, domain, "SN/%s",
1490 sid_to_fstring(sid_string, sid));
1491 if (!centry)
1492 goto do_query;
1494 status = centry->status;
1495 if (NT_STATUS_IS_OK(status)) {
1496 *type = (enum lsa_SidType)centry_uint32(centry);
1497 *domain_name = centry_string(centry, mem_ctx);
1498 *name = centry_string(centry, mem_ctx);
1501 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1502 domain->name, nt_errstr(status) ));
1504 centry_free(centry);
1505 return status;
1507 do_query:
1508 *name = NULL;
1509 *domain_name = NULL;
1511 /* If the seq number check indicated that there is a problem
1512 * with this DC, then return that status... except for
1513 * access_denied. This is special because the dc may be in
1514 * "restrict anonymous = 1" mode, in which case it will deny
1515 * most unauthenticated operations, but *will* allow the LSA
1516 * sid-to-name that we try as a fallback. */
1518 if (!(NT_STATUS_IS_OK(domain->last_status)
1519 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1520 return domain->last_status;
1522 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1523 domain->name ));
1525 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1527 /* and save it */
1528 refresh_sequence_number(domain, false);
1529 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1531 /* We can't save the name to sid mapping here, as with sid history a
1532 * later name2sid would give the wrong sid. */
1534 return status;
1537 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1538 TALLOC_CTX *mem_ctx,
1539 const DOM_SID *domain_sid,
1540 uint32 *rids,
1541 size_t num_rids,
1542 char **domain_name,
1543 char ***names,
1544 enum lsa_SidType **types)
1546 struct winbind_cache *cache = get_cache(domain);
1547 size_t i;
1548 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1549 bool have_mapped;
1550 bool have_unmapped;
1552 *domain_name = NULL;
1553 *names = NULL;
1554 *types = NULL;
1556 if (!cache->tdb) {
1557 goto do_query;
1560 if (num_rids == 0) {
1561 return NT_STATUS_OK;
1564 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1565 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1567 if ((*names == NULL) || (*types == NULL)) {
1568 result = NT_STATUS_NO_MEMORY;
1569 goto error;
1572 have_mapped = have_unmapped = false;
1574 for (i=0; i<num_rids; i++) {
1575 DOM_SID sid;
1576 struct cache_entry *centry;
1577 fstring tmp;
1579 if (!sid_compose(&sid, domain_sid, rids[i])) {
1580 result = NT_STATUS_INTERNAL_ERROR;
1581 goto error;
1584 centry = wcache_fetch(cache, domain, "SN/%s",
1585 sid_to_fstring(tmp, &sid));
1586 if (!centry) {
1587 goto do_query;
1590 (*types)[i] = SID_NAME_UNKNOWN;
1591 (*names)[i] = talloc_strdup(*names, "");
1593 if (NT_STATUS_IS_OK(centry->status)) {
1594 char *dom;
1595 have_mapped = true;
1596 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1598 dom = centry_string(centry, mem_ctx);
1599 if (*domain_name == NULL) {
1600 *domain_name = dom;
1601 } else {
1602 talloc_free(dom);
1605 (*names)[i] = centry_string(centry, *names);
1607 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1608 have_unmapped = true;
1610 } else {
1611 /* something's definitely wrong */
1612 result = centry->status;
1613 goto error;
1616 centry_free(centry);
1619 if (!have_mapped) {
1620 return NT_STATUS_NONE_MAPPED;
1622 if (!have_unmapped) {
1623 return NT_STATUS_OK;
1625 return STATUS_SOME_UNMAPPED;
1627 do_query:
1629 TALLOC_FREE(*names);
1630 TALLOC_FREE(*types);
1632 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1633 rids, num_rids, domain_name,
1634 names, types);
1637 None of the queried rids has been found so save all negative entries
1639 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1640 for (i = 0; i < num_rids; i++) {
1641 DOM_SID sid;
1642 const char *name = "";
1643 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1644 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1646 if (!sid_compose(&sid, domain_sid, rids[i])) {
1647 return NT_STATUS_INTERNAL_ERROR;
1650 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1651 name, type);
1654 return result;
1658 Some or all of the queried rids have been found.
1660 if (!NT_STATUS_IS_OK(result) &&
1661 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1662 return result;
1665 refresh_sequence_number(domain, false);
1667 for (i=0; i<num_rids; i++) {
1668 DOM_SID sid;
1669 NTSTATUS status;
1671 if (!sid_compose(&sid, domain_sid, rids[i])) {
1672 result = NT_STATUS_INTERNAL_ERROR;
1673 goto error;
1676 status = (*types)[i] == SID_NAME_UNKNOWN ?
1677 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1679 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1680 (*names)[i], (*types)[i]);
1683 return result;
1685 error:
1687 TALLOC_FREE(*names);
1688 TALLOC_FREE(*types);
1689 return result;
1692 /* Lookup user information from a rid */
1693 static NTSTATUS query_user(struct winbindd_domain *domain,
1694 TALLOC_CTX *mem_ctx,
1695 const DOM_SID *user_sid,
1696 WINBIND_USERINFO *info)
1698 struct winbind_cache *cache = get_cache(domain);
1699 struct cache_entry *centry = NULL;
1700 NTSTATUS status;
1701 fstring tmp;
1703 if (!cache->tdb)
1704 goto do_query;
1706 centry = wcache_fetch(cache, domain, "U/%s",
1707 sid_to_fstring(tmp, user_sid));
1709 /* If we have an access denied cache entry and a cached info3 in the
1710 samlogon cache then do a query. This will force the rpc back end
1711 to return the info3 data. */
1713 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1714 netsamlogon_cache_have(user_sid)) {
1715 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1716 domain->last_status = NT_STATUS_OK;
1717 centry_free(centry);
1718 goto do_query;
1721 if (!centry)
1722 goto do_query;
1724 /* if status is not ok then this is a negative hit
1725 and the rest of the data doesn't matter */
1726 status = centry->status;
1727 if (NT_STATUS_IS_OK(status)) {
1728 info->acct_name = centry_string(centry, mem_ctx);
1729 info->full_name = centry_string(centry, mem_ctx);
1730 info->homedir = centry_string(centry, mem_ctx);
1731 info->shell = centry_string(centry, mem_ctx);
1732 info->primary_gid = centry_uint32(centry);
1733 centry_sid(centry, mem_ctx, &info->user_sid);
1734 centry_sid(centry, mem_ctx, &info->group_sid);
1737 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1738 domain->name, nt_errstr(status) ));
1740 centry_free(centry);
1741 return status;
1743 do_query:
1744 ZERO_STRUCTP(info);
1746 /* Return status value returned by seq number check */
1748 if (!NT_STATUS_IS_OK(domain->last_status))
1749 return domain->last_status;
1751 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1752 domain->name ));
1754 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1756 /* and save it */
1757 refresh_sequence_number(domain, false);
1758 wcache_save_user(domain, status, info);
1760 return status;
1764 /* Lookup groups a user is a member of. */
1765 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1766 TALLOC_CTX *mem_ctx,
1767 const DOM_SID *user_sid,
1768 uint32 *num_groups, DOM_SID **user_gids)
1770 struct winbind_cache *cache = get_cache(domain);
1771 struct cache_entry *centry = NULL;
1772 NTSTATUS status;
1773 unsigned int i;
1774 fstring sid_string;
1776 if (!cache->tdb)
1777 goto do_query;
1779 centry = wcache_fetch(cache, domain, "UG/%s",
1780 sid_to_fstring(sid_string, user_sid));
1782 /* If we have an access denied cache entry and a cached info3 in the
1783 samlogon cache then do a query. This will force the rpc back end
1784 to return the info3 data. */
1786 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1787 netsamlogon_cache_have(user_sid)) {
1788 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1789 domain->last_status = NT_STATUS_OK;
1790 centry_free(centry);
1791 goto do_query;
1794 if (!centry)
1795 goto do_query;
1797 *num_groups = centry_uint32(centry);
1799 if (*num_groups == 0)
1800 goto do_cached;
1802 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1803 if (! (*user_gids)) {
1804 smb_panic_fn("lookup_usergroups out of memory");
1806 for (i=0; i<(*num_groups); i++) {
1807 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1810 do_cached:
1811 status = centry->status;
1813 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1814 domain->name, nt_errstr(status) ));
1816 centry_free(centry);
1817 return status;
1819 do_query:
1820 (*num_groups) = 0;
1821 (*user_gids) = NULL;
1823 /* Return status value returned by seq number check */
1825 if (!NT_STATUS_IS_OK(domain->last_status))
1826 return domain->last_status;
1828 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1829 domain->name ));
1831 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1833 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1834 goto skip_save;
1836 /* and save it */
1837 refresh_sequence_number(domain, false);
1838 centry = centry_start(domain, status);
1839 if (!centry)
1840 goto skip_save;
1842 centry_put_uint32(centry, *num_groups);
1843 for (i=0; i<(*num_groups); i++) {
1844 centry_put_sid(centry, &(*user_gids)[i]);
1847 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
1848 centry_free(centry);
1850 skip_save:
1851 return status;
1854 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1855 TALLOC_CTX *mem_ctx,
1856 uint32 num_sids, const DOM_SID *sids,
1857 uint32 *num_aliases, uint32 **alias_rids)
1859 struct winbind_cache *cache = get_cache(domain);
1860 struct cache_entry *centry = NULL;
1861 NTSTATUS status;
1862 char *sidlist = talloc_strdup(mem_ctx, "");
1863 int i;
1865 if (!cache->tdb)
1866 goto do_query;
1868 if (num_sids == 0) {
1869 *num_aliases = 0;
1870 *alias_rids = NULL;
1871 return NT_STATUS_OK;
1874 /* We need to cache indexed by the whole list of SIDs, the aliases
1875 * resulting might come from any of the SIDs. */
1877 for (i=0; i<num_sids; i++) {
1878 fstring tmp;
1879 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1880 sid_to_fstring(tmp, &sids[i]));
1881 if (sidlist == NULL)
1882 return NT_STATUS_NO_MEMORY;
1885 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1887 if (!centry)
1888 goto do_query;
1890 *num_aliases = centry_uint32(centry);
1891 *alias_rids = NULL;
1893 if (*num_aliases) {
1894 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1896 if ((*alias_rids) == NULL) {
1897 centry_free(centry);
1898 return NT_STATUS_NO_MEMORY;
1900 } else {
1901 (*alias_rids) = NULL;
1904 for (i=0; i<(*num_aliases); i++)
1905 (*alias_rids)[i] = centry_uint32(centry);
1907 status = centry->status;
1909 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1910 "status %s\n", domain->name, nt_errstr(status)));
1912 centry_free(centry);
1913 return status;
1915 do_query:
1916 (*num_aliases) = 0;
1917 (*alias_rids) = NULL;
1919 if (!NT_STATUS_IS_OK(domain->last_status))
1920 return domain->last_status;
1922 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1923 "for domain %s\n", domain->name ));
1925 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1926 num_sids, sids,
1927 num_aliases, alias_rids);
1929 /* and save it */
1930 refresh_sequence_number(domain, false);
1931 centry = centry_start(domain, status);
1932 if (!centry)
1933 goto skip_save;
1934 centry_put_uint32(centry, *num_aliases);
1935 for (i=0; i<(*num_aliases); i++)
1936 centry_put_uint32(centry, (*alias_rids)[i]);
1937 centry_end(centry, "UA%s", sidlist);
1938 centry_free(centry);
1940 skip_save:
1941 return status;
1945 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1946 TALLOC_CTX *mem_ctx,
1947 const DOM_SID *group_sid, uint32 *num_names,
1948 DOM_SID **sid_mem, char ***names,
1949 uint32 **name_types)
1951 struct winbind_cache *cache = get_cache(domain);
1952 struct cache_entry *centry = NULL;
1953 NTSTATUS status;
1954 unsigned int i;
1955 fstring sid_string;
1957 if (!cache->tdb)
1958 goto do_query;
1960 centry = wcache_fetch(cache, domain, "GM/%s",
1961 sid_to_fstring(sid_string, group_sid));
1962 if (!centry)
1963 goto do_query;
1965 *num_names = centry_uint32(centry);
1967 if (*num_names == 0)
1968 goto do_cached;
1970 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1971 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1972 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1974 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1975 smb_panic_fn("lookup_groupmem out of memory");
1978 for (i=0; i<(*num_names); i++) {
1979 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1980 (*names)[i] = centry_string(centry, mem_ctx);
1981 (*name_types)[i] = centry_uint32(centry);
1984 do_cached:
1985 status = centry->status;
1987 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1988 domain->name, nt_errstr(status)));
1990 centry_free(centry);
1991 return status;
1993 do_query:
1994 (*num_names) = 0;
1995 (*sid_mem) = NULL;
1996 (*names) = NULL;
1997 (*name_types) = NULL;
1999 /* Return status value returned by seq number check */
2001 if (!NT_STATUS_IS_OK(domain->last_status))
2002 return domain->last_status;
2004 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2005 domain->name ));
2007 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2008 sid_mem, names, name_types);
2010 /* and save it */
2011 refresh_sequence_number(domain, false);
2012 centry = centry_start(domain, status);
2013 if (!centry)
2014 goto skip_save;
2015 centry_put_uint32(centry, *num_names);
2016 for (i=0; i<(*num_names); i++) {
2017 centry_put_sid(centry, &(*sid_mem)[i]);
2018 centry_put_string(centry, (*names)[i]);
2019 centry_put_uint32(centry, (*name_types)[i]);
2021 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2022 centry_free(centry);
2024 skip_save:
2025 return status;
2028 /* find the sequence number for a domain */
2029 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2031 refresh_sequence_number(domain, false);
2033 *seq = domain->sequence_number;
2035 return NT_STATUS_OK;
2038 /* enumerate trusted domains
2039 * (we need to have the list of trustdoms in the cache when we go offline) -
2040 * Guenther */
2041 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2042 TALLOC_CTX *mem_ctx,
2043 uint32 *num_domains,
2044 char ***names,
2045 char ***alt_names,
2046 DOM_SID **dom_sids)
2048 struct winbind_cache *cache = get_cache(domain);
2049 struct cache_entry *centry = NULL;
2050 NTSTATUS status;
2051 int i;
2053 if (!cache->tdb)
2054 goto do_query;
2056 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2058 if (!centry) {
2059 goto do_query;
2062 *num_domains = centry_uint32(centry);
2064 if (*num_domains) {
2065 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2066 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2067 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2069 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2070 smb_panic_fn("trusted_domains out of memory");
2072 } else {
2073 (*names) = NULL;
2074 (*alt_names) = NULL;
2075 (*dom_sids) = NULL;
2078 for (i=0; i<(*num_domains); i++) {
2079 (*names)[i] = centry_string(centry, mem_ctx);
2080 (*alt_names)[i] = centry_string(centry, mem_ctx);
2081 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2082 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2086 status = centry->status;
2088 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2089 domain->name, *num_domains, nt_errstr(status) ));
2091 centry_free(centry);
2092 return status;
2094 do_query:
2095 (*num_domains) = 0;
2096 (*dom_sids) = NULL;
2097 (*names) = NULL;
2098 (*alt_names) = NULL;
2100 /* Return status value returned by seq number check */
2102 if (!NT_STATUS_IS_OK(domain->last_status))
2103 return domain->last_status;
2105 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2106 domain->name ));
2108 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2109 names, alt_names, dom_sids);
2111 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2112 * so that the generic centry handling still applies correctly -
2113 * Guenther*/
2115 if (!NT_STATUS_IS_ERR(status)) {
2116 status = NT_STATUS_OK;
2120 #if 0 /* Disabled as we want the trust dom list to be managed by
2121 the main parent and always to make the query. --jerry */
2123 /* and save it */
2124 refresh_sequence_number(domain, false);
2126 centry = centry_start(domain, status);
2127 if (!centry)
2128 goto skip_save;
2130 centry_put_uint32(centry, *num_domains);
2132 for (i=0; i<(*num_domains); i++) {
2133 centry_put_string(centry, (*names)[i]);
2134 centry_put_string(centry, (*alt_names)[i]);
2135 centry_put_sid(centry, &(*dom_sids)[i]);
2138 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2140 centry_free(centry);
2142 skip_save:
2143 #endif
2145 return status;
2148 /* get lockout policy */
2149 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2150 TALLOC_CTX *mem_ctx,
2151 struct samr_DomInfo12 *policy)
2153 struct winbind_cache *cache = get_cache(domain);
2154 struct cache_entry *centry = NULL;
2155 NTSTATUS status;
2157 if (!cache->tdb)
2158 goto do_query;
2160 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2162 if (!centry)
2163 goto do_query;
2165 policy->lockout_duration = centry_nttime(centry);
2166 policy->lockout_window = centry_nttime(centry);
2167 policy->lockout_threshold = centry_uint16(centry);
2169 status = centry->status;
2171 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2172 domain->name, nt_errstr(status) ));
2174 centry_free(centry);
2175 return status;
2177 do_query:
2178 ZERO_STRUCTP(policy);
2180 /* Return status value returned by seq number check */
2182 if (!NT_STATUS_IS_OK(domain->last_status))
2183 return domain->last_status;
2185 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2186 domain->name ));
2188 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2190 /* and save it */
2191 refresh_sequence_number(domain, false);
2192 wcache_save_lockout_policy(domain, status, policy);
2194 return status;
2197 /* get password policy */
2198 static NTSTATUS password_policy(struct winbindd_domain *domain,
2199 TALLOC_CTX *mem_ctx,
2200 struct samr_DomInfo1 *policy)
2202 struct winbind_cache *cache = get_cache(domain);
2203 struct cache_entry *centry = NULL;
2204 NTSTATUS status;
2206 if (!cache->tdb)
2207 goto do_query;
2209 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2211 if (!centry)
2212 goto do_query;
2214 policy->min_password_length = centry_uint16(centry);
2215 policy->password_history_length = centry_uint16(centry);
2216 policy->password_properties = centry_uint32(centry);
2217 policy->max_password_age = centry_nttime(centry);
2218 policy->min_password_age = centry_nttime(centry);
2220 status = centry->status;
2222 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2223 domain->name, nt_errstr(status) ));
2225 centry_free(centry);
2226 return status;
2228 do_query:
2229 ZERO_STRUCTP(policy);
2231 /* Return status value returned by seq number check */
2233 if (!NT_STATUS_IS_OK(domain->last_status))
2234 return domain->last_status;
2236 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2237 domain->name ));
2239 status = domain->backend->password_policy(domain, mem_ctx, policy);
2241 /* and save it */
2242 refresh_sequence_number(domain, false);
2243 if (NT_STATUS_IS_OK(status)) {
2244 wcache_save_password_policy(domain, status, policy);
2247 return status;
2251 /* Invalidate cached user and group lists coherently */
2253 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2254 void *state)
2256 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2257 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2258 tdb_delete(the_tdb, kbuf);
2260 return 0;
2263 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2265 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2266 struct netr_SamInfo3 *info3)
2268 DOM_SID sid;
2269 fstring key_str, sid_string;
2270 struct winbind_cache *cache;
2272 /* dont clear cached U/SID and UG/SID entries when we want to logon
2273 * offline - gd */
2275 if (lp_winbind_offline_logon()) {
2276 return;
2279 if (!domain)
2280 return;
2282 cache = get_cache(domain);
2284 if (!cache->tdb) {
2285 return;
2288 sid_copy(&sid, info3->base.domain_sid);
2289 sid_append_rid(&sid, info3->base.rid);
2291 /* Clear U/SID cache entry */
2292 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2293 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2294 tdb_delete(cache->tdb, string_tdb_data(key_str));
2296 /* Clear UG/SID cache entry */
2297 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2298 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2299 tdb_delete(cache->tdb, string_tdb_data(key_str));
2301 /* Samba/winbindd never needs this. */
2302 netsamlogon_clear_cached_user(info3);
2305 bool wcache_invalidate_cache(void)
2307 struct winbindd_domain *domain;
2309 for (domain = domain_list(); domain; domain = domain->next) {
2310 struct winbind_cache *cache = get_cache(domain);
2312 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2313 "entries for %s\n", domain->name));
2314 if (cache) {
2315 if (cache->tdb) {
2316 tdb_traverse(cache->tdb, traverse_fn, NULL);
2317 } else {
2318 return false;
2322 return true;
2325 bool init_wcache(void)
2327 if (wcache == NULL) {
2328 wcache = SMB_XMALLOC_P(struct winbind_cache);
2329 ZERO_STRUCTP(wcache);
2332 if (wcache->tdb != NULL)
2333 return true;
2335 /* when working offline we must not clear the cache on restart */
2336 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2337 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2338 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2339 O_RDWR|O_CREAT, 0600);
2341 if (wcache->tdb == NULL) {
2342 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2343 return false;
2346 return true;
2349 /************************************************************************
2350 This is called by the parent to initialize the cache file.
2351 We don't need sophisticated locking here as we know we're the
2352 only opener.
2353 ************************************************************************/
2355 bool initialize_winbindd_cache(void)
2357 bool cache_bad = true;
2358 uint32 vers;
2360 if (!init_wcache()) {
2361 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2362 return false;
2365 /* Check version number. */
2366 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2367 vers == WINBINDD_CACHE_VERSION) {
2368 cache_bad = false;
2371 if (cache_bad) {
2372 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2373 "and re-creating with version number %d\n",
2374 WINBINDD_CACHE_VERSION ));
2376 tdb_close(wcache->tdb);
2377 wcache->tdb = NULL;
2379 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2380 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2381 lock_path("winbindd_cache.tdb"),
2382 strerror(errno) ));
2383 return false;
2385 if (!init_wcache()) {
2386 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2387 "init_wcache failed.\n"));
2388 return false;
2391 /* Write the version. */
2392 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2393 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2394 tdb_errorstr(wcache->tdb) ));
2395 return false;
2399 tdb_close(wcache->tdb);
2400 wcache->tdb = NULL;
2401 return true;
2404 void close_winbindd_cache(void)
2406 if (!wcache) {
2407 return;
2409 if (wcache->tdb) {
2410 tdb_close(wcache->tdb);
2411 wcache->tdb = NULL;
2415 void cache_store_response(pid_t pid, struct winbindd_response *response)
2417 fstring key_str;
2419 if (!init_wcache())
2420 return;
2422 DEBUG(10, ("Storing response for pid %d, len %d\n",
2423 pid, response->length));
2425 fstr_sprintf(key_str, "DR/%d", pid);
2426 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2427 make_tdb_data((uint8 *)response, sizeof(*response)),
2428 TDB_REPLACE) == -1)
2429 return;
2431 if (response->length == sizeof(*response))
2432 return;
2434 /* There's extra data */
2436 DEBUG(10, ("Storing extra data: len=%d\n",
2437 (int)(response->length - sizeof(*response))));
2439 fstr_sprintf(key_str, "DE/%d", pid);
2440 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2441 make_tdb_data((uint8 *)response->extra_data.data,
2442 response->length - sizeof(*response)),
2443 TDB_REPLACE) == 0)
2444 return;
2446 /* We could not store the extra data, make sure the tdb does not
2447 * contain a main record with wrong dangling extra data */
2449 fstr_sprintf(key_str, "DR/%d", pid);
2450 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2452 return;
2455 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2457 TDB_DATA data;
2458 fstring key_str;
2460 if (!init_wcache())
2461 return false;
2463 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2465 fstr_sprintf(key_str, "DR/%d", pid);
2466 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2468 if (data.dptr == NULL)
2469 return false;
2471 if (data.dsize != sizeof(*response))
2472 return false;
2474 memcpy(response, data.dptr, data.dsize);
2475 SAFE_FREE(data.dptr);
2477 if (response->length == sizeof(*response)) {
2478 response->extra_data.data = NULL;
2479 return true;
2482 /* There's extra data */
2484 DEBUG(10, ("Retrieving extra data length=%d\n",
2485 (int)(response->length - sizeof(*response))));
2487 fstr_sprintf(key_str, "DE/%d", pid);
2488 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2490 if (data.dptr == NULL) {
2491 DEBUG(0, ("Did not find extra data\n"));
2492 return false;
2495 if (data.dsize != (response->length - sizeof(*response))) {
2496 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2497 SAFE_FREE(data.dptr);
2498 return false;
2501 dump_data(11, (uint8 *)data.dptr, data.dsize);
2503 response->extra_data.data = data.dptr;
2504 return true;
2507 void cache_cleanup_response(pid_t pid)
2509 fstring key_str;
2511 if (!init_wcache())
2512 return;
2514 fstr_sprintf(key_str, "DR/%d", pid);
2515 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2517 fstr_sprintf(key_str, "DE/%d", pid);
2518 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2520 return;
2524 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2525 char **domain_name, char **name,
2526 enum lsa_SidType *type)
2528 struct winbindd_domain *domain;
2529 struct winbind_cache *cache;
2530 struct cache_entry *centry = NULL;
2531 NTSTATUS status;
2532 fstring tmp;
2534 domain = find_lookup_domain_from_sid(sid);
2535 if (domain == NULL) {
2536 return false;
2539 cache = get_cache(domain);
2541 if (cache->tdb == NULL) {
2542 return false;
2545 centry = wcache_fetch(cache, domain, "SN/%s",
2546 sid_to_fstring(tmp, sid));
2547 if (centry == NULL) {
2548 return false;
2551 if (NT_STATUS_IS_OK(centry->status)) {
2552 *type = (enum lsa_SidType)centry_uint32(centry);
2553 *domain_name = centry_string(centry, mem_ctx);
2554 *name = centry_string(centry, mem_ctx);
2557 status = centry->status;
2558 centry_free(centry);
2559 return NT_STATUS_IS_OK(status);
2562 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2563 const char *domain_name,
2564 const char *name,
2565 DOM_SID *sid,
2566 enum lsa_SidType *type)
2568 struct winbindd_domain *domain;
2569 struct winbind_cache *cache;
2570 struct cache_entry *centry = NULL;
2571 NTSTATUS status;
2572 fstring uname;
2573 bool original_online_state;
2575 domain = find_lookup_domain_from_name(domain_name);
2576 if (domain == NULL) {
2577 return false;
2580 cache = get_cache(domain);
2582 if (cache->tdb == NULL) {
2583 return false;
2586 fstrcpy(uname, name);
2587 strupper_m(uname);
2589 /* If we are doing a cached logon, temporarily set the domain
2590 offline so the cache won't expire the entry */
2592 original_online_state = domain->online;
2593 domain->online = false;
2594 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2595 domain->online = original_online_state;
2597 if (centry == NULL) {
2598 return false;
2601 if (NT_STATUS_IS_OK(centry->status)) {
2602 *type = (enum lsa_SidType)centry_uint32(centry);
2603 centry_sid(centry, mem_ctx, sid);
2606 status = centry->status;
2607 centry_free(centry);
2609 return NT_STATUS_IS_OK(status);
2612 void cache_name2sid(struct winbindd_domain *domain,
2613 const char *domain_name, const char *name,
2614 enum lsa_SidType type, const DOM_SID *sid)
2616 refresh_sequence_number(domain, false);
2617 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2618 sid, type);
2622 * The original idea that this cache only contains centries has
2623 * been blurred - now other stuff gets put in here. Ensure we
2624 * ignore these things on cleanup.
2627 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2628 TDB_DATA dbuf, void *state)
2630 struct cache_entry *centry;
2632 if (is_non_centry_key(kbuf)) {
2633 return 0;
2636 centry = wcache_fetch_raw((char *)kbuf.dptr);
2637 if (!centry) {
2638 return 0;
2641 if (!NT_STATUS_IS_OK(centry->status)) {
2642 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2643 tdb_delete(the_tdb, kbuf);
2646 centry_free(centry);
2647 return 0;
2650 /* flush the cache */
2651 void wcache_flush_cache(void)
2653 if (!wcache)
2654 return;
2655 if (wcache->tdb) {
2656 tdb_close(wcache->tdb);
2657 wcache->tdb = NULL;
2659 if (opt_nocache)
2660 return;
2662 /* when working offline we must not clear the cache on restart */
2663 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2664 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2665 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2666 O_RDWR|O_CREAT, 0600);
2668 if (!wcache->tdb) {
2669 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2670 return;
2673 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2675 DEBUG(10,("wcache_flush_cache success\n"));
2678 /* Count cached creds */
2680 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2681 void *state)
2683 int *cred_count = (int*)state;
2685 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2686 (*cred_count)++;
2688 return 0;
2691 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2693 struct winbind_cache *cache = get_cache(domain);
2695 *count = 0;
2697 if (!cache->tdb) {
2698 return NT_STATUS_INTERNAL_DB_ERROR;
2701 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2703 return NT_STATUS_OK;
2706 struct cred_list {
2707 struct cred_list *prev, *next;
2708 TDB_DATA key;
2709 fstring name;
2710 time_t created;
2712 static struct cred_list *wcache_cred_list;
2714 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2715 void *state)
2717 struct cred_list *cred;
2719 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2721 cred = SMB_MALLOC_P(struct cred_list);
2722 if (cred == NULL) {
2723 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2724 return -1;
2727 ZERO_STRUCTP(cred);
2729 /* save a copy of the key */
2731 fstrcpy(cred->name, (const char *)kbuf.dptr);
2732 DLIST_ADD(wcache_cred_list, cred);
2735 return 0;
2738 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2740 struct winbind_cache *cache = get_cache(domain);
2741 NTSTATUS status;
2742 int ret;
2743 struct cred_list *cred, *oldest = NULL;
2745 if (!cache->tdb) {
2746 return NT_STATUS_INTERNAL_DB_ERROR;
2749 /* we possibly already have an entry */
2750 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2752 fstring key_str, tmp;
2754 DEBUG(11,("we already have an entry, deleting that\n"));
2756 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2758 tdb_delete(cache->tdb, string_tdb_data(key_str));
2760 return NT_STATUS_OK;
2763 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2764 if (ret == 0) {
2765 return NT_STATUS_OK;
2766 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2767 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2770 ZERO_STRUCTP(oldest);
2772 for (cred = wcache_cred_list; cred; cred = cred->next) {
2774 TDB_DATA data;
2775 time_t t;
2777 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2778 if (!data.dptr) {
2779 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2780 cred->name));
2781 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2782 goto done;
2785 t = IVAL(data.dptr, 0);
2786 SAFE_FREE(data.dptr);
2788 if (!oldest) {
2789 oldest = SMB_MALLOC_P(struct cred_list);
2790 if (oldest == NULL) {
2791 status = NT_STATUS_NO_MEMORY;
2792 goto done;
2795 fstrcpy(oldest->name, cred->name);
2796 oldest->created = t;
2797 continue;
2800 if (t < oldest->created) {
2801 fstrcpy(oldest->name, cred->name);
2802 oldest->created = t;
2806 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2807 status = NT_STATUS_OK;
2808 } else {
2809 status = NT_STATUS_UNSUCCESSFUL;
2811 done:
2812 SAFE_FREE(wcache_cred_list);
2813 SAFE_FREE(oldest);
2815 return status;
2818 /* Change the global online/offline state. */
2819 bool set_global_winbindd_state_offline(void)
2821 TDB_DATA data;
2823 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2825 /* Only go offline if someone has created
2826 the key "WINBINDD_OFFLINE" in the cache tdb. */
2828 if (wcache == NULL || wcache->tdb == NULL) {
2829 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2830 return false;
2833 if (!lp_winbind_offline_logon()) {
2834 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2835 return false;
2838 if (global_winbindd_offline_state) {
2839 /* Already offline. */
2840 return true;
2843 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2845 if (!data.dptr || data.dsize != 4) {
2846 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2847 SAFE_FREE(data.dptr);
2848 return false;
2849 } else {
2850 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2851 global_winbindd_offline_state = true;
2852 SAFE_FREE(data.dptr);
2853 return true;
2857 void set_global_winbindd_state_online(void)
2859 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2861 if (!lp_winbind_offline_logon()) {
2862 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2863 return;
2866 if (!global_winbindd_offline_state) {
2867 /* Already online. */
2868 return;
2870 global_winbindd_offline_state = false;
2872 if (!wcache->tdb) {
2873 return;
2876 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2877 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2880 bool get_global_winbindd_state_offline(void)
2882 return global_winbindd_offline_state;
2885 /***********************************************************************
2886 Validate functions for all possible cache tdb keys.
2887 ***********************************************************************/
2889 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2890 struct tdb_validation_status *state)
2892 struct cache_entry *centry;
2894 centry = SMB_XMALLOC_P(struct cache_entry);
2895 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2896 if (!centry->data) {
2897 SAFE_FREE(centry);
2898 return NULL;
2900 centry->len = data.dsize;
2901 centry->ofs = 0;
2903 if (centry->len < 8) {
2904 /* huh? corrupt cache? */
2905 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2906 centry_free(centry);
2907 state->bad_entry = true;
2908 state->success = false;
2909 return NULL;
2912 centry->status = NT_STATUS(centry_uint32(centry));
2913 centry->sequence_number = centry_uint32(centry);
2914 return centry;
2917 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2918 struct tdb_validation_status *state)
2920 if (dbuf.dsize != 8) {
2921 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2922 keystr, (unsigned int)dbuf.dsize ));
2923 state->bad_entry = true;
2924 return 1;
2926 return 0;
2929 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2930 struct tdb_validation_status *state)
2932 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2933 if (!centry) {
2934 return 1;
2937 (void)centry_uint32(centry);
2938 if (NT_STATUS_IS_OK(centry->status)) {
2939 DOM_SID sid;
2940 (void)centry_sid(centry, mem_ctx, &sid);
2943 centry_free(centry);
2945 if (!(state->success)) {
2946 return 1;
2948 DEBUG(10,("validate_ns: %s ok\n", keystr));
2949 return 0;
2952 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2953 struct tdb_validation_status *state)
2955 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2956 if (!centry) {
2957 return 1;
2960 if (NT_STATUS_IS_OK(centry->status)) {
2961 (void)centry_uint32(centry);
2962 (void)centry_string(centry, mem_ctx);
2963 (void)centry_string(centry, mem_ctx);
2966 centry_free(centry);
2968 if (!(state->success)) {
2969 return 1;
2971 DEBUG(10,("validate_sn: %s ok\n", keystr));
2972 return 0;
2975 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2976 struct tdb_validation_status *state)
2978 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2979 DOM_SID sid;
2981 if (!centry) {
2982 return 1;
2985 (void)centry_string(centry, mem_ctx);
2986 (void)centry_string(centry, mem_ctx);
2987 (void)centry_string(centry, mem_ctx);
2988 (void)centry_string(centry, mem_ctx);
2989 (void)centry_uint32(centry);
2990 (void)centry_sid(centry, mem_ctx, &sid);
2991 (void)centry_sid(centry, mem_ctx, &sid);
2993 centry_free(centry);
2995 if (!(state->success)) {
2996 return 1;
2998 DEBUG(10,("validate_u: %s ok\n", keystr));
2999 return 0;
3002 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3003 struct tdb_validation_status *state)
3005 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3007 if (!centry) {
3008 return 1;
3011 (void)centry_nttime(centry);
3012 (void)centry_nttime(centry);
3013 (void)centry_uint16(centry);
3015 centry_free(centry);
3017 if (!(state->success)) {
3018 return 1;
3020 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3021 return 0;
3024 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3025 struct tdb_validation_status *state)
3027 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3029 if (!centry) {
3030 return 1;
3033 (void)centry_uint16(centry);
3034 (void)centry_uint16(centry);
3035 (void)centry_uint32(centry);
3036 (void)centry_nttime(centry);
3037 (void)centry_nttime(centry);
3039 centry_free(centry);
3041 if (!(state->success)) {
3042 return 1;
3044 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3045 return 0;
3048 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3049 struct tdb_validation_status *state)
3051 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3053 if (!centry) {
3054 return 1;
3057 (void)centry_time(centry);
3058 (void)centry_hash16(centry, mem_ctx);
3060 /* We only have 17 bytes more data in the salted cred case. */
3061 if (centry->len - centry->ofs == 17) {
3062 (void)centry_hash16(centry, mem_ctx);
3065 centry_free(centry);
3067 if (!(state->success)) {
3068 return 1;
3070 DEBUG(10,("validate_cred: %s ok\n", keystr));
3071 return 0;
3074 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3075 struct tdb_validation_status *state)
3077 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3078 int32 num_entries, i;
3080 if (!centry) {
3081 return 1;
3084 num_entries = (int32)centry_uint32(centry);
3086 for (i=0; i< num_entries; i++) {
3087 DOM_SID sid;
3088 (void)centry_string(centry, mem_ctx);
3089 (void)centry_string(centry, mem_ctx);
3090 (void)centry_string(centry, mem_ctx);
3091 (void)centry_string(centry, mem_ctx);
3092 (void)centry_sid(centry, mem_ctx, &sid);
3093 (void)centry_sid(centry, mem_ctx, &sid);
3096 centry_free(centry);
3098 if (!(state->success)) {
3099 return 1;
3101 DEBUG(10,("validate_ul: %s ok\n", keystr));
3102 return 0;
3105 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3106 struct tdb_validation_status *state)
3108 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3109 int32 num_entries, i;
3111 if (!centry) {
3112 return 1;
3115 num_entries = centry_uint32(centry);
3117 for (i=0; i< num_entries; i++) {
3118 (void)centry_string(centry, mem_ctx);
3119 (void)centry_string(centry, mem_ctx);
3120 (void)centry_uint32(centry);
3123 centry_free(centry);
3125 if (!(state->success)) {
3126 return 1;
3128 DEBUG(10,("validate_gl: %s ok\n", keystr));
3129 return 0;
3132 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3133 struct tdb_validation_status *state)
3135 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3136 int32 num_groups, i;
3138 if (!centry) {
3139 return 1;
3142 num_groups = centry_uint32(centry);
3144 for (i=0; i< num_groups; i++) {
3145 DOM_SID sid;
3146 centry_sid(centry, mem_ctx, &sid);
3149 centry_free(centry);
3151 if (!(state->success)) {
3152 return 1;
3154 DEBUG(10,("validate_ug: %s ok\n", keystr));
3155 return 0;
3158 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3159 struct tdb_validation_status *state)
3161 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3162 int32 num_aliases, i;
3164 if (!centry) {
3165 return 1;
3168 num_aliases = centry_uint32(centry);
3170 for (i=0; i < num_aliases; i++) {
3171 (void)centry_uint32(centry);
3174 centry_free(centry);
3176 if (!(state->success)) {
3177 return 1;
3179 DEBUG(10,("validate_ua: %s ok\n", keystr));
3180 return 0;
3183 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3184 struct tdb_validation_status *state)
3186 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3187 int32 num_names, i;
3189 if (!centry) {
3190 return 1;
3193 num_names = centry_uint32(centry);
3195 for (i=0; i< num_names; i++) {
3196 DOM_SID sid;
3197 centry_sid(centry, mem_ctx, &sid);
3198 (void)centry_string(centry, mem_ctx);
3199 (void)centry_uint32(centry);
3202 centry_free(centry);
3204 if (!(state->success)) {
3205 return 1;
3207 DEBUG(10,("validate_gm: %s ok\n", keystr));
3208 return 0;
3211 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3212 struct tdb_validation_status *state)
3214 /* Can't say anything about this other than must be nonzero. */
3215 if (dbuf.dsize == 0) {
3216 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3217 keystr));
3218 state->bad_entry = true;
3219 state->success = false;
3220 return 1;
3223 DEBUG(10,("validate_dr: %s ok\n", keystr));
3224 return 0;
3227 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3228 struct tdb_validation_status *state)
3230 /* Can't say anything about this other than must be nonzero. */
3231 if (dbuf.dsize == 0) {
3232 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3233 keystr));
3234 state->bad_entry = true;
3235 state->success = false;
3236 return 1;
3239 DEBUG(10,("validate_de: %s ok\n", keystr));
3240 return 0;
3243 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3244 TDB_DATA dbuf, struct tdb_validation_status *state)
3246 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3248 if (!centry) {
3249 return 1;
3252 (void)centry_string(centry, mem_ctx);
3253 (void)centry_string(centry, mem_ctx);
3254 (void)centry_string(centry, mem_ctx);
3255 (void)centry_uint32(centry);
3257 centry_free(centry);
3259 if (!(state->success)) {
3260 return 1;
3262 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3263 return 0;
3266 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3267 struct tdb_validation_status *state)
3269 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3270 int32 num_domains, i;
3272 if (!centry) {
3273 return 1;
3276 num_domains = centry_uint32(centry);
3278 for (i=0; i< num_domains; i++) {
3279 DOM_SID sid;
3280 (void)centry_string(centry, mem_ctx);
3281 (void)centry_string(centry, mem_ctx);
3282 (void)centry_sid(centry, mem_ctx, &sid);
3285 centry_free(centry);
3287 if (!(state->success)) {
3288 return 1;
3290 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3291 return 0;
3294 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3295 TDB_DATA dbuf,
3296 struct tdb_validation_status *state)
3298 if (dbuf.dsize == 0) {
3299 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3300 "key %s (len ==0) ?\n", keystr));
3301 state->bad_entry = true;
3302 state->success = false;
3303 return 1;
3306 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3307 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3308 return 0;
3311 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3312 struct tdb_validation_status *state)
3314 if (dbuf.dsize != 4) {
3315 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3316 keystr, (unsigned int)dbuf.dsize ));
3317 state->bad_entry = true;
3318 state->success = false;
3319 return 1;
3321 DEBUG(10,("validate_offline: %s ok\n", keystr));
3322 return 0;
3325 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3326 struct tdb_validation_status *state)
3328 if (dbuf.dsize != 4) {
3329 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3330 "key %s (len %u != 4) ?\n",
3331 keystr, (unsigned int)dbuf.dsize));
3332 state->bad_entry = true;
3333 state->success = false;
3334 return 1;
3337 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3338 return 0;
3341 /***********************************************************************
3342 A list of all possible cache tdb keys with associated validation
3343 functions.
3344 ***********************************************************************/
3346 struct key_val_struct {
3347 const char *keyname;
3348 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3349 } key_val[] = {
3350 {"SEQNUM/", validate_seqnum},
3351 {"NS/", validate_ns},
3352 {"SN/", validate_sn},
3353 {"U/", validate_u},
3354 {"LOC_POL/", validate_loc_pol},
3355 {"PWD_POL/", validate_pwd_pol},
3356 {"CRED/", validate_cred},
3357 {"UL/", validate_ul},
3358 {"GL/", validate_gl},
3359 {"UG/", validate_ug},
3360 {"UA", validate_ua},
3361 {"GM/", validate_gm},
3362 {"DR/", validate_dr},
3363 {"DE/", validate_de},
3364 {"NSS/PWINFO/", validate_pwinfo},
3365 {"TRUSTDOMS/", validate_trustdoms},
3366 {"TRUSTDOMCACHE/", validate_trustdomcache},
3367 {"WINBINDD_OFFLINE", validate_offline},
3368 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3369 {NULL, NULL}
3372 /***********************************************************************
3373 Function to look at every entry in the tdb and validate it as far as
3374 possible.
3375 ***********************************************************************/
3377 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3379 int i;
3380 unsigned int max_key_len = 1024;
3381 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3383 /* Paranoia check. */
3384 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3385 max_key_len = 1024 * 1024;
3387 if (kbuf.dsize > max_key_len) {
3388 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3389 "(%u) > (%u)\n\n",
3390 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3391 return 1;
3394 for (i = 0; key_val[i].keyname; i++) {
3395 size_t namelen = strlen(key_val[i].keyname);
3396 if (kbuf.dsize >= namelen && (
3397 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3398 TALLOC_CTX *mem_ctx;
3399 char *keystr;
3400 int ret;
3402 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3403 if (!keystr) {
3404 return 1;
3406 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3407 keystr[kbuf.dsize] = '\0';
3409 mem_ctx = talloc_init("validate_ctx");
3410 if (!mem_ctx) {
3411 SAFE_FREE(keystr);
3412 return 1;
3415 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3416 v_state);
3418 SAFE_FREE(keystr);
3419 talloc_destroy(mem_ctx);
3420 return ret;
3424 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3425 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3426 DEBUG(0,("data :\n"));
3427 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3428 v_state->unknown_key = true;
3429 v_state->success = false;
3430 return 1; /* terminate. */
3433 static void validate_panic(const char *const why)
3435 DEBUG(0,("validating cache: would panic %s\n", why ));
3436 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3437 exit(47);
3440 /***********************************************************************
3441 Try and validate every entry in the winbindd cache. If we fail here,
3442 delete the cache tdb and return non-zero.
3443 ***********************************************************************/
3445 int winbindd_validate_cache(void)
3447 int ret = -1;
3448 const char *tdb_path = lock_path("winbindd_cache.tdb");
3449 TDB_CONTEXT *tdb = NULL;
3451 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3452 smb_panic_fn = validate_panic;
3455 tdb = tdb_open_log(tdb_path,
3456 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3457 ( lp_winbind_offline_logon()
3458 ? TDB_DEFAULT
3459 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3460 O_RDWR|O_CREAT,
3461 0600);
3462 if (!tdb) {
3463 DEBUG(0, ("winbindd_validate_cache: "
3464 "error opening/initializing tdb\n"));
3465 goto done;
3467 tdb_close(tdb);
3469 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3471 if (ret != 0) {
3472 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3473 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3474 unlink(tdb_path);
3477 done:
3478 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3479 smb_panic_fn = smb_panic;
3480 return ret;
3483 /***********************************************************************
3484 Try and validate every entry in the winbindd cache.
3485 ***********************************************************************/
3487 int winbindd_validate_cache_nobackup(void)
3489 int ret = -1;
3490 const char *tdb_path = lock_path("winbindd_cache.tdb");
3492 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3493 smb_panic_fn = validate_panic;
3496 if (wcache == NULL || wcache->tdb == NULL) {
3497 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3498 } else {
3499 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3502 if (ret != 0) {
3503 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3504 "successful.\n"));
3507 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3508 "function\n"));
3509 smb_panic_fn = smb_panic;
3510 return ret;
3513 bool winbindd_cache_validate_and_initialize(void)
3515 close_winbindd_cache();
3517 if (lp_winbind_offline_logon()) {
3518 if (winbindd_validate_cache() < 0) {
3519 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3520 "could be restored.\n"));
3524 return initialize_winbindd_cache();
3527 /*********************************************************************
3528 ********************************************************************/
3530 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3531 struct winbindd_tdc_domain **domains,
3532 size_t *num_domains )
3534 struct winbindd_tdc_domain *list = NULL;
3535 size_t idx;
3536 int i;
3537 bool set_only = false;
3539 /* don't allow duplicates */
3541 idx = *num_domains;
3542 list = *domains;
3544 for ( i=0; i< (*num_domains); i++ ) {
3545 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3546 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3547 new_dom->name));
3548 idx = i;
3549 set_only = true;
3551 break;
3555 if ( !set_only ) {
3556 if ( !*domains ) {
3557 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3558 idx = 0;
3559 } else {
3560 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3561 struct winbindd_tdc_domain,
3562 (*num_domains)+1);
3563 idx = *num_domains;
3566 ZERO_STRUCT( list[idx] );
3569 if ( !list )
3570 return false;
3572 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3573 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3575 if ( !is_null_sid( &new_dom->sid ) ) {
3576 sid_copy( &list[idx].sid, &new_dom->sid );
3577 } else {
3578 sid_copy(&list[idx].sid, &global_sid_NULL);
3581 if ( new_dom->domain_flags != 0x0 )
3582 list[idx].trust_flags = new_dom->domain_flags;
3584 if ( new_dom->domain_type != 0x0 )
3585 list[idx].trust_type = new_dom->domain_type;
3587 if ( new_dom->domain_trust_attribs != 0x0 )
3588 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3590 if ( !set_only ) {
3591 *domains = list;
3592 *num_domains = idx + 1;
3595 return true;
3598 /*********************************************************************
3599 ********************************************************************/
3601 static TDB_DATA make_tdc_key( const char *domain_name )
3603 char *keystr = NULL;
3604 TDB_DATA key = { NULL, 0 };
3606 if ( !domain_name ) {
3607 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3608 return key;
3612 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3613 key = string_term_tdb_data(keystr);
3615 return key;
3618 /*********************************************************************
3619 ********************************************************************/
3621 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3622 size_t num_domains,
3623 unsigned char **buf )
3625 unsigned char *buffer = NULL;
3626 int len = 0;
3627 int buflen = 0;
3628 int i = 0;
3630 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3631 (int)num_domains));
3633 buflen = 0;
3635 again:
3636 len = 0;
3638 /* Store the number of array items first */
3639 len += tdb_pack( buffer+len, buflen-len, "d",
3640 num_domains );
3642 /* now pack each domain trust record */
3643 for ( i=0; i<num_domains; i++ ) {
3645 fstring tmp;
3647 if ( buflen > 0 ) {
3648 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3649 domains[i].domain_name,
3650 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3653 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3654 domains[i].domain_name,
3655 domains[i].dns_name,
3656 sid_to_fstring(tmp, &domains[i].sid),
3657 domains[i].trust_flags,
3658 domains[i].trust_attribs,
3659 domains[i].trust_type );
3662 if ( buflen < len ) {
3663 SAFE_FREE(buffer);
3664 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3665 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3666 buflen = -1;
3667 goto done;
3669 buflen = len;
3670 goto again;
3673 *buf = buffer;
3675 done:
3676 return buflen;
3679 /*********************************************************************
3680 ********************************************************************/
3682 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3683 struct winbindd_tdc_domain **domains )
3685 fstring domain_name, dns_name, sid_string;
3686 uint32 type, attribs, flags;
3687 int num_domains;
3688 int len = 0;
3689 int i;
3690 struct winbindd_tdc_domain *list = NULL;
3692 /* get the number of domains */
3693 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3694 if ( len == -1 ) {
3695 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3696 return 0;
3699 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3700 if ( !list ) {
3701 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3702 return 0;
3705 for ( i=0; i<num_domains; i++ ) {
3706 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3707 domain_name,
3708 dns_name,
3709 sid_string,
3710 &flags,
3711 &attribs,
3712 &type );
3714 if ( len == -1 ) {
3715 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3716 TALLOC_FREE( list );
3717 return 0;
3720 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3721 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3722 domain_name, dns_name, sid_string,
3723 flags, attribs, type));
3725 list[i].domain_name = talloc_strdup( list, domain_name );
3726 list[i].dns_name = talloc_strdup( list, dns_name );
3727 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3728 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3729 domain_name));
3731 list[i].trust_flags = flags;
3732 list[i].trust_attribs = attribs;
3733 list[i].trust_type = type;
3736 *domains = list;
3738 return num_domains;
3741 /*********************************************************************
3742 ********************************************************************/
3744 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3746 TDB_DATA key = make_tdc_key( lp_workgroup() );
3747 TDB_DATA data = { NULL, 0 };
3748 int ret;
3750 if ( !key.dptr )
3751 return false;
3753 /* See if we were asked to delete the cache entry */
3755 if ( !domains ) {
3756 ret = tdb_delete( wcache->tdb, key );
3757 goto done;
3760 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3762 if ( !data.dptr ) {
3763 ret = -1;
3764 goto done;
3767 ret = tdb_store( wcache->tdb, key, data, 0 );
3769 done:
3770 SAFE_FREE( data.dptr );
3771 SAFE_FREE( key.dptr );
3773 return ( ret != -1 );
3776 /*********************************************************************
3777 ********************************************************************/
3779 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3781 TDB_DATA key = make_tdc_key( lp_workgroup() );
3782 TDB_DATA data = { NULL, 0 };
3784 *domains = NULL;
3785 *num_domains = 0;
3787 if ( !key.dptr )
3788 return false;
3790 data = tdb_fetch( wcache->tdb, key );
3792 SAFE_FREE( key.dptr );
3794 if ( !data.dptr )
3795 return false;
3797 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3799 SAFE_FREE( data.dptr );
3801 if ( !*domains )
3802 return false;
3804 return true;
3807 /*********************************************************************
3808 ********************************************************************/
3810 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3812 struct winbindd_tdc_domain *dom_list = NULL;
3813 size_t num_domains = 0;
3814 bool ret = false;
3816 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3817 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3818 domain->name, domain->alt_name,
3819 sid_string_dbg(&domain->sid),
3820 domain->domain_flags,
3821 domain->domain_trust_attribs,
3822 domain->domain_type));
3824 if ( !init_wcache() ) {
3825 return false;
3828 /* fetch the list */
3830 wcache_tdc_fetch_list( &dom_list, &num_domains );
3832 /* add the new domain */
3834 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3835 goto done;
3838 /* pack the domain */
3840 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3841 goto done;
3844 /* Success */
3846 ret = true;
3847 done:
3848 TALLOC_FREE( dom_list );
3850 return ret;
3853 /*********************************************************************
3854 ********************************************************************/
3856 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3858 struct winbindd_tdc_domain *dom_list = NULL;
3859 size_t num_domains = 0;
3860 int i;
3861 struct winbindd_tdc_domain *d = NULL;
3863 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3865 if ( !init_wcache() ) {
3866 return false;
3869 /* fetch the list */
3871 wcache_tdc_fetch_list( &dom_list, &num_domains );
3873 for ( i=0; i<num_domains; i++ ) {
3874 if ( strequal(name, dom_list[i].domain_name) ||
3875 strequal(name, dom_list[i].dns_name) )
3877 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3878 name));
3880 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3881 if ( !d )
3882 break;
3884 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3885 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3886 sid_copy( &d->sid, &dom_list[i].sid );
3887 d->trust_flags = dom_list[i].trust_flags;
3888 d->trust_type = dom_list[i].trust_type;
3889 d->trust_attribs = dom_list[i].trust_attribs;
3891 break;
3895 TALLOC_FREE( dom_list );
3897 return d;
3901 /*********************************************************************
3902 ********************************************************************/
3904 void wcache_tdc_clear( void )
3906 if ( !init_wcache() )
3907 return;
3909 wcache_tdc_store_list( NULL, 0 );
3911 return;
3915 /*********************************************************************
3916 ********************************************************************/
3918 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3919 NTSTATUS status,
3920 const DOM_SID *user_sid,
3921 const char *homedir,
3922 const char *shell,
3923 const char *gecos,
3924 uint32 gid)
3926 struct cache_entry *centry;
3927 fstring tmp;
3929 if ( (centry = centry_start(domain, status)) == NULL )
3930 return;
3932 centry_put_string( centry, homedir );
3933 centry_put_string( centry, shell );
3934 centry_put_string( centry, gecos );
3935 centry_put_uint32( centry, gid );
3937 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
3939 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
3941 centry_free(centry);
3944 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3945 const DOM_SID *user_sid,
3946 TALLOC_CTX *ctx,
3947 ADS_STRUCT *ads, LDAPMessage *msg,
3948 char **homedir, char **shell, char **gecos,
3949 gid_t *p_gid)
3951 struct winbind_cache *cache = get_cache(domain);
3952 struct cache_entry *centry = NULL;
3953 NTSTATUS nt_status;
3954 fstring tmp;
3956 if (!cache->tdb)
3957 goto do_query;
3959 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
3960 sid_to_fstring(tmp, user_sid));
3962 if (!centry)
3963 goto do_query;
3965 *homedir = centry_string( centry, ctx );
3966 *shell = centry_string( centry, ctx );
3967 *gecos = centry_string( centry, ctx );
3968 *p_gid = centry_uint32( centry );
3970 centry_free(centry);
3972 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3973 sid_string_dbg(user_sid)));
3975 return NT_STATUS_OK;
3977 do_query:
3979 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3980 homedir, shell, gecos, p_gid );
3982 if ( NT_STATUS_IS_OK(nt_status) ) {
3983 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3984 *homedir, *shell, *gecos, *p_gid );
3987 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3988 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3989 domain->name ));
3990 set_domain_offline( domain );
3993 return nt_status;
3997 /* the cache backend methods are exposed via this structure */
3998 struct winbindd_methods cache_methods = {
3999 true,
4000 query_user_list,
4001 enum_dom_groups,
4002 enum_local_groups,
4003 name_to_sid,
4004 sid_to_name,
4005 rids_to_names,
4006 query_user,
4007 lookup_usergroups,
4008 lookup_useraliases,
4009 lookup_groupmem,
4010 sequence_number,
4011 lockout_policy,
4012 password_policy,
4013 trusted_domains