s3-groupdb: fix enum_aliasmem in ldb branch.
[Samba.git] / source / winbindd / winbindd_cache.c
blobbe44da4aa09fbc1c8cb04ea31049150b75abdd7f
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 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
491 NT_STATUS_IS_OK(domain->last_status)) {
492 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
493 goto done;
496 /* try to get the sequence number from the tdb cache first */
497 /* this will update the timestamp as well */
499 status = fetch_cache_seqnum( domain, t );
500 if (NT_STATUS_IS_OK(status) &&
501 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
502 NT_STATUS_IS_OK(domain->last_status)) {
503 goto done;
506 /* important! make sure that we know if this is a native
507 mode domain or not. And that we can contact it. */
509 if ( winbindd_can_contact_domain( domain ) ) {
510 struct winbindd_methods *orig_backend = domain->backend;
511 status = domain->backend->sequence_number(domain,
512 &domain->sequence_number);
513 if (domain->backend != orig_backend) {
514 /* Try again. */
515 status = domain->backend->sequence_number(domain,
516 &domain->sequence_number);
518 } else {
519 /* just use the current time */
520 status = NT_STATUS_OK;
521 domain->sequence_number = time(NULL);
525 /* the above call could have set our domain->backend to NULL when
526 * coming from offline to online mode, make sure to reinitialize the
527 * backend - Guenther */
528 get_cache( domain );
530 if (!NT_STATUS_IS_OK(status)) {
531 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
532 domain->sequence_number = DOM_SEQUENCE_NONE;
535 domain->last_status = status;
536 domain->last_seq_check = time(NULL);
538 /* save the new sequence number in the cache */
539 store_cache_seqnum( domain );
541 done:
542 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
543 domain->name, domain->sequence_number));
545 return;
549 decide if a cache entry has expired
551 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
553 /* If we've been told to be offline - stay in that state... */
554 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
555 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
556 keystr, domain->name ));
557 return false;
560 /* when the domain is offline return the cached entry.
561 * This deals with transient offline states... */
563 if (!domain->online) {
564 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
565 keystr, domain->name ));
566 return false;
569 /* if the server is OK and our cache entry came from when it was down then
570 the entry is invalid */
571 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
572 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
573 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
574 keystr, domain->name ));
575 return true;
578 /* if the server is down or the cache entry is not older than the
579 current sequence number then it is OK */
580 if (wcache_server_down(domain) ||
581 centry->sequence_number == domain->sequence_number) {
582 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
583 keystr, domain->name ));
584 return false;
587 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
588 keystr, domain->name ));
590 /* it's expired */
591 return true;
594 static struct cache_entry *wcache_fetch_raw(char *kstr)
596 TDB_DATA data;
597 struct cache_entry *centry;
598 TDB_DATA key;
600 key = string_tdb_data(kstr);
601 data = tdb_fetch(wcache->tdb, key);
602 if (!data.dptr) {
603 /* a cache miss */
604 return NULL;
607 centry = SMB_XMALLOC_P(struct cache_entry);
608 centry->data = (unsigned char *)data.dptr;
609 centry->len = data.dsize;
610 centry->ofs = 0;
612 if (centry->len < 8) {
613 /* huh? corrupt cache? */
614 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
615 centry_free(centry);
616 return NULL;
619 centry->status = centry_ntstatus(centry);
620 centry->sequence_number = centry_uint32(centry);
622 return centry;
626 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
627 number and return status
629 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
630 struct winbindd_domain *domain,
631 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
632 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
633 struct winbindd_domain *domain,
634 const char *format, ...)
636 va_list ap;
637 char *kstr;
638 struct cache_entry *centry;
640 if (opt_nocache) {
641 return NULL;
644 refresh_sequence_number(domain, false);
646 va_start(ap, format);
647 smb_xvasprintf(&kstr, format, ap);
648 va_end(ap);
650 centry = wcache_fetch_raw(kstr);
651 if (centry == NULL) {
652 free(kstr);
653 return NULL;
656 if (centry_expired(domain, kstr, centry)) {
658 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
659 kstr, domain->name ));
661 centry_free(centry);
662 free(kstr);
663 return NULL;
666 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
667 kstr, domain->name ));
669 free(kstr);
670 return centry;
673 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
674 static void wcache_delete(const char *format, ...)
676 va_list ap;
677 char *kstr;
678 TDB_DATA key;
680 va_start(ap, format);
681 smb_xvasprintf(&kstr, format, ap);
682 va_end(ap);
684 key = string_tdb_data(kstr);
686 tdb_delete(wcache->tdb, key);
687 free(kstr);
691 make sure we have at least len bytes available in a centry
693 static void centry_expand(struct cache_entry *centry, uint32 len)
695 if (centry->len - centry->ofs >= len)
696 return;
697 centry->len *= 2;
698 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
699 centry->len);
700 if (!centry->data) {
701 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
702 smb_panic_fn("out of memory in centry_expand");
707 push a uint32 into a centry
709 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
711 centry_expand(centry, 4);
712 SIVAL(centry->data, centry->ofs, v);
713 centry->ofs += 4;
717 push a uint16 into a centry
719 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
721 centry_expand(centry, 2);
722 SIVAL(centry->data, centry->ofs, v);
723 centry->ofs += 2;
727 push a uint8 into a centry
729 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
731 centry_expand(centry, 1);
732 SCVAL(centry->data, centry->ofs, v);
733 centry->ofs += 1;
737 push a string into a centry
739 static void centry_put_string(struct cache_entry *centry, const char *s)
741 int len;
743 if (!s) {
744 /* null strings are marked as len 0xFFFF */
745 centry_put_uint8(centry, 0xFF);
746 return;
749 len = strlen(s);
750 /* can't handle more than 254 char strings. Truncating is probably best */
751 if (len > 254) {
752 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
753 len = 254;
755 centry_put_uint8(centry, len);
756 centry_expand(centry, len);
757 memcpy(centry->data + centry->ofs, s, len);
758 centry->ofs += len;
762 push a 16 byte hash into a centry - treat as 16 byte string.
764 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
766 centry_put_uint8(centry, 16);
767 centry_expand(centry, 16);
768 memcpy(centry->data + centry->ofs, val, 16);
769 centry->ofs += 16;
772 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
774 fstring sid_string;
775 centry_put_string(centry, sid_to_fstring(sid_string, sid));
780 put NTSTATUS into a centry
782 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
784 uint32 status_value = NT_STATUS_V(status);
785 centry_put_uint32(centry, status_value);
790 push a NTTIME into a centry
792 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
794 centry_expand(centry, 8);
795 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
796 centry->ofs += 4;
797 SIVAL(centry->data, centry->ofs, nt >> 32);
798 centry->ofs += 4;
802 push a time_t into a centry - use a 64 bit size.
803 NTTIME here is being used as a convenient 64-bit size.
805 static void centry_put_time(struct cache_entry *centry, time_t t)
807 NTTIME nt = (NTTIME)t;
808 centry_put_nttime(centry, nt);
812 start a centry for output. When finished, call centry_end()
814 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
816 struct cache_entry *centry;
818 if (!wcache->tdb)
819 return NULL;
821 centry = SMB_XMALLOC_P(struct cache_entry);
823 centry->len = 8192; /* reasonable default */
824 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
825 centry->ofs = 0;
826 centry->sequence_number = domain->sequence_number;
827 centry_put_ntstatus(centry, status);
828 centry_put_uint32(centry, centry->sequence_number);
829 return centry;
833 finish a centry and write it to the tdb
835 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
836 static void centry_end(struct cache_entry *centry, const char *format, ...)
838 va_list ap;
839 char *kstr;
840 TDB_DATA key, data;
842 if (opt_nocache) {
843 return;
846 va_start(ap, format);
847 smb_xvasprintf(&kstr, format, ap);
848 va_end(ap);
850 key = string_tdb_data(kstr);
851 data.dptr = centry->data;
852 data.dsize = centry->ofs;
854 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
855 free(kstr);
858 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
859 NTSTATUS status, const char *domain_name,
860 const char *name, const DOM_SID *sid,
861 enum lsa_SidType type)
863 struct cache_entry *centry;
864 fstring uname;
866 centry = centry_start(domain, status);
867 if (!centry)
868 return;
869 centry_put_uint32(centry, type);
870 centry_put_sid(centry, sid);
871 fstrcpy(uname, name);
872 strupper_m(uname);
873 centry_end(centry, "NS/%s/%s", domain_name, uname);
874 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
875 uname, sid_string_dbg(sid), nt_errstr(status)));
876 centry_free(centry);
879 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
880 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
882 struct cache_entry *centry;
883 fstring sid_string;
885 centry = centry_start(domain, status);
886 if (!centry)
887 return;
889 if (NT_STATUS_IS_OK(status)) {
890 centry_put_uint32(centry, type);
891 centry_put_string(centry, domain_name);
892 centry_put_string(centry, name);
895 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
896 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
897 name, nt_errstr(status)));
898 centry_free(centry);
902 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
904 struct cache_entry *centry;
905 fstring sid_string;
907 if (is_null_sid(&info->user_sid)) {
908 return;
911 centry = centry_start(domain, status);
912 if (!centry)
913 return;
914 centry_put_string(centry, info->acct_name);
915 centry_put_string(centry, info->full_name);
916 centry_put_string(centry, info->homedir);
917 centry_put_string(centry, info->shell);
918 centry_put_uint32(centry, info->primary_gid);
919 centry_put_sid(centry, &info->user_sid);
920 centry_put_sid(centry, &info->group_sid);
921 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
922 &info->user_sid));
923 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
924 centry_free(centry);
927 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
928 NTSTATUS status,
929 struct samr_DomInfo12 *lockout_policy)
931 struct cache_entry *centry;
933 centry = centry_start(domain, status);
934 if (!centry)
935 return;
937 centry_put_nttime(centry, lockout_policy->lockout_duration);
938 centry_put_nttime(centry, lockout_policy->lockout_window);
939 centry_put_uint16(centry, lockout_policy->lockout_threshold);
941 centry_end(centry, "LOC_POL/%s", domain->name);
943 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
945 centry_free(centry);
948 static void wcache_save_password_policy(struct winbindd_domain *domain,
949 NTSTATUS status,
950 struct samr_DomInfo1 *policy)
952 struct cache_entry *centry;
954 centry = centry_start(domain, status);
955 if (!centry)
956 return;
958 centry_put_uint16(centry, policy->min_password_length);
959 centry_put_uint16(centry, policy->password_history_length);
960 centry_put_uint32(centry, policy->password_properties);
961 centry_put_nttime(centry, policy->max_password_age);
962 centry_put_nttime(centry, policy->min_password_age);
964 centry_end(centry, "PWD_POL/%s", domain->name);
966 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
968 centry_free(centry);
971 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
973 struct winbind_cache *cache = get_cache(domain);
974 TDB_DATA data;
975 fstring key_str, tmp;
976 uint32 rid;
978 if (!cache->tdb) {
979 return NT_STATUS_INTERNAL_DB_ERROR;
982 if (is_null_sid(sid)) {
983 return NT_STATUS_INVALID_SID;
986 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
987 return NT_STATUS_INVALID_SID;
990 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
992 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
993 if (!data.dptr) {
994 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
997 SAFE_FREE(data.dptr);
998 return NT_STATUS_OK;
1001 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1002 as new salted ones. */
1004 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1005 TALLOC_CTX *mem_ctx,
1006 const DOM_SID *sid,
1007 const uint8 **cached_nt_pass,
1008 const uint8 **cached_salt)
1010 struct winbind_cache *cache = get_cache(domain);
1011 struct cache_entry *centry = NULL;
1012 NTSTATUS status;
1013 time_t t;
1014 uint32 rid;
1015 fstring tmp;
1017 if (!cache->tdb) {
1018 return NT_STATUS_INTERNAL_DB_ERROR;
1021 if (is_null_sid(sid)) {
1022 return NT_STATUS_INVALID_SID;
1025 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1026 return NT_STATUS_INVALID_SID;
1029 /* Try and get a salted cred first. If we can't
1030 fall back to an unsalted cred. */
1032 centry = wcache_fetch(cache, domain, "CRED/%s",
1033 sid_to_fstring(tmp, sid));
1034 if (!centry) {
1035 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1036 sid_string_dbg(sid)));
1037 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1040 t = centry_time(centry);
1042 /* In the salted case this isn't actually the nt_hash itself,
1043 but the MD5 of the salt + nt_hash. Let the caller
1044 sort this out. It can tell as we only return the cached_salt
1045 if we are returning a salted cred. */
1047 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1048 if (*cached_nt_pass == NULL) {
1049 fstring sidstr;
1051 sid_to_fstring(sidstr, sid);
1053 /* Bad (old) cred cache. Delete and pretend we
1054 don't have it. */
1055 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1056 sidstr));
1057 wcache_delete("CRED/%s", sidstr);
1058 centry_free(centry);
1059 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1062 /* We only have 17 bytes more data in the salted cred case. */
1063 if (centry->len - centry->ofs == 17) {
1064 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1065 } else {
1066 *cached_salt = NULL;
1069 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1070 if (*cached_salt) {
1071 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1074 status = centry->status;
1076 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1077 sid_string_dbg(sid), nt_errstr(status) ));
1079 centry_free(centry);
1080 return status;
1083 /* Store creds for a SID - only writes out new salted ones. */
1085 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1086 TALLOC_CTX *mem_ctx,
1087 const DOM_SID *sid,
1088 const uint8 nt_pass[NT_HASH_LEN])
1090 struct cache_entry *centry;
1091 fstring sid_string;
1092 uint32 rid;
1093 uint8 cred_salt[NT_HASH_LEN];
1094 uint8 salted_hash[NT_HASH_LEN];
1096 if (is_null_sid(sid)) {
1097 return NT_STATUS_INVALID_SID;
1100 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1101 return NT_STATUS_INVALID_SID;
1104 centry = centry_start(domain, NT_STATUS_OK);
1105 if (!centry) {
1106 return NT_STATUS_INTERNAL_DB_ERROR;
1109 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1111 centry_put_time(centry, time(NULL));
1113 /* Create a salt and then salt the hash. */
1114 generate_random_buffer(cred_salt, NT_HASH_LEN);
1115 E_md5hash(cred_salt, nt_pass, salted_hash);
1117 centry_put_hash16(centry, salted_hash);
1118 centry_put_hash16(centry, cred_salt);
1119 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1121 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1123 centry_free(centry);
1125 return NT_STATUS_OK;
1129 /* Query display info. This is the basic user list fn */
1130 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1131 TALLOC_CTX *mem_ctx,
1132 uint32 *num_entries,
1133 WINBIND_USERINFO **info)
1135 struct winbind_cache *cache = get_cache(domain);
1136 struct cache_entry *centry = NULL;
1137 NTSTATUS status;
1138 unsigned int i, retry;
1140 if (!cache->tdb)
1141 goto do_query;
1143 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1144 if (!centry)
1145 goto do_query;
1147 *num_entries = centry_uint32(centry);
1149 if (*num_entries == 0)
1150 goto do_cached;
1152 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1153 if (! (*info)) {
1154 smb_panic_fn("query_user_list out of memory");
1156 for (i=0; i<(*num_entries); i++) {
1157 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1158 (*info)[i].full_name = centry_string(centry, mem_ctx);
1159 (*info)[i].homedir = centry_string(centry, mem_ctx);
1160 (*info)[i].shell = centry_string(centry, mem_ctx);
1161 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1162 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1165 do_cached:
1166 status = centry->status;
1168 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1169 domain->name, nt_errstr(status) ));
1171 centry_free(centry);
1172 return status;
1174 do_query:
1175 *num_entries = 0;
1176 *info = NULL;
1178 /* Return status value returned by seq number check */
1180 if (!NT_STATUS_IS_OK(domain->last_status))
1181 return domain->last_status;
1183 /* Put the query_user_list() in a retry loop. There appears to be
1184 * some bug either with Windows 2000 or Samba's handling of large
1185 * rpc replies. This manifests itself as sudden disconnection
1186 * at a random point in the enumeration of a large (60k) user list.
1187 * The retry loop simply tries the operation again. )-: It's not
1188 * pretty but an acceptable workaround until we work out what the
1189 * real problem is. */
1191 retry = 0;
1192 do {
1194 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1195 domain->name ));
1197 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1198 if (!NT_STATUS_IS_OK(status)) {
1199 DEBUG(3, ("query_user_list: returned 0x%08x, "
1200 "retrying\n", NT_STATUS_V(status)));
1202 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1203 DEBUG(3, ("query_user_list: flushing "
1204 "connection cache\n"));
1205 invalidate_cm_connection(&domain->conn);
1208 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1209 (retry++ < 5));
1211 /* and save it */
1212 refresh_sequence_number(domain, false);
1213 centry = centry_start(domain, status);
1214 if (!centry)
1215 goto skip_save;
1216 centry_put_uint32(centry, *num_entries);
1217 for (i=0; i<(*num_entries); i++) {
1218 centry_put_string(centry, (*info)[i].acct_name);
1219 centry_put_string(centry, (*info)[i].full_name);
1220 centry_put_string(centry, (*info)[i].homedir);
1221 centry_put_string(centry, (*info)[i].shell);
1222 centry_put_sid(centry, &(*info)[i].user_sid);
1223 centry_put_sid(centry, &(*info)[i].group_sid);
1224 if (domain->backend && domain->backend->consistent) {
1225 /* when the backend is consistent we can pre-prime some mappings */
1226 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1227 domain->name,
1228 (*info)[i].acct_name,
1229 &(*info)[i].user_sid,
1230 SID_NAME_USER);
1231 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1232 &(*info)[i].user_sid,
1233 domain->name,
1234 (*info)[i].acct_name,
1235 SID_NAME_USER);
1236 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1239 centry_end(centry, "UL/%s", domain->name);
1240 centry_free(centry);
1242 skip_save:
1243 return status;
1246 /* list all domain groups */
1247 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1248 TALLOC_CTX *mem_ctx,
1249 uint32 *num_entries,
1250 struct acct_info **info)
1252 struct winbind_cache *cache = get_cache(domain);
1253 struct cache_entry *centry = NULL;
1254 NTSTATUS status;
1255 unsigned int i;
1257 if (!cache->tdb)
1258 goto do_query;
1260 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1261 if (!centry)
1262 goto do_query;
1264 *num_entries = centry_uint32(centry);
1266 if (*num_entries == 0)
1267 goto do_cached;
1269 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1270 if (! (*info)) {
1271 smb_panic_fn("enum_dom_groups out of memory");
1273 for (i=0; i<(*num_entries); i++) {
1274 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1275 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1276 (*info)[i].rid = centry_uint32(centry);
1279 do_cached:
1280 status = centry->status;
1282 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1283 domain->name, nt_errstr(status) ));
1285 centry_free(centry);
1286 return status;
1288 do_query:
1289 *num_entries = 0;
1290 *info = NULL;
1292 /* Return status value returned by seq number check */
1294 if (!NT_STATUS_IS_OK(domain->last_status))
1295 return domain->last_status;
1297 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1298 domain->name ));
1300 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1302 /* and save it */
1303 refresh_sequence_number(domain, false);
1304 centry = centry_start(domain, status);
1305 if (!centry)
1306 goto skip_save;
1307 centry_put_uint32(centry, *num_entries);
1308 for (i=0; i<(*num_entries); i++) {
1309 centry_put_string(centry, (*info)[i].acct_name);
1310 centry_put_string(centry, (*info)[i].acct_desc);
1311 centry_put_uint32(centry, (*info)[i].rid);
1313 centry_end(centry, "GL/%s/domain", domain->name);
1314 centry_free(centry);
1316 skip_save:
1317 return status;
1320 /* list all domain groups */
1321 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1322 TALLOC_CTX *mem_ctx,
1323 uint32 *num_entries,
1324 struct acct_info **info)
1326 struct winbind_cache *cache = get_cache(domain);
1327 struct cache_entry *centry = NULL;
1328 NTSTATUS status;
1329 unsigned int i;
1331 if (!cache->tdb)
1332 goto do_query;
1334 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1335 if (!centry)
1336 goto do_query;
1338 *num_entries = centry_uint32(centry);
1340 if (*num_entries == 0)
1341 goto do_cached;
1343 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1344 if (! (*info)) {
1345 smb_panic_fn("enum_dom_groups out of memory");
1347 for (i=0; i<(*num_entries); i++) {
1348 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1349 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1350 (*info)[i].rid = centry_uint32(centry);
1353 do_cached:
1355 /* If we are returning cached data and the domain controller
1356 is down then we don't know whether the data is up to date
1357 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1358 indicate this. */
1360 if (wcache_server_down(domain)) {
1361 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1362 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1363 } else
1364 status = centry->status;
1366 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1367 domain->name, nt_errstr(status) ));
1369 centry_free(centry);
1370 return status;
1372 do_query:
1373 *num_entries = 0;
1374 *info = NULL;
1376 /* Return status value returned by seq number check */
1378 if (!NT_STATUS_IS_OK(domain->last_status))
1379 return domain->last_status;
1381 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1382 domain->name ));
1384 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1386 /* and save it */
1387 refresh_sequence_number(domain, false);
1388 centry = centry_start(domain, status);
1389 if (!centry)
1390 goto skip_save;
1391 centry_put_uint32(centry, *num_entries);
1392 for (i=0; i<(*num_entries); i++) {
1393 centry_put_string(centry, (*info)[i].acct_name);
1394 centry_put_string(centry, (*info)[i].acct_desc);
1395 centry_put_uint32(centry, (*info)[i].rid);
1397 centry_end(centry, "GL/%s/local", domain->name);
1398 centry_free(centry);
1400 skip_save:
1401 return status;
1404 /* convert a single name to a sid in a domain */
1405 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1406 TALLOC_CTX *mem_ctx,
1407 enum winbindd_cmd orig_cmd,
1408 const char *domain_name,
1409 const char *name,
1410 DOM_SID *sid,
1411 enum lsa_SidType *type)
1413 struct winbind_cache *cache = get_cache(domain);
1414 struct cache_entry *centry = NULL;
1415 NTSTATUS status;
1416 fstring uname;
1418 if (!cache->tdb)
1419 goto do_query;
1421 fstrcpy(uname, name);
1422 strupper_m(uname);
1423 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1424 if (!centry)
1425 goto do_query;
1427 status = centry->status;
1428 if (NT_STATUS_IS_OK(status)) {
1429 *type = (enum lsa_SidType)centry_uint32(centry);
1430 centry_sid(centry, mem_ctx, sid);
1433 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1434 domain->name, nt_errstr(status) ));
1436 centry_free(centry);
1437 return status;
1439 do_query:
1440 ZERO_STRUCTP(sid);
1442 /* If the seq number check indicated that there is a problem
1443 * with this DC, then return that status... except for
1444 * access_denied. This is special because the dc may be in
1445 * "restrict anonymous = 1" mode, in which case it will deny
1446 * most unauthenticated operations, but *will* allow the LSA
1447 * name-to-sid that we try as a fallback. */
1449 if (!(NT_STATUS_IS_OK(domain->last_status)
1450 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1451 return domain->last_status;
1453 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1454 domain->name ));
1456 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1457 domain_name, name, sid, type);
1459 /* and save it */
1460 refresh_sequence_number(domain, false);
1462 if (domain->online &&
1463 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1464 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1466 /* Only save the reverse mapping if this was not a UPN */
1467 if (!strchr(name, '@')) {
1468 strupper_m(CONST_DISCARD(char *,domain_name));
1469 strlower_m(CONST_DISCARD(char *,name));
1470 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1474 return status;
1477 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1478 given */
1479 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1480 TALLOC_CTX *mem_ctx,
1481 const DOM_SID *sid,
1482 char **domain_name,
1483 char **name,
1484 enum lsa_SidType *type)
1486 struct winbind_cache *cache = get_cache(domain);
1487 struct cache_entry *centry = NULL;
1488 NTSTATUS status;
1489 fstring sid_string;
1491 if (!cache->tdb)
1492 goto do_query;
1494 centry = wcache_fetch(cache, domain, "SN/%s",
1495 sid_to_fstring(sid_string, sid));
1496 if (!centry)
1497 goto do_query;
1499 status = centry->status;
1500 if (NT_STATUS_IS_OK(status)) {
1501 *type = (enum lsa_SidType)centry_uint32(centry);
1502 *domain_name = centry_string(centry, mem_ctx);
1503 *name = centry_string(centry, mem_ctx);
1506 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1507 domain->name, nt_errstr(status) ));
1509 centry_free(centry);
1510 return status;
1512 do_query:
1513 *name = NULL;
1514 *domain_name = NULL;
1516 /* If the seq number check indicated that there is a problem
1517 * with this DC, then return that status... except for
1518 * access_denied. This is special because the dc may be in
1519 * "restrict anonymous = 1" mode, in which case it will deny
1520 * most unauthenticated operations, but *will* allow the LSA
1521 * sid-to-name that we try as a fallback. */
1523 if (!(NT_STATUS_IS_OK(domain->last_status)
1524 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1525 return domain->last_status;
1527 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1528 domain->name ));
1530 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1532 /* and save it */
1533 refresh_sequence_number(domain, false);
1534 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1536 /* We can't save the name to sid mapping here, as with sid history a
1537 * later name2sid would give the wrong sid. */
1539 return status;
1542 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1543 TALLOC_CTX *mem_ctx,
1544 const DOM_SID *domain_sid,
1545 uint32 *rids,
1546 size_t num_rids,
1547 char **domain_name,
1548 char ***names,
1549 enum lsa_SidType **types)
1551 struct winbind_cache *cache = get_cache(domain);
1552 size_t i;
1553 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1554 bool have_mapped;
1555 bool have_unmapped;
1557 *domain_name = NULL;
1558 *names = NULL;
1559 *types = NULL;
1561 if (!cache->tdb) {
1562 goto do_query;
1565 if (num_rids == 0) {
1566 return NT_STATUS_OK;
1569 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1570 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1572 if ((*names == NULL) || (*types == NULL)) {
1573 result = NT_STATUS_NO_MEMORY;
1574 goto error;
1577 have_mapped = have_unmapped = false;
1579 for (i=0; i<num_rids; i++) {
1580 DOM_SID sid;
1581 struct cache_entry *centry;
1582 fstring tmp;
1584 if (!sid_compose(&sid, domain_sid, rids[i])) {
1585 result = NT_STATUS_INTERNAL_ERROR;
1586 goto error;
1589 centry = wcache_fetch(cache, domain, "SN/%s",
1590 sid_to_fstring(tmp, &sid));
1591 if (!centry) {
1592 goto do_query;
1595 (*types)[i] = SID_NAME_UNKNOWN;
1596 (*names)[i] = talloc_strdup(*names, "");
1598 if (NT_STATUS_IS_OK(centry->status)) {
1599 char *dom;
1600 have_mapped = true;
1601 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1603 dom = centry_string(centry, mem_ctx);
1604 if (*domain_name == NULL) {
1605 *domain_name = dom;
1606 } else {
1607 talloc_free(dom);
1610 (*names)[i] = centry_string(centry, *names);
1612 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1613 have_unmapped = true;
1615 } else {
1616 /* something's definitely wrong */
1617 result = centry->status;
1618 goto error;
1621 centry_free(centry);
1624 if (!have_mapped) {
1625 return NT_STATUS_NONE_MAPPED;
1627 if (!have_unmapped) {
1628 return NT_STATUS_OK;
1630 return STATUS_SOME_UNMAPPED;
1632 do_query:
1634 TALLOC_FREE(*names);
1635 TALLOC_FREE(*types);
1637 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1638 rids, num_rids, domain_name,
1639 names, types);
1642 None of the queried rids has been found so save all negative entries
1644 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1645 for (i = 0; i < num_rids; i++) {
1646 DOM_SID sid;
1647 const char *name = "";
1648 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1649 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1651 if (!sid_compose(&sid, domain_sid, rids[i])) {
1652 return NT_STATUS_INTERNAL_ERROR;
1655 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1656 name, type);
1659 return result;
1663 Some or all of the queried rids have been found.
1665 if (!NT_STATUS_IS_OK(result) &&
1666 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1667 return result;
1670 refresh_sequence_number(domain, false);
1672 for (i=0; i<num_rids; i++) {
1673 DOM_SID sid;
1674 NTSTATUS status;
1676 if (!sid_compose(&sid, domain_sid, rids[i])) {
1677 result = NT_STATUS_INTERNAL_ERROR;
1678 goto error;
1681 status = (*types)[i] == SID_NAME_UNKNOWN ?
1682 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1684 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1685 (*names)[i], (*types)[i]);
1688 return result;
1690 error:
1692 TALLOC_FREE(*names);
1693 TALLOC_FREE(*types);
1694 return result;
1697 /* Lookup user information from a rid */
1698 static NTSTATUS query_user(struct winbindd_domain *domain,
1699 TALLOC_CTX *mem_ctx,
1700 const DOM_SID *user_sid,
1701 WINBIND_USERINFO *info)
1703 struct winbind_cache *cache = get_cache(domain);
1704 struct cache_entry *centry = NULL;
1705 NTSTATUS status;
1706 fstring tmp;
1708 if (!cache->tdb)
1709 goto do_query;
1711 centry = wcache_fetch(cache, domain, "U/%s",
1712 sid_to_fstring(tmp, user_sid));
1714 /* If we have an access denied cache entry and a cached info3 in the
1715 samlogon cache then do a query. This will force the rpc back end
1716 to return the info3 data. */
1718 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1719 netsamlogon_cache_have(user_sid)) {
1720 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1721 domain->last_status = NT_STATUS_OK;
1722 centry_free(centry);
1723 goto do_query;
1726 if (!centry)
1727 goto do_query;
1729 /* if status is not ok then this is a negative hit
1730 and the rest of the data doesn't matter */
1731 status = centry->status;
1732 if (NT_STATUS_IS_OK(status)) {
1733 info->acct_name = centry_string(centry, mem_ctx);
1734 info->full_name = centry_string(centry, mem_ctx);
1735 info->homedir = centry_string(centry, mem_ctx);
1736 info->shell = centry_string(centry, mem_ctx);
1737 info->primary_gid = centry_uint32(centry);
1738 centry_sid(centry, mem_ctx, &info->user_sid);
1739 centry_sid(centry, mem_ctx, &info->group_sid);
1742 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1743 domain->name, nt_errstr(status) ));
1745 centry_free(centry);
1746 return status;
1748 do_query:
1749 ZERO_STRUCTP(info);
1751 /* Return status value returned by seq number check */
1753 if (!NT_STATUS_IS_OK(domain->last_status))
1754 return domain->last_status;
1756 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1757 domain->name ));
1759 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1761 /* and save it */
1762 refresh_sequence_number(domain, false);
1763 wcache_save_user(domain, status, info);
1765 return status;
1769 /* Lookup groups a user is a member of. */
1770 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1771 TALLOC_CTX *mem_ctx,
1772 const DOM_SID *user_sid,
1773 uint32 *num_groups, DOM_SID **user_gids)
1775 struct winbind_cache *cache = get_cache(domain);
1776 struct cache_entry *centry = NULL;
1777 NTSTATUS status;
1778 unsigned int i;
1779 fstring sid_string;
1781 if (!cache->tdb)
1782 goto do_query;
1784 centry = wcache_fetch(cache, domain, "UG/%s",
1785 sid_to_fstring(sid_string, user_sid));
1787 /* If we have an access denied cache entry and a cached info3 in the
1788 samlogon cache then do a query. This will force the rpc back end
1789 to return the info3 data. */
1791 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1792 netsamlogon_cache_have(user_sid)) {
1793 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1794 domain->last_status = NT_STATUS_OK;
1795 centry_free(centry);
1796 goto do_query;
1799 if (!centry)
1800 goto do_query;
1802 *num_groups = centry_uint32(centry);
1804 if (*num_groups == 0)
1805 goto do_cached;
1807 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1808 if (! (*user_gids)) {
1809 smb_panic_fn("lookup_usergroups out of memory");
1811 for (i=0; i<(*num_groups); i++) {
1812 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1815 do_cached:
1816 status = centry->status;
1818 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1819 domain->name, nt_errstr(status) ));
1821 centry_free(centry);
1822 return status;
1824 do_query:
1825 (*num_groups) = 0;
1826 (*user_gids) = NULL;
1828 /* Return status value returned by seq number check */
1830 if (!NT_STATUS_IS_OK(domain->last_status))
1831 return domain->last_status;
1833 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1834 domain->name ));
1836 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1838 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1839 goto skip_save;
1841 /* and save it */
1842 refresh_sequence_number(domain, false);
1843 centry = centry_start(domain, status);
1844 if (!centry)
1845 goto skip_save;
1847 centry_put_uint32(centry, *num_groups);
1848 for (i=0; i<(*num_groups); i++) {
1849 centry_put_sid(centry, &(*user_gids)[i]);
1852 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
1853 centry_free(centry);
1855 skip_save:
1856 return status;
1859 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1860 TALLOC_CTX *mem_ctx,
1861 uint32 num_sids, const DOM_SID *sids,
1862 uint32 *num_aliases, uint32 **alias_rids)
1864 struct winbind_cache *cache = get_cache(domain);
1865 struct cache_entry *centry = NULL;
1866 NTSTATUS status;
1867 char *sidlist = talloc_strdup(mem_ctx, "");
1868 int i;
1870 if (!cache->tdb)
1871 goto do_query;
1873 if (num_sids == 0) {
1874 *num_aliases = 0;
1875 *alias_rids = NULL;
1876 return NT_STATUS_OK;
1879 /* We need to cache indexed by the whole list of SIDs, the aliases
1880 * resulting might come from any of the SIDs. */
1882 for (i=0; i<num_sids; i++) {
1883 fstring tmp;
1884 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1885 sid_to_fstring(tmp, &sids[i]));
1886 if (sidlist == NULL)
1887 return NT_STATUS_NO_MEMORY;
1890 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1892 if (!centry)
1893 goto do_query;
1895 *num_aliases = centry_uint32(centry);
1896 *alias_rids = NULL;
1898 if (*num_aliases) {
1899 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1901 if ((*alias_rids) == NULL) {
1902 centry_free(centry);
1903 return NT_STATUS_NO_MEMORY;
1905 } else {
1906 (*alias_rids) = NULL;
1909 for (i=0; i<(*num_aliases); i++)
1910 (*alias_rids)[i] = centry_uint32(centry);
1912 status = centry->status;
1914 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1915 "status %s\n", domain->name, nt_errstr(status)));
1917 centry_free(centry);
1918 return status;
1920 do_query:
1921 (*num_aliases) = 0;
1922 (*alias_rids) = NULL;
1924 if (!NT_STATUS_IS_OK(domain->last_status))
1925 return domain->last_status;
1927 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1928 "for domain %s\n", domain->name ));
1930 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1931 num_sids, sids,
1932 num_aliases, alias_rids);
1934 /* and save it */
1935 refresh_sequence_number(domain, false);
1936 centry = centry_start(domain, status);
1937 if (!centry)
1938 goto skip_save;
1939 centry_put_uint32(centry, *num_aliases);
1940 for (i=0; i<(*num_aliases); i++)
1941 centry_put_uint32(centry, (*alias_rids)[i]);
1942 centry_end(centry, "UA%s", sidlist);
1943 centry_free(centry);
1945 skip_save:
1946 return status;
1950 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1951 TALLOC_CTX *mem_ctx,
1952 const DOM_SID *group_sid, uint32 *num_names,
1953 DOM_SID **sid_mem, char ***names,
1954 uint32 **name_types)
1956 struct winbind_cache *cache = get_cache(domain);
1957 struct cache_entry *centry = NULL;
1958 NTSTATUS status;
1959 unsigned int i;
1960 fstring sid_string;
1962 if (!cache->tdb)
1963 goto do_query;
1965 centry = wcache_fetch(cache, domain, "GM/%s",
1966 sid_to_fstring(sid_string, group_sid));
1967 if (!centry)
1968 goto do_query;
1970 *num_names = centry_uint32(centry);
1972 if (*num_names == 0)
1973 goto do_cached;
1975 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1976 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1977 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1979 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1980 smb_panic_fn("lookup_groupmem out of memory");
1983 for (i=0; i<(*num_names); i++) {
1984 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1985 (*names)[i] = centry_string(centry, mem_ctx);
1986 (*name_types)[i] = centry_uint32(centry);
1989 do_cached:
1990 status = centry->status;
1992 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1993 domain->name, nt_errstr(status)));
1995 centry_free(centry);
1996 return status;
1998 do_query:
1999 (*num_names) = 0;
2000 (*sid_mem) = NULL;
2001 (*names) = NULL;
2002 (*name_types) = NULL;
2004 /* Return status value returned by seq number check */
2006 if (!NT_STATUS_IS_OK(domain->last_status))
2007 return domain->last_status;
2009 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2010 domain->name ));
2012 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2013 sid_mem, names, name_types);
2015 /* and save it */
2016 refresh_sequence_number(domain, false);
2017 centry = centry_start(domain, status);
2018 if (!centry)
2019 goto skip_save;
2020 centry_put_uint32(centry, *num_names);
2021 for (i=0; i<(*num_names); i++) {
2022 centry_put_sid(centry, &(*sid_mem)[i]);
2023 centry_put_string(centry, (*names)[i]);
2024 centry_put_uint32(centry, (*name_types)[i]);
2026 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2027 centry_free(centry);
2029 skip_save:
2030 return status;
2033 /* find the sequence number for a domain */
2034 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2036 refresh_sequence_number(domain, false);
2038 *seq = domain->sequence_number;
2040 return NT_STATUS_OK;
2043 /* enumerate trusted domains
2044 * (we need to have the list of trustdoms in the cache when we go offline) -
2045 * Guenther */
2046 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2047 TALLOC_CTX *mem_ctx,
2048 uint32 *num_domains,
2049 char ***names,
2050 char ***alt_names,
2051 DOM_SID **dom_sids)
2053 struct winbind_cache *cache = get_cache(domain);
2054 struct cache_entry *centry = NULL;
2055 NTSTATUS status;
2056 int i;
2058 if (!cache->tdb)
2059 goto do_query;
2061 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2063 if (!centry) {
2064 goto do_query;
2067 *num_domains = centry_uint32(centry);
2069 if (*num_domains) {
2070 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2071 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2072 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2074 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2075 smb_panic_fn("trusted_domains out of memory");
2077 } else {
2078 (*names) = NULL;
2079 (*alt_names) = NULL;
2080 (*dom_sids) = NULL;
2083 for (i=0; i<(*num_domains); i++) {
2084 (*names)[i] = centry_string(centry, mem_ctx);
2085 (*alt_names)[i] = centry_string(centry, mem_ctx);
2086 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2087 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2091 status = centry->status;
2093 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2094 domain->name, *num_domains, nt_errstr(status) ));
2096 centry_free(centry);
2097 return status;
2099 do_query:
2100 (*num_domains) = 0;
2101 (*dom_sids) = NULL;
2102 (*names) = NULL;
2103 (*alt_names) = NULL;
2105 /* Return status value returned by seq number check */
2107 if (!NT_STATUS_IS_OK(domain->last_status))
2108 return domain->last_status;
2110 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2111 domain->name ));
2113 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2114 names, alt_names, dom_sids);
2116 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2117 * so that the generic centry handling still applies correctly -
2118 * Guenther*/
2120 if (!NT_STATUS_IS_ERR(status)) {
2121 status = NT_STATUS_OK;
2125 #if 0 /* Disabled as we want the trust dom list to be managed by
2126 the main parent and always to make the query. --jerry */
2128 /* and save it */
2129 refresh_sequence_number(domain, false);
2131 centry = centry_start(domain, status);
2132 if (!centry)
2133 goto skip_save;
2135 centry_put_uint32(centry, *num_domains);
2137 for (i=0; i<(*num_domains); i++) {
2138 centry_put_string(centry, (*names)[i]);
2139 centry_put_string(centry, (*alt_names)[i]);
2140 centry_put_sid(centry, &(*dom_sids)[i]);
2143 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2145 centry_free(centry);
2147 skip_save:
2148 #endif
2150 return status;
2153 /* get lockout policy */
2154 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2155 TALLOC_CTX *mem_ctx,
2156 struct samr_DomInfo12 *policy)
2158 struct winbind_cache *cache = get_cache(domain);
2159 struct cache_entry *centry = NULL;
2160 NTSTATUS status;
2162 if (!cache->tdb)
2163 goto do_query;
2165 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2167 if (!centry)
2168 goto do_query;
2170 policy->lockout_duration = centry_nttime(centry);
2171 policy->lockout_window = centry_nttime(centry);
2172 policy->lockout_threshold = centry_uint16(centry);
2174 status = centry->status;
2176 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2177 domain->name, nt_errstr(status) ));
2179 centry_free(centry);
2180 return status;
2182 do_query:
2183 ZERO_STRUCTP(policy);
2185 /* Return status value returned by seq number check */
2187 if (!NT_STATUS_IS_OK(domain->last_status))
2188 return domain->last_status;
2190 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2191 domain->name ));
2193 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2195 /* and save it */
2196 refresh_sequence_number(domain, false);
2197 wcache_save_lockout_policy(domain, status, policy);
2199 return status;
2202 /* get password policy */
2203 static NTSTATUS password_policy(struct winbindd_domain *domain,
2204 TALLOC_CTX *mem_ctx,
2205 struct samr_DomInfo1 *policy)
2207 struct winbind_cache *cache = get_cache(domain);
2208 struct cache_entry *centry = NULL;
2209 NTSTATUS status;
2211 if (!cache->tdb)
2212 goto do_query;
2214 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2216 if (!centry)
2217 goto do_query;
2219 policy->min_password_length = centry_uint16(centry);
2220 policy->password_history_length = centry_uint16(centry);
2221 policy->password_properties = centry_uint32(centry);
2222 policy->max_password_age = centry_nttime(centry);
2223 policy->min_password_age = centry_nttime(centry);
2225 status = centry->status;
2227 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2228 domain->name, nt_errstr(status) ));
2230 centry_free(centry);
2231 return status;
2233 do_query:
2234 ZERO_STRUCTP(policy);
2236 /* Return status value returned by seq number check */
2238 if (!NT_STATUS_IS_OK(domain->last_status))
2239 return domain->last_status;
2241 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2242 domain->name ));
2244 status = domain->backend->password_policy(domain, mem_ctx, policy);
2246 /* and save it */
2247 refresh_sequence_number(domain, false);
2248 if (NT_STATUS_IS_OK(status)) {
2249 wcache_save_password_policy(domain, status, policy);
2252 return status;
2256 /* Invalidate cached user and group lists coherently */
2258 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2259 void *state)
2261 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2262 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2263 tdb_delete(the_tdb, kbuf);
2265 return 0;
2268 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2270 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2271 struct netr_SamInfo3 *info3)
2273 DOM_SID sid;
2274 fstring key_str, sid_string;
2275 struct winbind_cache *cache;
2277 /* dont clear cached U/SID and UG/SID entries when we want to logon
2278 * offline - gd */
2280 if (lp_winbind_offline_logon()) {
2281 return;
2284 if (!domain)
2285 return;
2287 cache = get_cache(domain);
2289 if (!cache->tdb) {
2290 return;
2293 sid_copy(&sid, info3->base.domain_sid);
2294 sid_append_rid(&sid, info3->base.rid);
2296 /* Clear U/SID cache entry */
2297 fstr_sprintf(key_str, "U/%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 /* Clear UG/SID cache entry */
2302 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2303 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2304 tdb_delete(cache->tdb, string_tdb_data(key_str));
2306 /* Samba/winbindd never needs this. */
2307 netsamlogon_clear_cached_user(info3);
2310 bool wcache_invalidate_cache(void)
2312 struct winbindd_domain *domain;
2314 for (domain = domain_list(); domain; domain = domain->next) {
2315 struct winbind_cache *cache = get_cache(domain);
2317 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2318 "entries for %s\n", domain->name));
2319 if (cache) {
2320 if (cache->tdb) {
2321 tdb_traverse(cache->tdb, traverse_fn, NULL);
2322 } else {
2323 return false;
2327 return true;
2330 bool init_wcache(void)
2332 if (wcache == NULL) {
2333 wcache = SMB_XMALLOC_P(struct winbind_cache);
2334 ZERO_STRUCTP(wcache);
2337 if (wcache->tdb != NULL)
2338 return true;
2340 /* when working offline we must not clear the cache on restart */
2341 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2342 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2343 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2344 O_RDWR|O_CREAT, 0600);
2346 if (wcache->tdb == NULL) {
2347 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2348 return false;
2351 return true;
2354 /************************************************************************
2355 This is called by the parent to initialize the cache file.
2356 We don't need sophisticated locking here as we know we're the
2357 only opener.
2358 ************************************************************************/
2360 bool initialize_winbindd_cache(void)
2362 bool cache_bad = true;
2363 uint32 vers;
2365 if (!init_wcache()) {
2366 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2367 return false;
2370 /* Check version number. */
2371 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2372 vers == WINBINDD_CACHE_VERSION) {
2373 cache_bad = false;
2376 if (cache_bad) {
2377 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2378 "and re-creating with version number %d\n",
2379 WINBINDD_CACHE_VERSION ));
2381 tdb_close(wcache->tdb);
2382 wcache->tdb = NULL;
2384 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2385 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2386 lock_path("winbindd_cache.tdb"),
2387 strerror(errno) ));
2388 return false;
2390 if (!init_wcache()) {
2391 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2392 "init_wcache failed.\n"));
2393 return false;
2396 /* Write the version. */
2397 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2398 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2399 tdb_errorstr(wcache->tdb) ));
2400 return false;
2404 tdb_close(wcache->tdb);
2405 wcache->tdb = NULL;
2406 return true;
2409 void close_winbindd_cache(void)
2411 if (!wcache) {
2412 return;
2414 if (wcache->tdb) {
2415 tdb_close(wcache->tdb);
2416 wcache->tdb = NULL;
2420 void cache_store_response(pid_t pid, struct winbindd_response *response)
2422 fstring key_str;
2424 if (!init_wcache())
2425 return;
2427 DEBUG(10, ("Storing response for pid %d, len %d\n",
2428 pid, response->length));
2430 fstr_sprintf(key_str, "DR/%d", pid);
2431 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2432 make_tdb_data((uint8 *)response, sizeof(*response)),
2433 TDB_REPLACE) == -1)
2434 return;
2436 if (response->length == sizeof(*response))
2437 return;
2439 /* There's extra data */
2441 DEBUG(10, ("Storing extra data: len=%d\n",
2442 (int)(response->length - sizeof(*response))));
2444 fstr_sprintf(key_str, "DE/%d", pid);
2445 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2446 make_tdb_data((uint8 *)response->extra_data.data,
2447 response->length - sizeof(*response)),
2448 TDB_REPLACE) == 0)
2449 return;
2451 /* We could not store the extra data, make sure the tdb does not
2452 * contain a main record with wrong dangling extra data */
2454 fstr_sprintf(key_str, "DR/%d", pid);
2455 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2457 return;
2460 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2462 TDB_DATA data;
2463 fstring key_str;
2465 if (!init_wcache())
2466 return false;
2468 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2470 fstr_sprintf(key_str, "DR/%d", pid);
2471 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2473 if (data.dptr == NULL)
2474 return false;
2476 if (data.dsize != sizeof(*response))
2477 return false;
2479 memcpy(response, data.dptr, data.dsize);
2480 SAFE_FREE(data.dptr);
2482 if (response->length == sizeof(*response)) {
2483 response->extra_data.data = NULL;
2484 return true;
2487 /* There's extra data */
2489 DEBUG(10, ("Retrieving extra data length=%d\n",
2490 (int)(response->length - sizeof(*response))));
2492 fstr_sprintf(key_str, "DE/%d", pid);
2493 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2495 if (data.dptr == NULL) {
2496 DEBUG(0, ("Did not find extra data\n"));
2497 return false;
2500 if (data.dsize != (response->length - sizeof(*response))) {
2501 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2502 SAFE_FREE(data.dptr);
2503 return false;
2506 dump_data(11, (uint8 *)data.dptr, data.dsize);
2508 response->extra_data.data = data.dptr;
2509 return true;
2512 void cache_cleanup_response(pid_t pid)
2514 fstring key_str;
2516 if (!init_wcache())
2517 return;
2519 fstr_sprintf(key_str, "DR/%d", pid);
2520 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2522 fstr_sprintf(key_str, "DE/%d", pid);
2523 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2525 return;
2529 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2530 char **domain_name, char **name,
2531 enum lsa_SidType *type)
2533 struct winbindd_domain *domain;
2534 struct winbind_cache *cache;
2535 struct cache_entry *centry = NULL;
2536 NTSTATUS status;
2537 fstring tmp;
2539 domain = find_lookup_domain_from_sid(sid);
2540 if (domain == NULL) {
2541 return false;
2544 cache = get_cache(domain);
2546 if (cache->tdb == NULL) {
2547 return false;
2550 centry = wcache_fetch(cache, domain, "SN/%s",
2551 sid_to_fstring(tmp, sid));
2552 if (centry == NULL) {
2553 return false;
2556 if (NT_STATUS_IS_OK(centry->status)) {
2557 *type = (enum lsa_SidType)centry_uint32(centry);
2558 *domain_name = centry_string(centry, mem_ctx);
2559 *name = centry_string(centry, mem_ctx);
2562 status = centry->status;
2563 centry_free(centry);
2564 return NT_STATUS_IS_OK(status);
2567 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2568 const char *domain_name,
2569 const char *name,
2570 DOM_SID *sid,
2571 enum lsa_SidType *type)
2573 struct winbindd_domain *domain;
2574 struct winbind_cache *cache;
2575 struct cache_entry *centry = NULL;
2576 NTSTATUS status;
2577 fstring uname;
2578 bool original_online_state;
2580 domain = find_lookup_domain_from_name(domain_name);
2581 if (domain == NULL) {
2582 return false;
2585 cache = get_cache(domain);
2587 if (cache->tdb == NULL) {
2588 return false;
2591 fstrcpy(uname, name);
2592 strupper_m(uname);
2594 /* If we are doing a cached logon, temporarily set the domain
2595 offline so the cache won't expire the entry */
2597 original_online_state = domain->online;
2598 domain->online = false;
2599 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2600 domain->online = original_online_state;
2602 if (centry == NULL) {
2603 return false;
2606 if (NT_STATUS_IS_OK(centry->status)) {
2607 *type = (enum lsa_SidType)centry_uint32(centry);
2608 centry_sid(centry, mem_ctx, sid);
2611 status = centry->status;
2612 centry_free(centry);
2614 return NT_STATUS_IS_OK(status);
2617 void cache_name2sid(struct winbindd_domain *domain,
2618 const char *domain_name, const char *name,
2619 enum lsa_SidType type, const DOM_SID *sid)
2621 refresh_sequence_number(domain, false);
2622 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2623 sid, type);
2627 * The original idea that this cache only contains centries has
2628 * been blurred - now other stuff gets put in here. Ensure we
2629 * ignore these things on cleanup.
2632 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2633 TDB_DATA dbuf, void *state)
2635 struct cache_entry *centry;
2637 if (is_non_centry_key(kbuf)) {
2638 return 0;
2641 centry = wcache_fetch_raw((char *)kbuf.dptr);
2642 if (!centry) {
2643 return 0;
2646 if (!NT_STATUS_IS_OK(centry->status)) {
2647 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2648 tdb_delete(the_tdb, kbuf);
2651 centry_free(centry);
2652 return 0;
2655 /* flush the cache */
2656 void wcache_flush_cache(void)
2658 if (!wcache)
2659 return;
2660 if (wcache->tdb) {
2661 tdb_close(wcache->tdb);
2662 wcache->tdb = NULL;
2664 if (opt_nocache)
2665 return;
2667 /* when working offline we must not clear the cache on restart */
2668 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2669 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2670 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2671 O_RDWR|O_CREAT, 0600);
2673 if (!wcache->tdb) {
2674 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2675 return;
2678 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2680 DEBUG(10,("wcache_flush_cache success\n"));
2683 /* Count cached creds */
2685 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2686 void *state)
2688 int *cred_count = (int*)state;
2690 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2691 (*cred_count)++;
2693 return 0;
2696 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2698 struct winbind_cache *cache = get_cache(domain);
2700 *count = 0;
2702 if (!cache->tdb) {
2703 return NT_STATUS_INTERNAL_DB_ERROR;
2706 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2708 return NT_STATUS_OK;
2711 struct cred_list {
2712 struct cred_list *prev, *next;
2713 TDB_DATA key;
2714 fstring name;
2715 time_t created;
2717 static struct cred_list *wcache_cred_list;
2719 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2720 void *state)
2722 struct cred_list *cred;
2724 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2726 cred = SMB_MALLOC_P(struct cred_list);
2727 if (cred == NULL) {
2728 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2729 return -1;
2732 ZERO_STRUCTP(cred);
2734 /* save a copy of the key */
2736 fstrcpy(cred->name, (const char *)kbuf.dptr);
2737 DLIST_ADD(wcache_cred_list, cred);
2740 return 0;
2743 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2745 struct winbind_cache *cache = get_cache(domain);
2746 NTSTATUS status;
2747 int ret;
2748 struct cred_list *cred, *oldest = NULL;
2750 if (!cache->tdb) {
2751 return NT_STATUS_INTERNAL_DB_ERROR;
2754 /* we possibly already have an entry */
2755 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2757 fstring key_str, tmp;
2759 DEBUG(11,("we already have an entry, deleting that\n"));
2761 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2763 tdb_delete(cache->tdb, string_tdb_data(key_str));
2765 return NT_STATUS_OK;
2768 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2769 if (ret == 0) {
2770 return NT_STATUS_OK;
2771 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2772 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2775 ZERO_STRUCTP(oldest);
2777 for (cred = wcache_cred_list; cred; cred = cred->next) {
2779 TDB_DATA data;
2780 time_t t;
2782 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2783 if (!data.dptr) {
2784 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2785 cred->name));
2786 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2787 goto done;
2790 t = IVAL(data.dptr, 0);
2791 SAFE_FREE(data.dptr);
2793 if (!oldest) {
2794 oldest = SMB_MALLOC_P(struct cred_list);
2795 if (oldest == NULL) {
2796 status = NT_STATUS_NO_MEMORY;
2797 goto done;
2800 fstrcpy(oldest->name, cred->name);
2801 oldest->created = t;
2802 continue;
2805 if (t < oldest->created) {
2806 fstrcpy(oldest->name, cred->name);
2807 oldest->created = t;
2811 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2812 status = NT_STATUS_OK;
2813 } else {
2814 status = NT_STATUS_UNSUCCESSFUL;
2816 done:
2817 SAFE_FREE(wcache_cred_list);
2818 SAFE_FREE(oldest);
2820 return status;
2823 /* Change the global online/offline state. */
2824 bool set_global_winbindd_state_offline(void)
2826 TDB_DATA data;
2828 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2830 /* Only go offline if someone has created
2831 the key "WINBINDD_OFFLINE" in the cache tdb. */
2833 if (wcache == NULL || wcache->tdb == NULL) {
2834 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2835 return false;
2838 if (!lp_winbind_offline_logon()) {
2839 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2840 return false;
2843 if (global_winbindd_offline_state) {
2844 /* Already offline. */
2845 return true;
2848 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2850 if (!data.dptr || data.dsize != 4) {
2851 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2852 SAFE_FREE(data.dptr);
2853 return false;
2854 } else {
2855 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2856 global_winbindd_offline_state = true;
2857 SAFE_FREE(data.dptr);
2858 return true;
2862 void set_global_winbindd_state_online(void)
2864 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2866 if (!lp_winbind_offline_logon()) {
2867 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2868 return;
2871 if (!global_winbindd_offline_state) {
2872 /* Already online. */
2873 return;
2875 global_winbindd_offline_state = false;
2877 if (!wcache->tdb) {
2878 return;
2881 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2882 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2885 bool get_global_winbindd_state_offline(void)
2887 return global_winbindd_offline_state;
2890 /***********************************************************************
2891 Validate functions for all possible cache tdb keys.
2892 ***********************************************************************/
2894 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2895 struct tdb_validation_status *state)
2897 struct cache_entry *centry;
2899 centry = SMB_XMALLOC_P(struct cache_entry);
2900 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2901 if (!centry->data) {
2902 SAFE_FREE(centry);
2903 return NULL;
2905 centry->len = data.dsize;
2906 centry->ofs = 0;
2908 if (centry->len < 8) {
2909 /* huh? corrupt cache? */
2910 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2911 centry_free(centry);
2912 state->bad_entry = true;
2913 state->success = false;
2914 return NULL;
2917 centry->status = NT_STATUS(centry_uint32(centry));
2918 centry->sequence_number = centry_uint32(centry);
2919 return centry;
2922 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2923 struct tdb_validation_status *state)
2925 if (dbuf.dsize != 8) {
2926 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2927 keystr, (unsigned int)dbuf.dsize ));
2928 state->bad_entry = true;
2929 return 1;
2931 return 0;
2934 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2935 struct tdb_validation_status *state)
2937 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2938 if (!centry) {
2939 return 1;
2942 (void)centry_uint32(centry);
2943 if (NT_STATUS_IS_OK(centry->status)) {
2944 DOM_SID sid;
2945 (void)centry_sid(centry, mem_ctx, &sid);
2948 centry_free(centry);
2950 if (!(state->success)) {
2951 return 1;
2953 DEBUG(10,("validate_ns: %s ok\n", keystr));
2954 return 0;
2957 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2958 struct tdb_validation_status *state)
2960 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2961 if (!centry) {
2962 return 1;
2965 if (NT_STATUS_IS_OK(centry->status)) {
2966 (void)centry_uint32(centry);
2967 (void)centry_string(centry, mem_ctx);
2968 (void)centry_string(centry, mem_ctx);
2971 centry_free(centry);
2973 if (!(state->success)) {
2974 return 1;
2976 DEBUG(10,("validate_sn: %s ok\n", keystr));
2977 return 0;
2980 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2981 struct tdb_validation_status *state)
2983 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2984 DOM_SID sid;
2986 if (!centry) {
2987 return 1;
2990 (void)centry_string(centry, mem_ctx);
2991 (void)centry_string(centry, mem_ctx);
2992 (void)centry_string(centry, mem_ctx);
2993 (void)centry_string(centry, mem_ctx);
2994 (void)centry_uint32(centry);
2995 (void)centry_sid(centry, mem_ctx, &sid);
2996 (void)centry_sid(centry, mem_ctx, &sid);
2998 centry_free(centry);
3000 if (!(state->success)) {
3001 return 1;
3003 DEBUG(10,("validate_u: %s ok\n", keystr));
3004 return 0;
3007 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3008 struct tdb_validation_status *state)
3010 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3012 if (!centry) {
3013 return 1;
3016 (void)centry_nttime(centry);
3017 (void)centry_nttime(centry);
3018 (void)centry_uint16(centry);
3020 centry_free(centry);
3022 if (!(state->success)) {
3023 return 1;
3025 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3026 return 0;
3029 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3030 struct tdb_validation_status *state)
3032 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3034 if (!centry) {
3035 return 1;
3038 (void)centry_uint16(centry);
3039 (void)centry_uint16(centry);
3040 (void)centry_uint32(centry);
3041 (void)centry_nttime(centry);
3042 (void)centry_nttime(centry);
3044 centry_free(centry);
3046 if (!(state->success)) {
3047 return 1;
3049 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3050 return 0;
3053 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3054 struct tdb_validation_status *state)
3056 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3058 if (!centry) {
3059 return 1;
3062 (void)centry_time(centry);
3063 (void)centry_hash16(centry, mem_ctx);
3065 /* We only have 17 bytes more data in the salted cred case. */
3066 if (centry->len - centry->ofs == 17) {
3067 (void)centry_hash16(centry, mem_ctx);
3070 centry_free(centry);
3072 if (!(state->success)) {
3073 return 1;
3075 DEBUG(10,("validate_cred: %s ok\n", keystr));
3076 return 0;
3079 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3080 struct tdb_validation_status *state)
3082 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3083 int32 num_entries, i;
3085 if (!centry) {
3086 return 1;
3089 num_entries = (int32)centry_uint32(centry);
3091 for (i=0; i< num_entries; i++) {
3092 DOM_SID sid;
3093 (void)centry_string(centry, mem_ctx);
3094 (void)centry_string(centry, mem_ctx);
3095 (void)centry_string(centry, mem_ctx);
3096 (void)centry_string(centry, mem_ctx);
3097 (void)centry_sid(centry, mem_ctx, &sid);
3098 (void)centry_sid(centry, mem_ctx, &sid);
3101 centry_free(centry);
3103 if (!(state->success)) {
3104 return 1;
3106 DEBUG(10,("validate_ul: %s ok\n", keystr));
3107 return 0;
3110 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3111 struct tdb_validation_status *state)
3113 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3114 int32 num_entries, i;
3116 if (!centry) {
3117 return 1;
3120 num_entries = centry_uint32(centry);
3122 for (i=0; i< num_entries; i++) {
3123 (void)centry_string(centry, mem_ctx);
3124 (void)centry_string(centry, mem_ctx);
3125 (void)centry_uint32(centry);
3128 centry_free(centry);
3130 if (!(state->success)) {
3131 return 1;
3133 DEBUG(10,("validate_gl: %s ok\n", keystr));
3134 return 0;
3137 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3138 struct tdb_validation_status *state)
3140 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3141 int32 num_groups, i;
3143 if (!centry) {
3144 return 1;
3147 num_groups = centry_uint32(centry);
3149 for (i=0; i< num_groups; i++) {
3150 DOM_SID sid;
3151 centry_sid(centry, mem_ctx, &sid);
3154 centry_free(centry);
3156 if (!(state->success)) {
3157 return 1;
3159 DEBUG(10,("validate_ug: %s ok\n", keystr));
3160 return 0;
3163 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3164 struct tdb_validation_status *state)
3166 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3167 int32 num_aliases, i;
3169 if (!centry) {
3170 return 1;
3173 num_aliases = centry_uint32(centry);
3175 for (i=0; i < num_aliases; i++) {
3176 (void)centry_uint32(centry);
3179 centry_free(centry);
3181 if (!(state->success)) {
3182 return 1;
3184 DEBUG(10,("validate_ua: %s ok\n", keystr));
3185 return 0;
3188 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3189 struct tdb_validation_status *state)
3191 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3192 int32 num_names, i;
3194 if (!centry) {
3195 return 1;
3198 num_names = centry_uint32(centry);
3200 for (i=0; i< num_names; i++) {
3201 DOM_SID sid;
3202 centry_sid(centry, mem_ctx, &sid);
3203 (void)centry_string(centry, mem_ctx);
3204 (void)centry_uint32(centry);
3207 centry_free(centry);
3209 if (!(state->success)) {
3210 return 1;
3212 DEBUG(10,("validate_gm: %s ok\n", keystr));
3213 return 0;
3216 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3217 struct tdb_validation_status *state)
3219 /* Can't say anything about this other than must be nonzero. */
3220 if (dbuf.dsize == 0) {
3221 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3222 keystr));
3223 state->bad_entry = true;
3224 state->success = false;
3225 return 1;
3228 DEBUG(10,("validate_dr: %s ok\n", keystr));
3229 return 0;
3232 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3233 struct tdb_validation_status *state)
3235 /* Can't say anything about this other than must be nonzero. */
3236 if (dbuf.dsize == 0) {
3237 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3238 keystr));
3239 state->bad_entry = true;
3240 state->success = false;
3241 return 1;
3244 DEBUG(10,("validate_de: %s ok\n", keystr));
3245 return 0;
3248 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3249 TDB_DATA dbuf, struct tdb_validation_status *state)
3251 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3253 if (!centry) {
3254 return 1;
3257 (void)centry_string(centry, mem_ctx);
3258 (void)centry_string(centry, mem_ctx);
3259 (void)centry_string(centry, mem_ctx);
3260 (void)centry_uint32(centry);
3262 centry_free(centry);
3264 if (!(state->success)) {
3265 return 1;
3267 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3268 return 0;
3271 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3272 struct tdb_validation_status *state)
3274 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3275 int32 num_domains, i;
3277 if (!centry) {
3278 return 1;
3281 num_domains = centry_uint32(centry);
3283 for (i=0; i< num_domains; i++) {
3284 DOM_SID sid;
3285 (void)centry_string(centry, mem_ctx);
3286 (void)centry_string(centry, mem_ctx);
3287 (void)centry_sid(centry, mem_ctx, &sid);
3290 centry_free(centry);
3292 if (!(state->success)) {
3293 return 1;
3295 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3296 return 0;
3299 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3300 TDB_DATA dbuf,
3301 struct tdb_validation_status *state)
3303 if (dbuf.dsize == 0) {
3304 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3305 "key %s (len ==0) ?\n", keystr));
3306 state->bad_entry = true;
3307 state->success = false;
3308 return 1;
3311 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3312 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3313 return 0;
3316 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3317 struct tdb_validation_status *state)
3319 if (dbuf.dsize != 4) {
3320 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3321 keystr, (unsigned int)dbuf.dsize ));
3322 state->bad_entry = true;
3323 state->success = false;
3324 return 1;
3326 DEBUG(10,("validate_offline: %s ok\n", keystr));
3327 return 0;
3330 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3331 struct tdb_validation_status *state)
3333 if (dbuf.dsize != 4) {
3334 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3335 "key %s (len %u != 4) ?\n",
3336 keystr, (unsigned int)dbuf.dsize));
3337 state->bad_entry = true;
3338 state->success = false;
3339 return 1;
3342 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3343 return 0;
3346 /***********************************************************************
3347 A list of all possible cache tdb keys with associated validation
3348 functions.
3349 ***********************************************************************/
3351 struct key_val_struct {
3352 const char *keyname;
3353 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3354 } key_val[] = {
3355 {"SEQNUM/", validate_seqnum},
3356 {"NS/", validate_ns},
3357 {"SN/", validate_sn},
3358 {"U/", validate_u},
3359 {"LOC_POL/", validate_loc_pol},
3360 {"PWD_POL/", validate_pwd_pol},
3361 {"CRED/", validate_cred},
3362 {"UL/", validate_ul},
3363 {"GL/", validate_gl},
3364 {"UG/", validate_ug},
3365 {"UA", validate_ua},
3366 {"GM/", validate_gm},
3367 {"DR/", validate_dr},
3368 {"DE/", validate_de},
3369 {"NSS/PWINFO/", validate_pwinfo},
3370 {"TRUSTDOMS/", validate_trustdoms},
3371 {"TRUSTDOMCACHE/", validate_trustdomcache},
3372 {"WINBINDD_OFFLINE", validate_offline},
3373 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3374 {NULL, NULL}
3377 /***********************************************************************
3378 Function to look at every entry in the tdb and validate it as far as
3379 possible.
3380 ***********************************************************************/
3382 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3384 int i;
3385 unsigned int max_key_len = 1024;
3386 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3388 /* Paranoia check. */
3389 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3390 max_key_len = 1024 * 1024;
3392 if (kbuf.dsize > max_key_len) {
3393 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3394 "(%u) > (%u)\n\n",
3395 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3396 return 1;
3399 for (i = 0; key_val[i].keyname; i++) {
3400 size_t namelen = strlen(key_val[i].keyname);
3401 if (kbuf.dsize >= namelen && (
3402 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3403 TALLOC_CTX *mem_ctx;
3404 char *keystr;
3405 int ret;
3407 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3408 if (!keystr) {
3409 return 1;
3411 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3412 keystr[kbuf.dsize] = '\0';
3414 mem_ctx = talloc_init("validate_ctx");
3415 if (!mem_ctx) {
3416 SAFE_FREE(keystr);
3417 return 1;
3420 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3421 v_state);
3423 SAFE_FREE(keystr);
3424 talloc_destroy(mem_ctx);
3425 return ret;
3429 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3430 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3431 DEBUG(0,("data :\n"));
3432 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3433 v_state->unknown_key = true;
3434 v_state->success = false;
3435 return 1; /* terminate. */
3438 static void validate_panic(const char *const why)
3440 DEBUG(0,("validating cache: would panic %s\n", why ));
3441 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3442 exit(47);
3445 /***********************************************************************
3446 Try and validate every entry in the winbindd cache. If we fail here,
3447 delete the cache tdb and return non-zero.
3448 ***********************************************************************/
3450 int winbindd_validate_cache(void)
3452 int ret = -1;
3453 const char *tdb_path = lock_path("winbindd_cache.tdb");
3454 TDB_CONTEXT *tdb = NULL;
3456 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3457 smb_panic_fn = validate_panic;
3460 tdb = tdb_open_log(tdb_path,
3461 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3462 ( lp_winbind_offline_logon()
3463 ? TDB_DEFAULT
3464 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3465 O_RDWR|O_CREAT,
3466 0600);
3467 if (!tdb) {
3468 DEBUG(0, ("winbindd_validate_cache: "
3469 "error opening/initializing tdb\n"));
3470 goto done;
3472 tdb_close(tdb);
3474 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3476 if (ret != 0) {
3477 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3478 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3479 unlink(tdb_path);
3482 done:
3483 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3484 smb_panic_fn = smb_panic;
3485 return ret;
3488 /***********************************************************************
3489 Try and validate every entry in the winbindd cache.
3490 ***********************************************************************/
3492 int winbindd_validate_cache_nobackup(void)
3494 int ret = -1;
3495 const char *tdb_path = lock_path("winbindd_cache.tdb");
3497 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3498 smb_panic_fn = validate_panic;
3501 if (wcache == NULL || wcache->tdb == NULL) {
3502 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3503 } else {
3504 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3507 if (ret != 0) {
3508 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3509 "successful.\n"));
3512 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3513 "function\n"));
3514 smb_panic_fn = smb_panic;
3515 return ret;
3518 bool winbindd_cache_validate_and_initialize(void)
3520 close_winbindd_cache();
3522 if (lp_winbind_offline_logon()) {
3523 if (winbindd_validate_cache() < 0) {
3524 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3525 "could be restored.\n"));
3529 return initialize_winbindd_cache();
3532 /*********************************************************************
3533 ********************************************************************/
3535 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3536 struct winbindd_tdc_domain **domains,
3537 size_t *num_domains )
3539 struct winbindd_tdc_domain *list = NULL;
3540 size_t idx;
3541 int i;
3542 bool set_only = false;
3544 /* don't allow duplicates */
3546 idx = *num_domains;
3547 list = *domains;
3549 for ( i=0; i< (*num_domains); i++ ) {
3550 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3551 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3552 new_dom->name));
3553 idx = i;
3554 set_only = true;
3556 break;
3560 if ( !set_only ) {
3561 if ( !*domains ) {
3562 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3563 idx = 0;
3564 } else {
3565 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3566 struct winbindd_tdc_domain,
3567 (*num_domains)+1);
3568 idx = *num_domains;
3571 ZERO_STRUCT( list[idx] );
3574 if ( !list )
3575 return false;
3577 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3578 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3580 if ( !is_null_sid( &new_dom->sid ) ) {
3581 sid_copy( &list[idx].sid, &new_dom->sid );
3582 } else {
3583 sid_copy(&list[idx].sid, &global_sid_NULL);
3586 if ( new_dom->domain_flags != 0x0 )
3587 list[idx].trust_flags = new_dom->domain_flags;
3589 if ( new_dom->domain_type != 0x0 )
3590 list[idx].trust_type = new_dom->domain_type;
3592 if ( new_dom->domain_trust_attribs != 0x0 )
3593 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3595 if ( !set_only ) {
3596 *domains = list;
3597 *num_domains = idx + 1;
3600 return true;
3603 /*********************************************************************
3604 ********************************************************************/
3606 static TDB_DATA make_tdc_key( const char *domain_name )
3608 char *keystr = NULL;
3609 TDB_DATA key = { NULL, 0 };
3611 if ( !domain_name ) {
3612 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3613 return key;
3617 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3618 key = string_term_tdb_data(keystr);
3620 return key;
3623 /*********************************************************************
3624 ********************************************************************/
3626 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3627 size_t num_domains,
3628 unsigned char **buf )
3630 unsigned char *buffer = NULL;
3631 int len = 0;
3632 int buflen = 0;
3633 int i = 0;
3635 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3636 (int)num_domains));
3638 buflen = 0;
3640 again:
3641 len = 0;
3643 /* Store the number of array items first */
3644 len += tdb_pack( buffer+len, buflen-len, "d",
3645 num_domains );
3647 /* now pack each domain trust record */
3648 for ( i=0; i<num_domains; i++ ) {
3650 fstring tmp;
3652 if ( buflen > 0 ) {
3653 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3654 domains[i].domain_name,
3655 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3658 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3659 domains[i].domain_name,
3660 domains[i].dns_name,
3661 sid_to_fstring(tmp, &domains[i].sid),
3662 domains[i].trust_flags,
3663 domains[i].trust_attribs,
3664 domains[i].trust_type );
3667 if ( buflen < len ) {
3668 SAFE_FREE(buffer);
3669 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3670 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3671 buflen = -1;
3672 goto done;
3674 buflen = len;
3675 goto again;
3678 *buf = buffer;
3680 done:
3681 return buflen;
3684 /*********************************************************************
3685 ********************************************************************/
3687 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3688 struct winbindd_tdc_domain **domains )
3690 fstring domain_name, dns_name, sid_string;
3691 uint32 type, attribs, flags;
3692 int num_domains;
3693 int len = 0;
3694 int i;
3695 struct winbindd_tdc_domain *list = NULL;
3697 /* get the number of domains */
3698 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3699 if ( len == -1 ) {
3700 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3701 return 0;
3704 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3705 if ( !list ) {
3706 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3707 return 0;
3710 for ( i=0; i<num_domains; i++ ) {
3711 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3712 domain_name,
3713 dns_name,
3714 sid_string,
3715 &flags,
3716 &attribs,
3717 &type );
3719 if ( len == -1 ) {
3720 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3721 TALLOC_FREE( list );
3722 return 0;
3725 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3726 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3727 domain_name, dns_name, sid_string,
3728 flags, attribs, type));
3730 list[i].domain_name = talloc_strdup( list, domain_name );
3731 list[i].dns_name = talloc_strdup( list, dns_name );
3732 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3733 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3734 domain_name));
3736 list[i].trust_flags = flags;
3737 list[i].trust_attribs = attribs;
3738 list[i].trust_type = type;
3741 *domains = list;
3743 return num_domains;
3746 /*********************************************************************
3747 ********************************************************************/
3749 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3751 TDB_DATA key = make_tdc_key( lp_workgroup() );
3752 TDB_DATA data = { NULL, 0 };
3753 int ret;
3755 if ( !key.dptr )
3756 return false;
3758 /* See if we were asked to delete the cache entry */
3760 if ( !domains ) {
3761 ret = tdb_delete( wcache->tdb, key );
3762 goto done;
3765 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3767 if ( !data.dptr ) {
3768 ret = -1;
3769 goto done;
3772 ret = tdb_store( wcache->tdb, key, data, 0 );
3774 done:
3775 SAFE_FREE( data.dptr );
3776 SAFE_FREE( key.dptr );
3778 return ( ret != -1 );
3781 /*********************************************************************
3782 ********************************************************************/
3784 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3786 TDB_DATA key = make_tdc_key( lp_workgroup() );
3787 TDB_DATA data = { NULL, 0 };
3789 *domains = NULL;
3790 *num_domains = 0;
3792 if ( !key.dptr )
3793 return false;
3795 data = tdb_fetch( wcache->tdb, key );
3797 SAFE_FREE( key.dptr );
3799 if ( !data.dptr )
3800 return false;
3802 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3804 SAFE_FREE( data.dptr );
3806 if ( !*domains )
3807 return false;
3809 return true;
3812 /*********************************************************************
3813 ********************************************************************/
3815 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3817 struct winbindd_tdc_domain *dom_list = NULL;
3818 size_t num_domains = 0;
3819 bool ret = false;
3821 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3822 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3823 domain->name, domain->alt_name,
3824 sid_string_dbg(&domain->sid),
3825 domain->domain_flags,
3826 domain->domain_trust_attribs,
3827 domain->domain_type));
3829 if ( !init_wcache() ) {
3830 return false;
3833 /* fetch the list */
3835 wcache_tdc_fetch_list( &dom_list, &num_domains );
3837 /* add the new domain */
3839 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3840 goto done;
3843 /* pack the domain */
3845 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3846 goto done;
3849 /* Success */
3851 ret = true;
3852 done:
3853 TALLOC_FREE( dom_list );
3855 return ret;
3858 /*********************************************************************
3859 ********************************************************************/
3861 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3863 struct winbindd_tdc_domain *dom_list = NULL;
3864 size_t num_domains = 0;
3865 int i;
3866 struct winbindd_tdc_domain *d = NULL;
3868 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3870 if ( !init_wcache() ) {
3871 return false;
3874 /* fetch the list */
3876 wcache_tdc_fetch_list( &dom_list, &num_domains );
3878 for ( i=0; i<num_domains; i++ ) {
3879 if ( strequal(name, dom_list[i].domain_name) ||
3880 strequal(name, dom_list[i].dns_name) )
3882 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3883 name));
3885 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3886 if ( !d )
3887 break;
3889 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3890 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3891 sid_copy( &d->sid, &dom_list[i].sid );
3892 d->trust_flags = dom_list[i].trust_flags;
3893 d->trust_type = dom_list[i].trust_type;
3894 d->trust_attribs = dom_list[i].trust_attribs;
3896 break;
3900 TALLOC_FREE( dom_list );
3902 return d;
3906 /*********************************************************************
3907 ********************************************************************/
3909 void wcache_tdc_clear( void )
3911 if ( !init_wcache() )
3912 return;
3914 wcache_tdc_store_list( NULL, 0 );
3916 return;
3920 /*********************************************************************
3921 ********************************************************************/
3923 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3924 NTSTATUS status,
3925 const DOM_SID *user_sid,
3926 const char *homedir,
3927 const char *shell,
3928 const char *gecos,
3929 uint32 gid)
3931 struct cache_entry *centry;
3932 fstring tmp;
3934 if ( (centry = centry_start(domain, status)) == NULL )
3935 return;
3937 centry_put_string( centry, homedir );
3938 centry_put_string( centry, shell );
3939 centry_put_string( centry, gecos );
3940 centry_put_uint32( centry, gid );
3942 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
3944 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
3946 centry_free(centry);
3949 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3950 const DOM_SID *user_sid,
3951 TALLOC_CTX *ctx,
3952 ADS_STRUCT *ads, LDAPMessage *msg,
3953 char **homedir, char **shell, char **gecos,
3954 gid_t *p_gid)
3956 struct winbind_cache *cache = get_cache(domain);
3957 struct cache_entry *centry = NULL;
3958 NTSTATUS nt_status;
3959 fstring tmp;
3961 if (!cache->tdb)
3962 goto do_query;
3964 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
3965 sid_to_fstring(tmp, user_sid));
3967 if (!centry)
3968 goto do_query;
3970 *homedir = centry_string( centry, ctx );
3971 *shell = centry_string( centry, ctx );
3972 *gecos = centry_string( centry, ctx );
3973 *p_gid = centry_uint32( centry );
3975 centry_free(centry);
3977 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3978 sid_string_dbg(user_sid)));
3980 return NT_STATUS_OK;
3982 do_query:
3984 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3985 homedir, shell, gecos, p_gid );
3987 if ( NT_STATUS_IS_OK(nt_status) ) {
3988 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3989 *homedir, *shell, *gecos, *p_gid );
3992 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3993 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3994 domain->name ));
3995 set_domain_offline( domain );
3998 return nt_status;
4002 /* the cache backend methods are exposed via this structure */
4003 struct winbindd_methods cache_methods = {
4004 true,
4005 query_user_list,
4006 enum_dom_groups,
4007 enum_local_groups,
4008 name_to_sid,
4009 sid_to_name,
4010 rids_to_names,
4011 query_user,
4012 lookup_usergroups,
4013 lookup_useraliases,
4014 lookup_groupmem,
4015 sequence_number,
4016 lockout_policy,
4017 password_policy,
4018 trusted_domains