libsmbconf: testsuite: refactor printing of string lists out.
[Samba.git] / source / winbindd / winbindd_cache.c
blobf65317282c0d873a980e8edcc422929e135f4e87
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 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 = &passdb_methods;
141 domain->initialized = True;
143 if ( !domain->initialized ) {
144 init_dc_connection( domain );
148 OK. listen up becasue I'm only going to say this once.
149 We have the following scenarios to consider
150 (a) trusted AD domains on a Samba DC,
151 (b) trusted AD domains and we are joined to a non-kerberos domain
152 (c) trusted AD domains and we are joined to a kerberos (AD) domain
154 For (a) we can always contact the trusted domain using krb5
155 since we have the domain trust account password
157 For (b) we can only use RPC since we have no way of
158 getting a krb5 ticket in our own domain
160 For (c) we can always use krb5 since we have a kerberos trust
162 --jerry
165 if (!domain->backend) {
166 #ifdef HAVE_ADS
167 struct winbindd_domain *our_domain = domain;
169 /* find our domain first so we can figure out if we
170 are joined to a kerberized domain */
172 if ( !domain->primary )
173 our_domain = find_our_domain();
175 if ((our_domain->active_directory || IS_DC)
176 && domain->active_directory
177 && !lp_winbind_rpc_only()) {
178 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
179 domain->backend = &ads_methods;
180 } else {
181 #endif /* HAVE_ADS */
182 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
183 domain->backend = &reconnect_methods;
184 #ifdef HAVE_ADS
186 #endif /* HAVE_ADS */
189 if (ret)
190 return ret;
192 ret = SMB_XMALLOC_P(struct winbind_cache);
193 ZERO_STRUCTP(ret);
195 wcache = ret;
196 wcache_flush_cache();
198 return ret;
202 free a centry structure
204 static void centry_free(struct cache_entry *centry)
206 if (!centry)
207 return;
208 SAFE_FREE(centry->data);
209 free(centry);
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
214 if (centry->len - centry->ofs < nbytes) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes,
217 centry->len - centry->ofs));
218 return false;
220 return true;
224 pull a uint32 from a cache entry
226 static uint32 centry_uint32(struct cache_entry *centry)
228 uint32 ret;
230 if (!centry_check_bytes(centry, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret = IVAL(centry->data, centry->ofs);
234 centry->ofs += 4;
235 return ret;
239 pull a uint16 from a cache entry
241 static uint16 centry_uint16(struct cache_entry *centry)
243 uint16 ret;
244 if (!centry_check_bytes(centry, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 2;
249 return ret;
253 pull a uint8 from a cache entry
255 static uint8 centry_uint8(struct cache_entry *centry)
257 uint8 ret;
258 if (!centry_check_bytes(centry, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret = CVAL(centry->data, centry->ofs);
262 centry->ofs += 1;
263 return ret;
267 pull a NTTIME from a cache entry
269 static NTTIME centry_nttime(struct cache_entry *centry)
271 NTTIME ret;
272 if (!centry_check_bytes(centry, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret = IVAL(centry->data, centry->ofs);
276 centry->ofs += 4;
277 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278 centry->ofs += 4;
279 return ret;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry *centry)
287 return (time_t)centry_nttime(centry);
290 /* pull a string from a cache entry, using the supplied
291 talloc context
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 uint32 len;
296 char *ret;
298 len = centry_uint8(centry);
300 if (len == 0xFF) {
301 /* a deliberate NULL string */
302 return NULL;
305 if (!centry_check_bytes(centry, (size_t)len)) {
306 smb_panic_fn("centry_string");
309 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310 if (!ret) {
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (!centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret,centry->data + centry->ofs, 16);
344 centry->ofs += 16;
345 return ret;
348 /* pull a sid from a cache entry, using the supplied
349 talloc context
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
353 char *sid_string;
354 sid_string = centry_string(centry, mem_ctx);
355 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356 return false;
358 return true;
363 pull a NTSTATUS from a cache entry
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
367 NTSTATUS status;
369 status = NT_STATUS(centry_uint32(centry));
370 return status;
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain *domain)
377 bool ret;
379 if (!wcache->tdb)
380 return false;
382 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
384 if (ret)
385 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
386 domain->name ));
387 return ret;
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
392 TDB_DATA data;
393 fstring key;
394 uint32 time_diff;
396 if (!wcache->tdb) {
397 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398 return NT_STATUS_UNSUCCESSFUL;
401 fstr_sprintf( key, "SEQNUM/%s", domain->name );
403 data = tdb_fetch_bystring( wcache->tdb, key );
404 if ( !data.dptr || data.dsize!=8 ) {
405 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
406 return NT_STATUS_UNSUCCESSFUL;
409 domain->sequence_number = IVAL(data.dptr, 0);
410 domain->last_seq_check = IVAL(data.dptr, 4);
412 SAFE_FREE(data.dptr);
414 /* have we expired? */
416 time_diff = now - domain->last_seq_check;
417 if ( time_diff > lp_winbind_cache_time() ) {
418 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419 domain->name, domain->sequence_number,
420 (uint32)domain->last_seq_check));
421 return NT_STATUS_UNSUCCESSFUL;
424 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
425 domain->name, domain->sequence_number,
426 (uint32)domain->last_seq_check));
428 return NT_STATUS_OK;
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
433 TDB_DATA data;
434 fstring key_str;
435 uint8 buf[8];
437 if (!wcache->tdb) {
438 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439 return NT_STATUS_UNSUCCESSFUL;
442 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
444 SIVAL(buf, 0, domain->sequence_number);
445 SIVAL(buf, 4, domain->last_seq_check);
446 data.dptr = buf;
447 data.dsize = 8;
449 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
450 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
458 return NT_STATUS_OK;
462 refresh the domain sequence number. If force is true
463 then always refresh it, no matter how recently we fetched it
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
468 NTSTATUS status;
469 unsigned time_diff;
470 time_t t = time(NULL);
471 unsigned cache_time = lp_winbind_cache_time();
473 if ( IS_DOMAIN_OFFLINE(domain) ) {
474 return;
477 get_cache( domain );
479 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
480 /* trying to reconnect is expensive, don't do it too often */
481 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
482 cache_time *= 8;
484 #endif
486 time_diff = t - domain->last_seq_check;
488 /* see if we have to refetch the domain sequence number */
489 if (!force && (time_diff < cache_time)) {
490 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
491 goto done;
494 /* try to get the sequence number from the tdb cache first */
495 /* this will update the timestamp as well */
497 status = fetch_cache_seqnum( domain, t );
498 if ( NT_STATUS_IS_OK(status) )
499 goto done;
501 /* important! make sure that we know if this is a native
502 mode domain or not. And that we can contact it. */
504 if ( winbindd_can_contact_domain( domain ) ) {
505 status = domain->backend->sequence_number(domain,
506 &domain->sequence_number);
507 } else {
508 /* just use the current time */
509 status = NT_STATUS_OK;
510 domain->sequence_number = time(NULL);
514 /* the above call could have set our domain->backend to NULL when
515 * coming from offline to online mode, make sure to reinitialize the
516 * backend - Guenther */
517 get_cache( domain );
519 if (!NT_STATUS_IS_OK(status)) {
520 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
521 domain->sequence_number = DOM_SEQUENCE_NONE;
524 domain->last_status = status;
525 domain->last_seq_check = time(NULL);
527 /* save the new sequence number ni the cache */
528 store_cache_seqnum( domain );
530 done:
531 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
532 domain->name, domain->sequence_number));
534 return;
538 decide if a cache entry has expired
540 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
542 /* If we've been told to be offline - stay in that state... */
543 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
544 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
545 keystr, domain->name ));
546 return false;
549 /* when the domain is offline return the cached entry.
550 * This deals with transient offline states... */
552 if (!domain->online) {
553 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
554 keystr, domain->name ));
555 return false;
558 /* if the server is OK and our cache entry came from when it was down then
559 the entry is invalid */
560 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
561 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
562 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
563 keystr, domain->name ));
564 return true;
567 /* if the server is down or the cache entry is not older than the
568 current sequence number then it is OK */
569 if (wcache_server_down(domain) ||
570 centry->sequence_number == domain->sequence_number) {
571 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
572 keystr, domain->name ));
573 return false;
576 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
577 keystr, domain->name ));
579 /* it's expired */
580 return true;
583 static struct cache_entry *wcache_fetch_raw(char *kstr)
585 TDB_DATA data;
586 struct cache_entry *centry;
587 TDB_DATA key;
589 key = string_tdb_data(kstr);
590 data = tdb_fetch(wcache->tdb, key);
591 if (!data.dptr) {
592 /* a cache miss */
593 return NULL;
596 centry = SMB_XMALLOC_P(struct cache_entry);
597 centry->data = (unsigned char *)data.dptr;
598 centry->len = data.dsize;
599 centry->ofs = 0;
601 if (centry->len < 8) {
602 /* huh? corrupt cache? */
603 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
604 centry_free(centry);
605 return NULL;
608 centry->status = centry_ntstatus(centry);
609 centry->sequence_number = centry_uint32(centry);
611 return centry;
615 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
616 number and return status
618 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
619 struct winbindd_domain *domain,
620 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
621 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
622 struct winbindd_domain *domain,
623 const char *format, ...)
625 va_list ap;
626 char *kstr;
627 struct cache_entry *centry;
629 if (opt_nocache) {
630 return NULL;
633 refresh_sequence_number(domain, false);
635 va_start(ap, format);
636 smb_xvasprintf(&kstr, format, ap);
637 va_end(ap);
639 centry = wcache_fetch_raw(kstr);
640 if (centry == NULL) {
641 free(kstr);
642 return NULL;
645 if (centry_expired(domain, kstr, centry)) {
647 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
648 kstr, domain->name ));
650 centry_free(centry);
651 free(kstr);
652 return NULL;
655 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
656 kstr, domain->name ));
658 free(kstr);
659 return centry;
662 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
663 static void wcache_delete(const char *format, ...)
665 va_list ap;
666 char *kstr;
667 TDB_DATA key;
669 va_start(ap, format);
670 smb_xvasprintf(&kstr, format, ap);
671 va_end(ap);
673 key = string_tdb_data(kstr);
675 tdb_delete(wcache->tdb, key);
676 free(kstr);
680 make sure we have at least len bytes available in a centry
682 static void centry_expand(struct cache_entry *centry, uint32 len)
684 if (centry->len - centry->ofs >= len)
685 return;
686 centry->len *= 2;
687 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
688 centry->len);
689 if (!centry->data) {
690 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
691 smb_panic_fn("out of memory in centry_expand");
696 push a uint32 into a centry
698 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
700 centry_expand(centry, 4);
701 SIVAL(centry->data, centry->ofs, v);
702 centry->ofs += 4;
706 push a uint16 into a centry
708 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
710 centry_expand(centry, 2);
711 SIVAL(centry->data, centry->ofs, v);
712 centry->ofs += 2;
716 push a uint8 into a centry
718 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
720 centry_expand(centry, 1);
721 SCVAL(centry->data, centry->ofs, v);
722 centry->ofs += 1;
726 push a string into a centry
728 static void centry_put_string(struct cache_entry *centry, const char *s)
730 int len;
732 if (!s) {
733 /* null strings are marked as len 0xFFFF */
734 centry_put_uint8(centry, 0xFF);
735 return;
738 len = strlen(s);
739 /* can't handle more than 254 char strings. Truncating is probably best */
740 if (len > 254) {
741 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
742 len = 254;
744 centry_put_uint8(centry, len);
745 centry_expand(centry, len);
746 memcpy(centry->data + centry->ofs, s, len);
747 centry->ofs += len;
751 push a 16 byte hash into a centry - treat as 16 byte string.
753 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
755 centry_put_uint8(centry, 16);
756 centry_expand(centry, 16);
757 memcpy(centry->data + centry->ofs, val, 16);
758 centry->ofs += 16;
761 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
763 fstring sid_string;
764 centry_put_string(centry, sid_to_fstring(sid_string, sid));
769 put NTSTATUS into a centry
771 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
773 uint32 status_value = NT_STATUS_V(status);
774 centry_put_uint32(centry, status_value);
779 push a NTTIME into a centry
781 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
783 centry_expand(centry, 8);
784 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
785 centry->ofs += 4;
786 SIVAL(centry->data, centry->ofs, nt >> 32);
787 centry->ofs += 4;
791 push a time_t into a centry - use a 64 bit size.
792 NTTIME here is being used as a convenient 64-bit size.
794 static void centry_put_time(struct cache_entry *centry, time_t t)
796 NTTIME nt = (NTTIME)t;
797 centry_put_nttime(centry, nt);
801 start a centry for output. When finished, call centry_end()
803 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
805 struct cache_entry *centry;
807 if (!wcache->tdb)
808 return NULL;
810 centry = SMB_XMALLOC_P(struct cache_entry);
812 centry->len = 8192; /* reasonable default */
813 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
814 centry->ofs = 0;
815 centry->sequence_number = domain->sequence_number;
816 centry_put_ntstatus(centry, status);
817 centry_put_uint32(centry, centry->sequence_number);
818 return centry;
822 finish a centry and write it to the tdb
824 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
825 static void centry_end(struct cache_entry *centry, const char *format, ...)
827 va_list ap;
828 char *kstr;
829 TDB_DATA key, data;
831 if (opt_nocache) {
832 return;
835 va_start(ap, format);
836 smb_xvasprintf(&kstr, format, ap);
837 va_end(ap);
839 key = string_tdb_data(kstr);
840 data.dptr = centry->data;
841 data.dsize = centry->ofs;
843 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
844 free(kstr);
847 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
848 NTSTATUS status, const char *domain_name,
849 const char *name, const DOM_SID *sid,
850 enum lsa_SidType type)
852 struct cache_entry *centry;
853 fstring uname;
855 centry = centry_start(domain, status);
856 if (!centry)
857 return;
858 centry_put_uint32(centry, type);
859 centry_put_sid(centry, sid);
860 fstrcpy(uname, name);
861 strupper_m(uname);
862 centry_end(centry, "NS/%s/%s", domain_name, uname);
863 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
864 uname, sid_string_dbg(sid), nt_errstr(status)));
865 centry_free(centry);
868 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
869 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
871 struct cache_entry *centry;
872 fstring sid_string;
874 centry = centry_start(domain, status);
875 if (!centry)
876 return;
878 if (NT_STATUS_IS_OK(status)) {
879 centry_put_uint32(centry, type);
880 centry_put_string(centry, domain_name);
881 centry_put_string(centry, name);
884 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
885 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
886 name, nt_errstr(status)));
887 centry_free(centry);
891 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
893 struct cache_entry *centry;
894 fstring sid_string;
896 if (is_null_sid(&info->user_sid)) {
897 return;
900 centry = centry_start(domain, status);
901 if (!centry)
902 return;
903 centry_put_string(centry, info->acct_name);
904 centry_put_string(centry, info->full_name);
905 centry_put_string(centry, info->homedir);
906 centry_put_string(centry, info->shell);
907 centry_put_uint32(centry, info->primary_gid);
908 centry_put_sid(centry, &info->user_sid);
909 centry_put_sid(centry, &info->group_sid);
910 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
911 &info->user_sid));
912 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
913 centry_free(centry);
916 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
917 NTSTATUS status,
918 struct samr_DomInfo12 *lockout_policy)
920 struct cache_entry *centry;
922 centry = centry_start(domain, status);
923 if (!centry)
924 return;
926 centry_put_nttime(centry, lockout_policy->lockout_duration);
927 centry_put_nttime(centry, lockout_policy->lockout_window);
928 centry_put_uint16(centry, lockout_policy->lockout_threshold);
930 centry_end(centry, "LOC_POL/%s", domain->name);
932 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
934 centry_free(centry);
937 static void wcache_save_password_policy(struct winbindd_domain *domain,
938 NTSTATUS status,
939 struct samr_DomInfo1 *policy)
941 struct cache_entry *centry;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
947 centry_put_uint16(centry, policy->min_password_length);
948 centry_put_uint16(centry, policy->password_history_length);
949 centry_put_uint32(centry, policy->password_properties);
950 centry_put_nttime(centry, policy->max_password_age);
951 centry_put_nttime(centry, policy->min_password_age);
953 centry_end(centry, "PWD_POL/%s", domain->name);
955 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
957 centry_free(centry);
960 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
962 struct winbind_cache *cache = get_cache(domain);
963 TDB_DATA data;
964 fstring key_str, tmp;
965 uint32 rid;
967 if (!cache->tdb) {
968 return NT_STATUS_INTERNAL_DB_ERROR;
971 if (is_null_sid(sid)) {
972 return NT_STATUS_INVALID_SID;
975 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
976 return NT_STATUS_INVALID_SID;
979 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
981 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
982 if (!data.dptr) {
983 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
986 SAFE_FREE(data.dptr);
987 return NT_STATUS_OK;
990 /* Lookup creds for a SID - copes with old (unsalted) creds as well
991 as new salted ones. */
993 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
994 TALLOC_CTX *mem_ctx,
995 const DOM_SID *sid,
996 const uint8 **cached_nt_pass,
997 const uint8 **cached_salt)
999 struct winbind_cache *cache = get_cache(domain);
1000 struct cache_entry *centry = NULL;
1001 NTSTATUS status;
1002 time_t t;
1003 uint32 rid;
1004 fstring tmp;
1006 if (!cache->tdb) {
1007 return NT_STATUS_INTERNAL_DB_ERROR;
1010 if (is_null_sid(sid)) {
1011 return NT_STATUS_INVALID_SID;
1014 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1015 return NT_STATUS_INVALID_SID;
1018 /* Try and get a salted cred first. If we can't
1019 fall back to an unsalted cred. */
1021 centry = wcache_fetch(cache, domain, "CRED/%s",
1022 sid_to_fstring(tmp, sid));
1023 if (!centry) {
1024 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1025 sid_string_dbg(sid)));
1026 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1029 t = centry_time(centry);
1031 /* In the salted case this isn't actually the nt_hash itself,
1032 but the MD5 of the salt + nt_hash. Let the caller
1033 sort this out. It can tell as we only return the cached_salt
1034 if we are returning a salted cred. */
1036 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1037 if (*cached_nt_pass == NULL) {
1038 fstring sidstr;
1040 sid_to_fstring(sidstr, sid);
1042 /* Bad (old) cred cache. Delete and pretend we
1043 don't have it. */
1044 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1045 sidstr));
1046 wcache_delete("CRED/%s", sidstr);
1047 centry_free(centry);
1048 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1051 /* We only have 17 bytes more data in the salted cred case. */
1052 if (centry->len - centry->ofs == 17) {
1053 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1054 } else {
1055 *cached_salt = NULL;
1058 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1059 if (*cached_salt) {
1060 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1063 status = centry->status;
1065 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1066 sid_string_dbg(sid), nt_errstr(status) ));
1068 centry_free(centry);
1069 return status;
1072 /* Store creds for a SID - only writes out new salted ones. */
1074 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1075 TALLOC_CTX *mem_ctx,
1076 const DOM_SID *sid,
1077 const uint8 nt_pass[NT_HASH_LEN])
1079 struct cache_entry *centry;
1080 fstring sid_string;
1081 uint32 rid;
1082 uint8 cred_salt[NT_HASH_LEN];
1083 uint8 salted_hash[NT_HASH_LEN];
1085 if (is_null_sid(sid)) {
1086 return NT_STATUS_INVALID_SID;
1089 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1090 return NT_STATUS_INVALID_SID;
1093 centry = centry_start(domain, NT_STATUS_OK);
1094 if (!centry) {
1095 return NT_STATUS_INTERNAL_DB_ERROR;
1098 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1100 centry_put_time(centry, time(NULL));
1102 /* Create a salt and then salt the hash. */
1103 generate_random_buffer(cred_salt, NT_HASH_LEN);
1104 E_md5hash(cred_salt, nt_pass, salted_hash);
1106 centry_put_hash16(centry, salted_hash);
1107 centry_put_hash16(centry, cred_salt);
1108 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1110 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1112 centry_free(centry);
1114 return NT_STATUS_OK;
1118 /* Query display info. This is the basic user list fn */
1119 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1120 TALLOC_CTX *mem_ctx,
1121 uint32 *num_entries,
1122 WINBIND_USERINFO **info)
1124 struct winbind_cache *cache = get_cache(domain);
1125 struct cache_entry *centry = NULL;
1126 NTSTATUS status;
1127 unsigned int i, retry;
1129 if (!cache->tdb)
1130 goto do_query;
1132 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1133 if (!centry)
1134 goto do_query;
1136 *num_entries = centry_uint32(centry);
1138 if (*num_entries == 0)
1139 goto do_cached;
1141 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1142 if (! (*info)) {
1143 smb_panic_fn("query_user_list out of memory");
1145 for (i=0; i<(*num_entries); i++) {
1146 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1147 (*info)[i].full_name = centry_string(centry, mem_ctx);
1148 (*info)[i].homedir = centry_string(centry, mem_ctx);
1149 (*info)[i].shell = centry_string(centry, mem_ctx);
1150 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1151 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1154 do_cached:
1155 status = centry->status;
1157 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1158 domain->name, nt_errstr(status) ));
1160 centry_free(centry);
1161 return status;
1163 do_query:
1164 *num_entries = 0;
1165 *info = NULL;
1167 /* Return status value returned by seq number check */
1169 if (!NT_STATUS_IS_OK(domain->last_status))
1170 return domain->last_status;
1172 /* Put the query_user_list() in a retry loop. There appears to be
1173 * some bug either with Windows 2000 or Samba's handling of large
1174 * rpc replies. This manifests itself as sudden disconnection
1175 * at a random point in the enumeration of a large (60k) user list.
1176 * The retry loop simply tries the operation again. )-: It's not
1177 * pretty but an acceptable workaround until we work out what the
1178 * real problem is. */
1180 retry = 0;
1181 do {
1183 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1184 domain->name ));
1186 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1187 if (!NT_STATUS_IS_OK(status)) {
1188 DEBUG(3, ("query_user_list: returned 0x%08x, "
1189 "retrying\n", NT_STATUS_V(status)));
1191 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1192 DEBUG(3, ("query_user_list: flushing "
1193 "connection cache\n"));
1194 invalidate_cm_connection(&domain->conn);
1197 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1198 (retry++ < 5));
1200 /* and save it */
1201 refresh_sequence_number(domain, false);
1202 centry = centry_start(domain, status);
1203 if (!centry)
1204 goto skip_save;
1205 centry_put_uint32(centry, *num_entries);
1206 for (i=0; i<(*num_entries); i++) {
1207 centry_put_string(centry, (*info)[i].acct_name);
1208 centry_put_string(centry, (*info)[i].full_name);
1209 centry_put_string(centry, (*info)[i].homedir);
1210 centry_put_string(centry, (*info)[i].shell);
1211 centry_put_sid(centry, &(*info)[i].user_sid);
1212 centry_put_sid(centry, &(*info)[i].group_sid);
1213 if (domain->backend && domain->backend->consistent) {
1214 /* when the backend is consistent we can pre-prime some mappings */
1215 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1216 domain->name,
1217 (*info)[i].acct_name,
1218 &(*info)[i].user_sid,
1219 SID_NAME_USER);
1220 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1221 &(*info)[i].user_sid,
1222 domain->name,
1223 (*info)[i].acct_name,
1224 SID_NAME_USER);
1225 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1228 centry_end(centry, "UL/%s", domain->name);
1229 centry_free(centry);
1231 skip_save:
1232 return status;
1235 /* list all domain groups */
1236 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1237 TALLOC_CTX *mem_ctx,
1238 uint32 *num_entries,
1239 struct acct_info **info)
1241 struct winbind_cache *cache = get_cache(domain);
1242 struct cache_entry *centry = NULL;
1243 NTSTATUS status;
1244 unsigned int i;
1246 if (!cache->tdb)
1247 goto do_query;
1249 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1250 if (!centry)
1251 goto do_query;
1253 *num_entries = centry_uint32(centry);
1255 if (*num_entries == 0)
1256 goto do_cached;
1258 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1259 if (! (*info)) {
1260 smb_panic_fn("enum_dom_groups out of memory");
1262 for (i=0; i<(*num_entries); i++) {
1263 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1264 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1265 (*info)[i].rid = centry_uint32(centry);
1268 do_cached:
1269 status = centry->status;
1271 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1272 domain->name, nt_errstr(status) ));
1274 centry_free(centry);
1275 return status;
1277 do_query:
1278 *num_entries = 0;
1279 *info = NULL;
1281 /* Return status value returned by seq number check */
1283 if (!NT_STATUS_IS_OK(domain->last_status))
1284 return domain->last_status;
1286 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1287 domain->name ));
1289 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1291 /* and save it */
1292 refresh_sequence_number(domain, false);
1293 centry = centry_start(domain, status);
1294 if (!centry)
1295 goto skip_save;
1296 centry_put_uint32(centry, *num_entries);
1297 for (i=0; i<(*num_entries); i++) {
1298 centry_put_string(centry, (*info)[i].acct_name);
1299 centry_put_string(centry, (*info)[i].acct_desc);
1300 centry_put_uint32(centry, (*info)[i].rid);
1302 centry_end(centry, "GL/%s/domain", domain->name);
1303 centry_free(centry);
1305 skip_save:
1306 return status;
1309 /* list all domain groups */
1310 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1311 TALLOC_CTX *mem_ctx,
1312 uint32 *num_entries,
1313 struct acct_info **info)
1315 struct winbind_cache *cache = get_cache(domain);
1316 struct cache_entry *centry = NULL;
1317 NTSTATUS status;
1318 unsigned int i;
1320 if (!cache->tdb)
1321 goto do_query;
1323 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1324 if (!centry)
1325 goto do_query;
1327 *num_entries = centry_uint32(centry);
1329 if (*num_entries == 0)
1330 goto do_cached;
1332 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1333 if (! (*info)) {
1334 smb_panic_fn("enum_dom_groups out of memory");
1336 for (i=0; i<(*num_entries); i++) {
1337 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1338 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1339 (*info)[i].rid = centry_uint32(centry);
1342 do_cached:
1344 /* If we are returning cached data and the domain controller
1345 is down then we don't know whether the data is up to date
1346 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1347 indicate this. */
1349 if (wcache_server_down(domain)) {
1350 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1351 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1352 } else
1353 status = centry->status;
1355 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1356 domain->name, nt_errstr(status) ));
1358 centry_free(centry);
1359 return status;
1361 do_query:
1362 *num_entries = 0;
1363 *info = NULL;
1365 /* Return status value returned by seq number check */
1367 if (!NT_STATUS_IS_OK(domain->last_status))
1368 return domain->last_status;
1370 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1371 domain->name ));
1373 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1375 /* and save it */
1376 refresh_sequence_number(domain, false);
1377 centry = centry_start(domain, status);
1378 if (!centry)
1379 goto skip_save;
1380 centry_put_uint32(centry, *num_entries);
1381 for (i=0; i<(*num_entries); i++) {
1382 centry_put_string(centry, (*info)[i].acct_name);
1383 centry_put_string(centry, (*info)[i].acct_desc);
1384 centry_put_uint32(centry, (*info)[i].rid);
1386 centry_end(centry, "GL/%s/local", domain->name);
1387 centry_free(centry);
1389 skip_save:
1390 return status;
1393 /* convert a single name to a sid in a domain */
1394 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1395 TALLOC_CTX *mem_ctx,
1396 enum winbindd_cmd orig_cmd,
1397 const char *domain_name,
1398 const char *name,
1399 DOM_SID *sid,
1400 enum lsa_SidType *type)
1402 struct winbind_cache *cache = get_cache(domain);
1403 struct cache_entry *centry = NULL;
1404 NTSTATUS status;
1405 fstring uname;
1407 if (!cache->tdb)
1408 goto do_query;
1410 fstrcpy(uname, name);
1411 strupper_m(uname);
1412 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1413 if (!centry)
1414 goto do_query;
1416 status = centry->status;
1417 if (NT_STATUS_IS_OK(status)) {
1418 *type = (enum lsa_SidType)centry_uint32(centry);
1419 centry_sid(centry, mem_ctx, sid);
1422 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1423 domain->name, nt_errstr(status) ));
1425 centry_free(centry);
1426 return status;
1428 do_query:
1429 ZERO_STRUCTP(sid);
1431 /* If the seq number check indicated that there is a problem
1432 * with this DC, then return that status... except for
1433 * access_denied. This is special because the dc may be in
1434 * "restrict anonymous = 1" mode, in which case it will deny
1435 * most unauthenticated operations, but *will* allow the LSA
1436 * name-to-sid that we try as a fallback. */
1438 if (!(NT_STATUS_IS_OK(domain->last_status)
1439 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1440 return domain->last_status;
1442 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1443 domain->name ));
1445 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1446 domain_name, name, sid, type);
1448 /* and save it */
1449 refresh_sequence_number(domain, false);
1451 if (domain->online &&
1452 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1453 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1455 /* Only save the reverse mapping if this was not a UPN */
1456 if (!strchr(name, '@')) {
1457 strupper_m(CONST_DISCARD(char *,domain_name));
1458 strlower_m(CONST_DISCARD(char *,name));
1459 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1463 return status;
1466 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1467 given */
1468 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1469 TALLOC_CTX *mem_ctx,
1470 const DOM_SID *sid,
1471 char **domain_name,
1472 char **name,
1473 enum lsa_SidType *type)
1475 struct winbind_cache *cache = get_cache(domain);
1476 struct cache_entry *centry = NULL;
1477 NTSTATUS status;
1478 fstring sid_string;
1480 if (!cache->tdb)
1481 goto do_query;
1483 centry = wcache_fetch(cache, domain, "SN/%s",
1484 sid_to_fstring(sid_string, sid));
1485 if (!centry)
1486 goto do_query;
1488 status = centry->status;
1489 if (NT_STATUS_IS_OK(status)) {
1490 *type = (enum lsa_SidType)centry_uint32(centry);
1491 *domain_name = centry_string(centry, mem_ctx);
1492 *name = centry_string(centry, mem_ctx);
1495 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1496 domain->name, nt_errstr(status) ));
1498 centry_free(centry);
1499 return status;
1501 do_query:
1502 *name = NULL;
1503 *domain_name = NULL;
1505 /* If the seq number check indicated that there is a problem
1506 * with this DC, then return that status... except for
1507 * access_denied. This is special because the dc may be in
1508 * "restrict anonymous = 1" mode, in which case it will deny
1509 * most unauthenticated operations, but *will* allow the LSA
1510 * sid-to-name that we try as a fallback. */
1512 if (!(NT_STATUS_IS_OK(domain->last_status)
1513 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1514 return domain->last_status;
1516 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1517 domain->name ));
1519 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1521 /* and save it */
1522 refresh_sequence_number(domain, false);
1523 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1525 /* We can't save the name to sid mapping here, as with sid history a
1526 * later name2sid would give the wrong sid. */
1528 return status;
1531 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1532 TALLOC_CTX *mem_ctx,
1533 const DOM_SID *domain_sid,
1534 uint32 *rids,
1535 size_t num_rids,
1536 char **domain_name,
1537 char ***names,
1538 enum lsa_SidType **types)
1540 struct winbind_cache *cache = get_cache(domain);
1541 size_t i;
1542 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1543 bool have_mapped;
1544 bool have_unmapped;
1546 *domain_name = NULL;
1547 *names = NULL;
1548 *types = NULL;
1550 if (!cache->tdb) {
1551 goto do_query;
1554 if (num_rids == 0) {
1555 return NT_STATUS_OK;
1558 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1559 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1561 if ((*names == NULL) || (*types == NULL)) {
1562 result = NT_STATUS_NO_MEMORY;
1563 goto error;
1566 have_mapped = have_unmapped = false;
1568 for (i=0; i<num_rids; i++) {
1569 DOM_SID sid;
1570 struct cache_entry *centry;
1571 fstring tmp;
1573 if (!sid_compose(&sid, domain_sid, rids[i])) {
1574 result = NT_STATUS_INTERNAL_ERROR;
1575 goto error;
1578 centry = wcache_fetch(cache, domain, "SN/%s",
1579 sid_to_fstring(tmp, &sid));
1580 if (!centry) {
1581 goto do_query;
1584 (*types)[i] = SID_NAME_UNKNOWN;
1585 (*names)[i] = talloc_strdup(*names, "");
1587 if (NT_STATUS_IS_OK(centry->status)) {
1588 char *dom;
1589 have_mapped = true;
1590 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1592 dom = centry_string(centry, mem_ctx);
1593 if (*domain_name == NULL) {
1594 *domain_name = dom;
1595 } else {
1596 talloc_free(dom);
1599 (*names)[i] = centry_string(centry, *names);
1601 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1602 have_unmapped = true;
1604 } else {
1605 /* something's definitely wrong */
1606 result = centry->status;
1607 goto error;
1610 centry_free(centry);
1613 if (!have_mapped) {
1614 return NT_STATUS_NONE_MAPPED;
1616 if (!have_unmapped) {
1617 return NT_STATUS_OK;
1619 return STATUS_SOME_UNMAPPED;
1621 do_query:
1623 TALLOC_FREE(*names);
1624 TALLOC_FREE(*types);
1626 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1627 rids, num_rids, domain_name,
1628 names, types);
1631 None of the queried rids has been found so save all negative entries
1633 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1634 for (i = 0; i < num_rids; i++) {
1635 DOM_SID sid;
1636 const char *name = "";
1637 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1638 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1640 if (!sid_compose(&sid, domain_sid, rids[i])) {
1641 return NT_STATUS_INTERNAL_ERROR;
1644 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1645 name, type);
1648 return result;
1652 Some or all of the queried rids have been found.
1654 if (!NT_STATUS_IS_OK(result) &&
1655 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1656 return result;
1659 refresh_sequence_number(domain, false);
1661 for (i=0; i<num_rids; i++) {
1662 DOM_SID sid;
1663 NTSTATUS status;
1665 if (!sid_compose(&sid, domain_sid, rids[i])) {
1666 result = NT_STATUS_INTERNAL_ERROR;
1667 goto error;
1670 status = (*types)[i] == SID_NAME_UNKNOWN ?
1671 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1673 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1674 (*names)[i], (*types)[i]);
1677 return result;
1679 error:
1681 TALLOC_FREE(*names);
1682 TALLOC_FREE(*types);
1683 return result;
1686 /* Lookup user information from a rid */
1687 static NTSTATUS query_user(struct winbindd_domain *domain,
1688 TALLOC_CTX *mem_ctx,
1689 const DOM_SID *user_sid,
1690 WINBIND_USERINFO *info)
1692 struct winbind_cache *cache = get_cache(domain);
1693 struct cache_entry *centry = NULL;
1694 NTSTATUS status;
1695 fstring tmp;
1697 if (!cache->tdb)
1698 goto do_query;
1700 centry = wcache_fetch(cache, domain, "U/%s",
1701 sid_to_fstring(tmp, user_sid));
1703 /* If we have an access denied cache entry and a cached info3 in the
1704 samlogon cache then do a query. This will force the rpc back end
1705 to return the info3 data. */
1707 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1708 netsamlogon_cache_have(user_sid)) {
1709 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1710 domain->last_status = NT_STATUS_OK;
1711 centry_free(centry);
1712 goto do_query;
1715 if (!centry)
1716 goto do_query;
1718 /* if status is not ok then this is a negative hit
1719 and the rest of the data doesn't matter */
1720 status = centry->status;
1721 if (NT_STATUS_IS_OK(status)) {
1722 info->acct_name = centry_string(centry, mem_ctx);
1723 info->full_name = centry_string(centry, mem_ctx);
1724 info->homedir = centry_string(centry, mem_ctx);
1725 info->shell = centry_string(centry, mem_ctx);
1726 info->primary_gid = centry_uint32(centry);
1727 centry_sid(centry, mem_ctx, &info->user_sid);
1728 centry_sid(centry, mem_ctx, &info->group_sid);
1731 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1732 domain->name, nt_errstr(status) ));
1734 centry_free(centry);
1735 return status;
1737 do_query:
1738 ZERO_STRUCTP(info);
1740 /* Return status value returned by seq number check */
1742 if (!NT_STATUS_IS_OK(domain->last_status))
1743 return domain->last_status;
1745 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1746 domain->name ));
1748 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1750 /* and save it */
1751 refresh_sequence_number(domain, false);
1752 wcache_save_user(domain, status, info);
1754 return status;
1758 /* Lookup groups a user is a member of. */
1759 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1760 TALLOC_CTX *mem_ctx,
1761 const DOM_SID *user_sid,
1762 uint32 *num_groups, DOM_SID **user_gids)
1764 struct winbind_cache *cache = get_cache(domain);
1765 struct cache_entry *centry = NULL;
1766 NTSTATUS status;
1767 unsigned int i;
1768 fstring sid_string;
1770 if (!cache->tdb)
1771 goto do_query;
1773 centry = wcache_fetch(cache, domain, "UG/%s",
1774 sid_to_fstring(sid_string, user_sid));
1776 /* If we have an access denied cache entry and a cached info3 in the
1777 samlogon cache then do a query. This will force the rpc back end
1778 to return the info3 data. */
1780 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1781 netsamlogon_cache_have(user_sid)) {
1782 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1783 domain->last_status = NT_STATUS_OK;
1784 centry_free(centry);
1785 goto do_query;
1788 if (!centry)
1789 goto do_query;
1791 *num_groups = centry_uint32(centry);
1793 if (*num_groups == 0)
1794 goto do_cached;
1796 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1797 if (! (*user_gids)) {
1798 smb_panic_fn("lookup_usergroups out of memory");
1800 for (i=0; i<(*num_groups); i++) {
1801 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1804 do_cached:
1805 status = centry->status;
1807 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1808 domain->name, nt_errstr(status) ));
1810 centry_free(centry);
1811 return status;
1813 do_query:
1814 (*num_groups) = 0;
1815 (*user_gids) = NULL;
1817 /* Return status value returned by seq number check */
1819 if (!NT_STATUS_IS_OK(domain->last_status))
1820 return domain->last_status;
1822 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1823 domain->name ));
1825 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1827 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1828 goto skip_save;
1830 /* and save it */
1831 refresh_sequence_number(domain, false);
1832 centry = centry_start(domain, status);
1833 if (!centry)
1834 goto skip_save;
1836 centry_put_uint32(centry, *num_groups);
1837 for (i=0; i<(*num_groups); i++) {
1838 centry_put_sid(centry, &(*user_gids)[i]);
1841 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
1842 centry_free(centry);
1844 skip_save:
1845 return status;
1848 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1849 TALLOC_CTX *mem_ctx,
1850 uint32 num_sids, const DOM_SID *sids,
1851 uint32 *num_aliases, uint32 **alias_rids)
1853 struct winbind_cache *cache = get_cache(domain);
1854 struct cache_entry *centry = NULL;
1855 NTSTATUS status;
1856 char *sidlist = talloc_strdup(mem_ctx, "");
1857 int i;
1859 if (!cache->tdb)
1860 goto do_query;
1862 if (num_sids == 0) {
1863 *num_aliases = 0;
1864 *alias_rids = NULL;
1865 return NT_STATUS_OK;
1868 /* We need to cache indexed by the whole list of SIDs, the aliases
1869 * resulting might come from any of the SIDs. */
1871 for (i=0; i<num_sids; i++) {
1872 fstring tmp;
1873 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1874 sid_to_fstring(tmp, &sids[i]));
1875 if (sidlist == NULL)
1876 return NT_STATUS_NO_MEMORY;
1879 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1881 if (!centry)
1882 goto do_query;
1884 *num_aliases = centry_uint32(centry);
1885 *alias_rids = NULL;
1887 if (*num_aliases) {
1888 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1890 if ((*alias_rids) == NULL) {
1891 centry_free(centry);
1892 return NT_STATUS_NO_MEMORY;
1894 } else {
1895 (*alias_rids) = NULL;
1898 for (i=0; i<(*num_aliases); i++)
1899 (*alias_rids)[i] = centry_uint32(centry);
1901 status = centry->status;
1903 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1904 "status %s\n", domain->name, nt_errstr(status)));
1906 centry_free(centry);
1907 return status;
1909 do_query:
1910 (*num_aliases) = 0;
1911 (*alias_rids) = NULL;
1913 if (!NT_STATUS_IS_OK(domain->last_status))
1914 return domain->last_status;
1916 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1917 "for domain %s\n", domain->name ));
1919 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1920 num_sids, sids,
1921 num_aliases, alias_rids);
1923 /* and save it */
1924 refresh_sequence_number(domain, false);
1925 centry = centry_start(domain, status);
1926 if (!centry)
1927 goto skip_save;
1928 centry_put_uint32(centry, *num_aliases);
1929 for (i=0; i<(*num_aliases); i++)
1930 centry_put_uint32(centry, (*alias_rids)[i]);
1931 centry_end(centry, "UA%s", sidlist);
1932 centry_free(centry);
1934 skip_save:
1935 return status;
1939 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1940 TALLOC_CTX *mem_ctx,
1941 const DOM_SID *group_sid, uint32 *num_names,
1942 DOM_SID **sid_mem, char ***names,
1943 uint32 **name_types)
1945 struct winbind_cache *cache = get_cache(domain);
1946 struct cache_entry *centry = NULL;
1947 NTSTATUS status;
1948 unsigned int i;
1949 fstring sid_string;
1951 if (!cache->tdb)
1952 goto do_query;
1954 centry = wcache_fetch(cache, domain, "GM/%s",
1955 sid_to_fstring(sid_string, group_sid));
1956 if (!centry)
1957 goto do_query;
1959 *num_names = centry_uint32(centry);
1961 if (*num_names == 0)
1962 goto do_cached;
1964 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1965 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1966 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1968 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1969 smb_panic_fn("lookup_groupmem out of memory");
1972 for (i=0; i<(*num_names); i++) {
1973 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1974 (*names)[i] = centry_string(centry, mem_ctx);
1975 (*name_types)[i] = centry_uint32(centry);
1978 do_cached:
1979 status = centry->status;
1981 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1982 domain->name, nt_errstr(status)));
1984 centry_free(centry);
1985 return status;
1987 do_query:
1988 (*num_names) = 0;
1989 (*sid_mem) = NULL;
1990 (*names) = NULL;
1991 (*name_types) = NULL;
1993 /* Return status value returned by seq number check */
1995 if (!NT_STATUS_IS_OK(domain->last_status))
1996 return domain->last_status;
1998 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1999 domain->name ));
2001 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2002 sid_mem, names, name_types);
2004 /* and save it */
2005 refresh_sequence_number(domain, false);
2006 centry = centry_start(domain, status);
2007 if (!centry)
2008 goto skip_save;
2009 centry_put_uint32(centry, *num_names);
2010 for (i=0; i<(*num_names); i++) {
2011 centry_put_sid(centry, &(*sid_mem)[i]);
2012 centry_put_string(centry, (*names)[i]);
2013 centry_put_uint32(centry, (*name_types)[i]);
2015 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2016 centry_free(centry);
2018 skip_save:
2019 return status;
2022 /* find the sequence number for a domain */
2023 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2025 refresh_sequence_number(domain, false);
2027 *seq = domain->sequence_number;
2029 return NT_STATUS_OK;
2032 /* enumerate trusted domains
2033 * (we need to have the list of trustdoms in the cache when we go offline) -
2034 * Guenther */
2035 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2036 TALLOC_CTX *mem_ctx,
2037 uint32 *num_domains,
2038 char ***names,
2039 char ***alt_names,
2040 DOM_SID **dom_sids)
2042 struct winbind_cache *cache = get_cache(domain);
2043 struct cache_entry *centry = NULL;
2044 NTSTATUS status;
2045 int i;
2047 if (!cache->tdb)
2048 goto do_query;
2050 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2052 if (!centry) {
2053 goto do_query;
2056 *num_domains = centry_uint32(centry);
2058 if (*num_domains) {
2059 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2060 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2061 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2063 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2064 smb_panic_fn("trusted_domains out of memory");
2066 } else {
2067 (*names) = NULL;
2068 (*alt_names) = NULL;
2069 (*dom_sids) = NULL;
2072 for (i=0; i<(*num_domains); i++) {
2073 (*names)[i] = centry_string(centry, mem_ctx);
2074 (*alt_names)[i] = centry_string(centry, mem_ctx);
2075 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
2078 status = centry->status;
2080 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2081 domain->name, *num_domains, nt_errstr(status) ));
2083 centry_free(centry);
2084 return status;
2086 do_query:
2087 (*num_domains) = 0;
2088 (*dom_sids) = NULL;
2089 (*names) = NULL;
2090 (*alt_names) = NULL;
2092 /* Return status value returned by seq number check */
2094 if (!NT_STATUS_IS_OK(domain->last_status))
2095 return domain->last_status;
2097 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2098 domain->name ));
2100 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2101 names, alt_names, dom_sids);
2103 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2104 * so that the generic centry handling still applies correctly -
2105 * Guenther*/
2107 if (!NT_STATUS_IS_ERR(status)) {
2108 status = NT_STATUS_OK;
2112 #if 0 /* Disabled as we want the trust dom list to be managed by
2113 the main parent and always to make the query. --jerry */
2115 /* and save it */
2116 refresh_sequence_number(domain, false);
2118 centry = centry_start(domain, status);
2119 if (!centry)
2120 goto skip_save;
2122 centry_put_uint32(centry, *num_domains);
2124 for (i=0; i<(*num_domains); i++) {
2125 centry_put_string(centry, (*names)[i]);
2126 centry_put_string(centry, (*alt_names)[i]);
2127 centry_put_sid(centry, &(*dom_sids)[i]);
2130 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2132 centry_free(centry);
2134 skip_save:
2135 #endif
2137 return status;
2140 /* get lockout policy */
2141 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2142 TALLOC_CTX *mem_ctx,
2143 struct samr_DomInfo12 *policy)
2145 struct winbind_cache *cache = get_cache(domain);
2146 struct cache_entry *centry = NULL;
2147 NTSTATUS status;
2149 if (!cache->tdb)
2150 goto do_query;
2152 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2154 if (!centry)
2155 goto do_query;
2157 policy->lockout_duration = centry_nttime(centry);
2158 policy->lockout_window = centry_nttime(centry);
2159 policy->lockout_threshold = centry_uint16(centry);
2161 status = centry->status;
2163 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2164 domain->name, nt_errstr(status) ));
2166 centry_free(centry);
2167 return status;
2169 do_query:
2170 ZERO_STRUCTP(policy);
2172 /* Return status value returned by seq number check */
2174 if (!NT_STATUS_IS_OK(domain->last_status))
2175 return domain->last_status;
2177 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2178 domain->name ));
2180 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2182 /* and save it */
2183 refresh_sequence_number(domain, false);
2184 wcache_save_lockout_policy(domain, status, policy);
2186 return status;
2189 /* get password policy */
2190 static NTSTATUS password_policy(struct winbindd_domain *domain,
2191 TALLOC_CTX *mem_ctx,
2192 struct samr_DomInfo1 *policy)
2194 struct winbind_cache *cache = get_cache(domain);
2195 struct cache_entry *centry = NULL;
2196 NTSTATUS status;
2198 if (!cache->tdb)
2199 goto do_query;
2201 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2203 if (!centry)
2204 goto do_query;
2206 policy->min_password_length = centry_uint16(centry);
2207 policy->password_history_length = centry_uint16(centry);
2208 policy->password_properties = centry_uint32(centry);
2209 policy->max_password_age = centry_nttime(centry);
2210 policy->min_password_age = centry_nttime(centry);
2212 status = centry->status;
2214 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2215 domain->name, nt_errstr(status) ));
2217 centry_free(centry);
2218 return status;
2220 do_query:
2221 ZERO_STRUCTP(policy);
2223 /* Return status value returned by seq number check */
2225 if (!NT_STATUS_IS_OK(domain->last_status))
2226 return domain->last_status;
2228 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2229 domain->name ));
2231 status = domain->backend->password_policy(domain, mem_ctx, policy);
2233 /* and save it */
2234 refresh_sequence_number(domain, false);
2235 wcache_save_password_policy(domain, status, policy);
2237 return status;
2241 /* Invalidate cached user and group lists coherently */
2243 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2244 void *state)
2246 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2247 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2248 tdb_delete(the_tdb, kbuf);
2250 return 0;
2253 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2255 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2256 struct netr_SamInfo3 *info3)
2258 struct winbind_cache *cache;
2260 /* dont clear cached U/SID and UG/SID entries when we want to logon
2261 * offline - gd */
2263 if (lp_winbind_offline_logon()) {
2264 return;
2267 if (!domain)
2268 return;
2270 cache = get_cache(domain);
2271 netsamlogon_clear_cached_user(cache->tdb, info3);
2274 bool wcache_invalidate_cache(void)
2276 struct winbindd_domain *domain;
2278 for (domain = domain_list(); domain; domain = domain->next) {
2279 struct winbind_cache *cache = get_cache(domain);
2281 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2282 "entries for %s\n", domain->name));
2283 if (cache) {
2284 if (cache->tdb) {
2285 tdb_traverse(cache->tdb, traverse_fn, NULL);
2286 } else {
2287 return false;
2291 return true;
2294 bool init_wcache(void)
2296 if (wcache == NULL) {
2297 wcache = SMB_XMALLOC_P(struct winbind_cache);
2298 ZERO_STRUCTP(wcache);
2301 if (wcache->tdb != NULL)
2302 return true;
2304 /* when working offline we must not clear the cache on restart */
2305 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2306 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2307 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2308 O_RDWR|O_CREAT, 0600);
2310 if (wcache->tdb == NULL) {
2311 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2312 return false;
2315 return true;
2318 /************************************************************************
2319 This is called by the parent to initialize the cache file.
2320 We don't need sophisticated locking here as we know we're the
2321 only opener.
2322 ************************************************************************/
2324 bool initialize_winbindd_cache(void)
2326 bool cache_bad = true;
2327 uint32 vers;
2329 if (!init_wcache()) {
2330 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2331 return false;
2334 /* Check version number. */
2335 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2336 vers == WINBINDD_CACHE_VERSION) {
2337 cache_bad = false;
2340 if (cache_bad) {
2341 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2342 "and re-creating with version number %d\n",
2343 WINBINDD_CACHE_VERSION ));
2345 tdb_close(wcache->tdb);
2346 wcache->tdb = NULL;
2348 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2349 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2350 lock_path("winbindd_cache.tdb"),
2351 strerror(errno) ));
2352 return false;
2354 if (!init_wcache()) {
2355 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2356 "init_wcache failed.\n"));
2357 return false;
2360 /* Write the version. */
2361 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2362 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2363 tdb_errorstr(wcache->tdb) ));
2364 return false;
2368 tdb_close(wcache->tdb);
2369 wcache->tdb = NULL;
2370 return true;
2373 void close_winbindd_cache(void)
2375 if (!wcache) {
2376 return;
2378 if (wcache->tdb) {
2379 tdb_close(wcache->tdb);
2380 wcache->tdb = NULL;
2384 void cache_store_response(pid_t pid, struct winbindd_response *response)
2386 fstring key_str;
2388 if (!init_wcache())
2389 return;
2391 DEBUG(10, ("Storing response for pid %d, len %d\n",
2392 pid, response->length));
2394 fstr_sprintf(key_str, "DR/%d", pid);
2395 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2396 make_tdb_data((uint8 *)response, sizeof(*response)),
2397 TDB_REPLACE) == -1)
2398 return;
2400 if (response->length == sizeof(*response))
2401 return;
2403 /* There's extra data */
2405 DEBUG(10, ("Storing extra data: len=%d\n",
2406 (int)(response->length - sizeof(*response))));
2408 fstr_sprintf(key_str, "DE/%d", pid);
2409 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2410 make_tdb_data((uint8 *)response->extra_data.data,
2411 response->length - sizeof(*response)),
2412 TDB_REPLACE) == 0)
2413 return;
2415 /* We could not store the extra data, make sure the tdb does not
2416 * contain a main record with wrong dangling extra data */
2418 fstr_sprintf(key_str, "DR/%d", pid);
2419 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2421 return;
2424 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2426 TDB_DATA data;
2427 fstring key_str;
2429 if (!init_wcache())
2430 return false;
2432 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2434 fstr_sprintf(key_str, "DR/%d", pid);
2435 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2437 if (data.dptr == NULL)
2438 return false;
2440 if (data.dsize != sizeof(*response))
2441 return false;
2443 memcpy(response, data.dptr, data.dsize);
2444 SAFE_FREE(data.dptr);
2446 if (response->length == sizeof(*response)) {
2447 response->extra_data.data = NULL;
2448 return true;
2451 /* There's extra data */
2453 DEBUG(10, ("Retrieving extra data length=%d\n",
2454 (int)(response->length - sizeof(*response))));
2456 fstr_sprintf(key_str, "DE/%d", pid);
2457 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2459 if (data.dptr == NULL) {
2460 DEBUG(0, ("Did not find extra data\n"));
2461 return false;
2464 if (data.dsize != (response->length - sizeof(*response))) {
2465 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2466 SAFE_FREE(data.dptr);
2467 return false;
2470 dump_data(11, (uint8 *)data.dptr, data.dsize);
2472 response->extra_data.data = data.dptr;
2473 return true;
2476 void cache_cleanup_response(pid_t pid)
2478 fstring key_str;
2480 if (!init_wcache())
2481 return;
2483 fstr_sprintf(key_str, "DR/%d", pid);
2484 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2486 fstr_sprintf(key_str, "DE/%d", pid);
2487 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2489 return;
2493 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2494 char **domain_name, char **name,
2495 enum lsa_SidType *type)
2497 struct winbindd_domain *domain;
2498 struct winbind_cache *cache;
2499 struct cache_entry *centry = NULL;
2500 NTSTATUS status;
2501 fstring tmp;
2503 domain = find_lookup_domain_from_sid(sid);
2504 if (domain == NULL) {
2505 return false;
2508 cache = get_cache(domain);
2510 if (cache->tdb == NULL) {
2511 return false;
2514 centry = wcache_fetch(cache, domain, "SN/%s",
2515 sid_to_fstring(tmp, sid));
2516 if (centry == NULL) {
2517 return false;
2520 if (NT_STATUS_IS_OK(centry->status)) {
2521 *type = (enum lsa_SidType)centry_uint32(centry);
2522 *domain_name = centry_string(centry, mem_ctx);
2523 *name = centry_string(centry, mem_ctx);
2526 status = centry->status;
2527 centry_free(centry);
2528 return NT_STATUS_IS_OK(status);
2531 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2532 const char *domain_name,
2533 const char *name,
2534 DOM_SID *sid,
2535 enum lsa_SidType *type)
2537 struct winbindd_domain *domain;
2538 struct winbind_cache *cache;
2539 struct cache_entry *centry = NULL;
2540 NTSTATUS status;
2541 fstring uname;
2542 bool original_online_state;
2544 domain = find_lookup_domain_from_name(domain_name);
2545 if (domain == NULL) {
2546 return false;
2549 cache = get_cache(domain);
2551 if (cache->tdb == NULL) {
2552 return false;
2555 fstrcpy(uname, name);
2556 strupper_m(uname);
2558 /* If we are doing a cached logon, temporarily set the domain
2559 offline so the cache won't expire the entry */
2561 original_online_state = domain->online;
2562 domain->online = false;
2563 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2564 domain->online = original_online_state;
2566 if (centry == NULL) {
2567 return false;
2570 if (NT_STATUS_IS_OK(centry->status)) {
2571 *type = (enum lsa_SidType)centry_uint32(centry);
2572 centry_sid(centry, mem_ctx, sid);
2575 status = centry->status;
2576 centry_free(centry);
2578 return NT_STATUS_IS_OK(status);
2581 void cache_name2sid(struct winbindd_domain *domain,
2582 const char *domain_name, const char *name,
2583 enum lsa_SidType type, const DOM_SID *sid)
2585 refresh_sequence_number(domain, false);
2586 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2587 sid, type);
2591 * The original idea that this cache only contains centries has
2592 * been blurred - now other stuff gets put in here. Ensure we
2593 * ignore these things on cleanup.
2596 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2597 TDB_DATA dbuf, void *state)
2599 struct cache_entry *centry;
2601 if (is_non_centry_key(kbuf)) {
2602 return 0;
2605 centry = wcache_fetch_raw((char *)kbuf.dptr);
2606 if (!centry) {
2607 return 0;
2610 if (!NT_STATUS_IS_OK(centry->status)) {
2611 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2612 tdb_delete(the_tdb, kbuf);
2615 centry_free(centry);
2616 return 0;
2619 /* flush the cache */
2620 void wcache_flush_cache(void)
2622 if (!wcache)
2623 return;
2624 if (wcache->tdb) {
2625 tdb_close(wcache->tdb);
2626 wcache->tdb = NULL;
2628 if (opt_nocache)
2629 return;
2631 /* when working offline we must not clear the cache on restart */
2632 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2633 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2634 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2635 O_RDWR|O_CREAT, 0600);
2637 if (!wcache->tdb) {
2638 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2639 return;
2642 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2644 DEBUG(10,("wcache_flush_cache success\n"));
2647 /* Count cached creds */
2649 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2650 void *state)
2652 int *cred_count = (int*)state;
2654 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2655 (*cred_count)++;
2657 return 0;
2660 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2662 struct winbind_cache *cache = get_cache(domain);
2664 *count = 0;
2666 if (!cache->tdb) {
2667 return NT_STATUS_INTERNAL_DB_ERROR;
2670 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2672 return NT_STATUS_OK;
2675 struct cred_list {
2676 struct cred_list *prev, *next;
2677 TDB_DATA key;
2678 fstring name;
2679 time_t created;
2681 static struct cred_list *wcache_cred_list;
2683 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2684 void *state)
2686 struct cred_list *cred;
2688 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2690 cred = SMB_MALLOC_P(struct cred_list);
2691 if (cred == NULL) {
2692 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2693 return -1;
2696 ZERO_STRUCTP(cred);
2698 /* save a copy of the key */
2700 fstrcpy(cred->name, (const char *)kbuf.dptr);
2701 DLIST_ADD(wcache_cred_list, cred);
2704 return 0;
2707 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2709 struct winbind_cache *cache = get_cache(domain);
2710 NTSTATUS status;
2711 int ret;
2712 struct cred_list *cred, *oldest = NULL;
2714 if (!cache->tdb) {
2715 return NT_STATUS_INTERNAL_DB_ERROR;
2718 /* we possibly already have an entry */
2719 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2721 fstring key_str, tmp;
2723 DEBUG(11,("we already have an entry, deleting that\n"));
2725 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2727 tdb_delete(cache->tdb, string_tdb_data(key_str));
2729 return NT_STATUS_OK;
2732 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2733 if (ret == 0) {
2734 return NT_STATUS_OK;
2735 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2736 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2739 ZERO_STRUCTP(oldest);
2741 for (cred = wcache_cred_list; cred; cred = cred->next) {
2743 TDB_DATA data;
2744 time_t t;
2746 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2747 if (!data.dptr) {
2748 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2749 cred->name));
2750 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2751 goto done;
2754 t = IVAL(data.dptr, 0);
2755 SAFE_FREE(data.dptr);
2757 if (!oldest) {
2758 oldest = SMB_MALLOC_P(struct cred_list);
2759 if (oldest == NULL) {
2760 status = NT_STATUS_NO_MEMORY;
2761 goto done;
2764 fstrcpy(oldest->name, cred->name);
2765 oldest->created = t;
2766 continue;
2769 if (t < oldest->created) {
2770 fstrcpy(oldest->name, cred->name);
2771 oldest->created = t;
2775 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2776 status = NT_STATUS_OK;
2777 } else {
2778 status = NT_STATUS_UNSUCCESSFUL;
2780 done:
2781 SAFE_FREE(wcache_cred_list);
2782 SAFE_FREE(oldest);
2784 return status;
2787 /* Change the global online/offline state. */
2788 bool set_global_winbindd_state_offline(void)
2790 TDB_DATA data;
2792 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2794 /* Only go offline if someone has created
2795 the key "WINBINDD_OFFLINE" in the cache tdb. */
2797 if (wcache == NULL || wcache->tdb == NULL) {
2798 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2799 return false;
2802 if (!lp_winbind_offline_logon()) {
2803 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2804 return false;
2807 if (global_winbindd_offline_state) {
2808 /* Already offline. */
2809 return true;
2812 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2814 if (!data.dptr || data.dsize != 4) {
2815 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2816 SAFE_FREE(data.dptr);
2817 return false;
2818 } else {
2819 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2820 global_winbindd_offline_state = true;
2821 SAFE_FREE(data.dptr);
2822 return true;
2826 void set_global_winbindd_state_online(void)
2828 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2830 if (!lp_winbind_offline_logon()) {
2831 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2832 return;
2835 if (!global_winbindd_offline_state) {
2836 /* Already online. */
2837 return;
2839 global_winbindd_offline_state = false;
2841 if (!wcache->tdb) {
2842 return;
2845 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2846 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2849 bool get_global_winbindd_state_offline(void)
2851 return global_winbindd_offline_state;
2854 /***********************************************************************
2855 Validate functions for all possible cache tdb keys.
2856 ***********************************************************************/
2858 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2859 struct tdb_validation_status *state)
2861 struct cache_entry *centry;
2863 centry = SMB_XMALLOC_P(struct cache_entry);
2864 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2865 if (!centry->data) {
2866 SAFE_FREE(centry);
2867 return NULL;
2869 centry->len = data.dsize;
2870 centry->ofs = 0;
2872 if (centry->len < 8) {
2873 /* huh? corrupt cache? */
2874 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2875 centry_free(centry);
2876 state->bad_entry = true;
2877 state->success = false;
2878 return NULL;
2881 centry->status = NT_STATUS(centry_uint32(centry));
2882 centry->sequence_number = centry_uint32(centry);
2883 return centry;
2886 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2887 struct tdb_validation_status *state)
2889 if (dbuf.dsize != 8) {
2890 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2891 keystr, (unsigned int)dbuf.dsize ));
2892 state->bad_entry = true;
2893 return 1;
2895 return 0;
2898 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2899 struct tdb_validation_status *state)
2901 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2902 if (!centry) {
2903 return 1;
2906 (void)centry_uint32(centry);
2907 if (NT_STATUS_IS_OK(centry->status)) {
2908 DOM_SID sid;
2909 (void)centry_sid(centry, mem_ctx, &sid);
2912 centry_free(centry);
2914 if (!(state->success)) {
2915 return 1;
2917 DEBUG(10,("validate_ns: %s ok\n", keystr));
2918 return 0;
2921 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2922 struct tdb_validation_status *state)
2924 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2925 if (!centry) {
2926 return 1;
2929 if (NT_STATUS_IS_OK(centry->status)) {
2930 (void)centry_uint32(centry);
2931 (void)centry_string(centry, mem_ctx);
2932 (void)centry_string(centry, mem_ctx);
2935 centry_free(centry);
2937 if (!(state->success)) {
2938 return 1;
2940 DEBUG(10,("validate_sn: %s ok\n", keystr));
2941 return 0;
2944 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2945 struct tdb_validation_status *state)
2947 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2948 DOM_SID sid;
2950 if (!centry) {
2951 return 1;
2954 (void)centry_string(centry, mem_ctx);
2955 (void)centry_string(centry, mem_ctx);
2956 (void)centry_string(centry, mem_ctx);
2957 (void)centry_string(centry, mem_ctx);
2958 (void)centry_uint32(centry);
2959 (void)centry_sid(centry, mem_ctx, &sid);
2960 (void)centry_sid(centry, mem_ctx, &sid);
2962 centry_free(centry);
2964 if (!(state->success)) {
2965 return 1;
2967 DEBUG(10,("validate_u: %s ok\n", keystr));
2968 return 0;
2971 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2972 struct tdb_validation_status *state)
2974 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2976 if (!centry) {
2977 return 1;
2980 (void)centry_nttime(centry);
2981 (void)centry_nttime(centry);
2982 (void)centry_uint16(centry);
2984 centry_free(centry);
2986 if (!(state->success)) {
2987 return 1;
2989 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2990 return 0;
2993 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2994 struct tdb_validation_status *state)
2996 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2998 if (!centry) {
2999 return 1;
3002 (void)centry_uint16(centry);
3003 (void)centry_uint16(centry);
3004 (void)centry_uint32(centry);
3005 (void)centry_nttime(centry);
3006 (void)centry_nttime(centry);
3008 centry_free(centry);
3010 if (!(state->success)) {
3011 return 1;
3013 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3014 return 0;
3017 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3018 struct tdb_validation_status *state)
3020 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3022 if (!centry) {
3023 return 1;
3026 (void)centry_time(centry);
3027 (void)centry_hash16(centry, mem_ctx);
3029 /* We only have 17 bytes more data in the salted cred case. */
3030 if (centry->len - centry->ofs == 17) {
3031 (void)centry_hash16(centry, mem_ctx);
3034 centry_free(centry);
3036 if (!(state->success)) {
3037 return 1;
3039 DEBUG(10,("validate_cred: %s ok\n", keystr));
3040 return 0;
3043 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3044 struct tdb_validation_status *state)
3046 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3047 int32 num_entries, i;
3049 if (!centry) {
3050 return 1;
3053 num_entries = (int32)centry_uint32(centry);
3055 for (i=0; i< num_entries; i++) {
3056 DOM_SID sid;
3057 (void)centry_string(centry, mem_ctx);
3058 (void)centry_string(centry, mem_ctx);
3059 (void)centry_string(centry, mem_ctx);
3060 (void)centry_string(centry, mem_ctx);
3061 (void)centry_sid(centry, mem_ctx, &sid);
3062 (void)centry_sid(centry, mem_ctx, &sid);
3065 centry_free(centry);
3067 if (!(state->success)) {
3068 return 1;
3070 DEBUG(10,("validate_ul: %s ok\n", keystr));
3071 return 0;
3074 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3075 struct tdb_validation_status *state)
3077 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3078 int32 num_entries, i;
3080 if (!centry) {
3081 return 1;
3084 num_entries = centry_uint32(centry);
3086 for (i=0; i< num_entries; i++) {
3087 (void)centry_string(centry, mem_ctx);
3088 (void)centry_string(centry, mem_ctx);
3089 (void)centry_uint32(centry);
3092 centry_free(centry);
3094 if (!(state->success)) {
3095 return 1;
3097 DEBUG(10,("validate_gl: %s ok\n", keystr));
3098 return 0;
3101 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3102 struct tdb_validation_status *state)
3104 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3105 int32 num_groups, i;
3107 if (!centry) {
3108 return 1;
3111 num_groups = centry_uint32(centry);
3113 for (i=0; i< num_groups; i++) {
3114 DOM_SID sid;
3115 centry_sid(centry, mem_ctx, &sid);
3118 centry_free(centry);
3120 if (!(state->success)) {
3121 return 1;
3123 DEBUG(10,("validate_ug: %s ok\n", keystr));
3124 return 0;
3127 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3128 struct tdb_validation_status *state)
3130 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3131 int32 num_aliases, i;
3133 if (!centry) {
3134 return 1;
3137 num_aliases = centry_uint32(centry);
3139 for (i=0; i < num_aliases; i++) {
3140 (void)centry_uint32(centry);
3143 centry_free(centry);
3145 if (!(state->success)) {
3146 return 1;
3148 DEBUG(10,("validate_ua: %s ok\n", keystr));
3149 return 0;
3152 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3153 struct tdb_validation_status *state)
3155 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3156 int32 num_names, i;
3158 if (!centry) {
3159 return 1;
3162 num_names = centry_uint32(centry);
3164 for (i=0; i< num_names; i++) {
3165 DOM_SID sid;
3166 centry_sid(centry, mem_ctx, &sid);
3167 (void)centry_string(centry, mem_ctx);
3168 (void)centry_uint32(centry);
3171 centry_free(centry);
3173 if (!(state->success)) {
3174 return 1;
3176 DEBUG(10,("validate_gm: %s ok\n", keystr));
3177 return 0;
3180 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3181 struct tdb_validation_status *state)
3183 /* Can't say anything about this other than must be nonzero. */
3184 if (dbuf.dsize == 0) {
3185 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3186 keystr));
3187 state->bad_entry = true;
3188 state->success = false;
3189 return 1;
3192 DEBUG(10,("validate_dr: %s ok\n", keystr));
3193 return 0;
3196 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3197 struct tdb_validation_status *state)
3199 /* Can't say anything about this other than must be nonzero. */
3200 if (dbuf.dsize == 0) {
3201 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3202 keystr));
3203 state->bad_entry = true;
3204 state->success = false;
3205 return 1;
3208 DEBUG(10,("validate_de: %s ok\n", keystr));
3209 return 0;
3212 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3213 TDB_DATA dbuf, struct tdb_validation_status *state)
3215 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3217 if (!centry) {
3218 return 1;
3221 (void)centry_string(centry, mem_ctx);
3222 (void)centry_string(centry, mem_ctx);
3223 (void)centry_string(centry, mem_ctx);
3224 (void)centry_uint32(centry);
3226 centry_free(centry);
3228 if (!(state->success)) {
3229 return 1;
3231 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3232 return 0;
3235 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3236 struct tdb_validation_status *state)
3238 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3239 int32 num_domains, i;
3241 if (!centry) {
3242 return 1;
3245 num_domains = centry_uint32(centry);
3247 for (i=0; i< num_domains; i++) {
3248 DOM_SID sid;
3249 (void)centry_string(centry, mem_ctx);
3250 (void)centry_string(centry, mem_ctx);
3251 (void)centry_sid(centry, mem_ctx, &sid);
3254 centry_free(centry);
3256 if (!(state->success)) {
3257 return 1;
3259 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3260 return 0;
3263 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3264 TDB_DATA dbuf,
3265 struct tdb_validation_status *state)
3267 if (dbuf.dsize == 0) {
3268 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3269 "key %s (len ==0) ?\n", keystr));
3270 state->bad_entry = true;
3271 state->success = false;
3272 return 1;
3275 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3276 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3277 return 0;
3280 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3281 struct tdb_validation_status *state)
3283 if (dbuf.dsize != 4) {
3284 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3285 keystr, (unsigned int)dbuf.dsize ));
3286 state->bad_entry = true;
3287 state->success = false;
3288 return 1;
3290 DEBUG(10,("validate_offline: %s ok\n", keystr));
3291 return 0;
3294 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3295 struct tdb_validation_status *state)
3297 if (dbuf.dsize != 4) {
3298 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3299 "key %s (len %u != 4) ?\n",
3300 keystr, (unsigned int)dbuf.dsize));
3301 state->bad_entry = true;
3302 state->success = false;
3303 return 1;
3306 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3307 return 0;
3310 /***********************************************************************
3311 A list of all possible cache tdb keys with associated validation
3312 functions.
3313 ***********************************************************************/
3315 struct key_val_struct {
3316 const char *keyname;
3317 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3318 } key_val[] = {
3319 {"SEQNUM/", validate_seqnum},
3320 {"NS/", validate_ns},
3321 {"SN/", validate_sn},
3322 {"U/", validate_u},
3323 {"LOC_POL/", validate_loc_pol},
3324 {"PWD_POL/", validate_pwd_pol},
3325 {"CRED/", validate_cred},
3326 {"UL/", validate_ul},
3327 {"GL/", validate_gl},
3328 {"UG/", validate_ug},
3329 {"UA", validate_ua},
3330 {"GM/", validate_gm},
3331 {"DR/", validate_dr},
3332 {"DE/", validate_de},
3333 {"NSS/PWINFO/", validate_pwinfo},
3334 {"TRUSTDOMS/", validate_trustdoms},
3335 {"TRUSTDOMCACHE/", validate_trustdomcache},
3336 {"WINBINDD_OFFLINE", validate_offline},
3337 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3338 {NULL, NULL}
3341 /***********************************************************************
3342 Function to look at every entry in the tdb and validate it as far as
3343 possible.
3344 ***********************************************************************/
3346 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3348 int i;
3349 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3351 /* Paranoia check. */
3352 if (kbuf.dsize > 1024) {
3353 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3354 (unsigned int)kbuf.dsize ));
3355 return 1;
3358 for (i = 0; key_val[i].keyname; i++) {
3359 size_t namelen = strlen(key_val[i].keyname);
3360 if (kbuf.dsize >= namelen && (
3361 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3362 TALLOC_CTX *mem_ctx;
3363 char *keystr;
3364 int ret;
3366 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3367 if (!keystr) {
3368 return 1;
3370 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3371 keystr[kbuf.dsize] = '\0';
3373 mem_ctx = talloc_init("validate_ctx");
3374 if (!mem_ctx) {
3375 SAFE_FREE(keystr);
3376 return 1;
3379 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3380 v_state);
3382 SAFE_FREE(keystr);
3383 talloc_destroy(mem_ctx);
3384 return ret;
3388 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3389 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3390 DEBUG(0,("data :\n"));
3391 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3392 v_state->unknown_key = true;
3393 v_state->success = false;
3394 return 1; /* terminate. */
3397 static void validate_panic(const char *const why)
3399 DEBUG(0,("validating cache: would panic %s\n", why ));
3400 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3401 exit(47);
3404 /***********************************************************************
3405 Try and validate every entry in the winbindd cache. If we fail here,
3406 delete the cache tdb and return non-zero.
3407 ***********************************************************************/
3409 int winbindd_validate_cache(void)
3411 int ret = -1;
3412 const char *tdb_path = lock_path("winbindd_cache.tdb");
3413 TDB_CONTEXT *tdb = NULL;
3415 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3416 smb_panic_fn = validate_panic;
3419 tdb = tdb_open_log(tdb_path,
3420 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3421 ( lp_winbind_offline_logon()
3422 ? TDB_DEFAULT
3423 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3424 O_RDWR|O_CREAT,
3425 0600);
3426 if (!tdb) {
3427 DEBUG(0, ("winbindd_validate_cache: "
3428 "error opening/initializing tdb\n"));
3429 goto done;
3431 tdb_close(tdb);
3433 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3435 if (ret != 0) {
3436 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3437 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3438 unlink(tdb_path);
3441 done:
3442 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3443 smb_panic_fn = smb_panic;
3444 return ret;
3447 /***********************************************************************
3448 Try and validate every entry in the winbindd cache.
3449 ***********************************************************************/
3451 int winbindd_validate_cache_nobackup(void)
3453 int ret = -1;
3454 const char *tdb_path = lock_path("winbindd_cache.tdb");
3456 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3457 smb_panic_fn = validate_panic;
3460 if (wcache == NULL || wcache->tdb == NULL) {
3461 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3462 } else {
3463 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3466 if (ret != 0) {
3467 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3468 "successful.\n"));
3471 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3472 "function\n"));
3473 smb_panic_fn = smb_panic;
3474 return ret;
3477 bool winbindd_cache_validate_and_initialize(void)
3479 close_winbindd_cache();
3481 if (lp_winbind_offline_logon()) {
3482 if (winbindd_validate_cache() < 0) {
3483 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3484 "could be restored.\n"));
3488 return initialize_winbindd_cache();
3491 /*********************************************************************
3492 ********************************************************************/
3494 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3495 struct winbindd_tdc_domain **domains,
3496 size_t *num_domains )
3498 struct winbindd_tdc_domain *list = NULL;
3499 size_t idx;
3500 int i;
3501 bool set_only = false;
3503 /* don't allow duplicates */
3505 idx = *num_domains;
3506 list = *domains;
3508 for ( i=0; i< (*num_domains); i++ ) {
3509 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3510 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3511 new_dom->name));
3512 idx = i;
3513 set_only = true;
3515 break;
3519 if ( !set_only ) {
3520 if ( !*domains ) {
3521 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3522 idx = 0;
3523 } else {
3524 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3525 struct winbindd_tdc_domain,
3526 (*num_domains)+1);
3527 idx = *num_domains;
3530 ZERO_STRUCT( list[idx] );
3533 if ( !list )
3534 return false;
3536 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3537 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3539 if ( !is_null_sid( &new_dom->sid ) )
3540 sid_copy( &list[idx].sid, &new_dom->sid );
3542 if ( new_dom->domain_flags != 0x0 )
3543 list[idx].trust_flags = new_dom->domain_flags;
3545 if ( new_dom->domain_type != 0x0 )
3546 list[idx].trust_type = new_dom->domain_type;
3548 if ( new_dom->domain_trust_attribs != 0x0 )
3549 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3551 if ( !set_only ) {
3552 *domains = list;
3553 *num_domains = idx + 1;
3556 return true;
3559 /*********************************************************************
3560 ********************************************************************/
3562 static TDB_DATA make_tdc_key( const char *domain_name )
3564 char *keystr = NULL;
3565 TDB_DATA key = { NULL, 0 };
3567 if ( !domain_name ) {
3568 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3569 return key;
3573 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3574 key = string_term_tdb_data(keystr);
3576 return key;
3579 /*********************************************************************
3580 ********************************************************************/
3582 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3583 size_t num_domains,
3584 unsigned char **buf )
3586 unsigned char *buffer = NULL;
3587 int len = 0;
3588 int buflen = 0;
3589 int i = 0;
3591 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3592 (int)num_domains));
3594 buflen = 0;
3596 again:
3597 len = 0;
3599 /* Store the number of array items first */
3600 len += tdb_pack( buffer+len, buflen-len, "d",
3601 num_domains );
3603 /* now pack each domain trust record */
3604 for ( i=0; i<num_domains; i++ ) {
3606 fstring tmp;
3608 if ( buflen > 0 ) {
3609 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3610 domains[i].domain_name,
3611 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3614 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3615 domains[i].domain_name,
3616 domains[i].dns_name,
3617 sid_to_fstring(tmp, &domains[i].sid),
3618 domains[i].trust_flags,
3619 domains[i].trust_attribs,
3620 domains[i].trust_type );
3623 if ( buflen < len ) {
3624 SAFE_FREE(buffer);
3625 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3626 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3627 buflen = -1;
3628 goto done;
3630 buflen = len;
3631 goto again;
3634 *buf = buffer;
3636 done:
3637 return buflen;
3640 /*********************************************************************
3641 ********************************************************************/
3643 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3644 struct winbindd_tdc_domain **domains )
3646 fstring domain_name, dns_name, sid_string;
3647 uint32 type, attribs, flags;
3648 int num_domains;
3649 int len = 0;
3650 int i;
3651 struct winbindd_tdc_domain *list = NULL;
3653 /* get the number of domains */
3654 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3655 if ( len == -1 ) {
3656 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3657 return 0;
3660 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3661 if ( !list ) {
3662 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3663 return 0;
3666 for ( i=0; i<num_domains; i++ ) {
3667 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3668 domain_name,
3669 dns_name,
3670 sid_string,
3671 &flags,
3672 &attribs,
3673 &type );
3675 if ( len == -1 ) {
3676 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3677 TALLOC_FREE( list );
3678 return 0;
3681 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3682 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3683 domain_name, dns_name, sid_string,
3684 flags, attribs, type));
3686 list[i].domain_name = talloc_strdup( list, domain_name );
3687 list[i].dns_name = talloc_strdup( list, dns_name );
3688 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3689 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3690 domain_name));
3692 list[i].trust_flags = flags;
3693 list[i].trust_attribs = attribs;
3694 list[i].trust_type = type;
3697 *domains = list;
3699 return num_domains;
3702 /*********************************************************************
3703 ********************************************************************/
3705 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3707 TDB_DATA key = make_tdc_key( lp_workgroup() );
3708 TDB_DATA data = { NULL, 0 };
3709 int ret;
3711 if ( !key.dptr )
3712 return false;
3714 /* See if we were asked to delete the cache entry */
3716 if ( !domains ) {
3717 ret = tdb_delete( wcache->tdb, key );
3718 goto done;
3721 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3723 if ( !data.dptr ) {
3724 ret = -1;
3725 goto done;
3728 ret = tdb_store( wcache->tdb, key, data, 0 );
3730 done:
3731 SAFE_FREE( data.dptr );
3732 SAFE_FREE( key.dptr );
3734 return ( ret != -1 );
3737 /*********************************************************************
3738 ********************************************************************/
3740 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3742 TDB_DATA key = make_tdc_key( lp_workgroup() );
3743 TDB_DATA data = { NULL, 0 };
3745 *domains = NULL;
3746 *num_domains = 0;
3748 if ( !key.dptr )
3749 return false;
3751 data = tdb_fetch( wcache->tdb, key );
3753 SAFE_FREE( key.dptr );
3755 if ( !data.dptr )
3756 return false;
3758 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3760 SAFE_FREE( data.dptr );
3762 if ( !*domains )
3763 return false;
3765 return true;
3768 /*********************************************************************
3769 ********************************************************************/
3771 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3773 struct winbindd_tdc_domain *dom_list = NULL;
3774 size_t num_domains = 0;
3775 bool ret = false;
3777 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3778 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3779 domain->name, domain->alt_name,
3780 sid_string_dbg(&domain->sid),
3781 domain->domain_flags,
3782 domain->domain_trust_attribs,
3783 domain->domain_type));
3785 if ( !init_wcache() ) {
3786 return false;
3789 /* fetch the list */
3791 wcache_tdc_fetch_list( &dom_list, &num_domains );
3793 /* add the new domain */
3795 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3796 goto done;
3799 /* pack the domain */
3801 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3802 goto done;
3805 /* Success */
3807 ret = true;
3808 done:
3809 TALLOC_FREE( dom_list );
3811 return ret;
3814 /*********************************************************************
3815 ********************************************************************/
3817 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3819 struct winbindd_tdc_domain *dom_list = NULL;
3820 size_t num_domains = 0;
3821 int i;
3822 struct winbindd_tdc_domain *d = NULL;
3824 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3826 if ( !init_wcache() ) {
3827 return false;
3830 /* fetch the list */
3832 wcache_tdc_fetch_list( &dom_list, &num_domains );
3834 for ( i=0; i<num_domains; i++ ) {
3835 if ( strequal(name, dom_list[i].domain_name) ||
3836 strequal(name, dom_list[i].dns_name) )
3838 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3839 name));
3841 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3842 if ( !d )
3843 break;
3845 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3846 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3847 sid_copy( &d->sid, &dom_list[i].sid );
3848 d->trust_flags = dom_list[i].trust_flags;
3849 d->trust_type = dom_list[i].trust_type;
3850 d->trust_attribs = dom_list[i].trust_attribs;
3852 break;
3856 TALLOC_FREE( dom_list );
3858 return d;
3862 /*********************************************************************
3863 ********************************************************************/
3865 void wcache_tdc_clear( void )
3867 if ( !init_wcache() )
3868 return;
3870 wcache_tdc_store_list( NULL, 0 );
3872 return;
3876 /*********************************************************************
3877 ********************************************************************/
3879 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3880 NTSTATUS status,
3881 const DOM_SID *user_sid,
3882 const char *homedir,
3883 const char *shell,
3884 const char *gecos,
3885 uint32 gid)
3887 struct cache_entry *centry;
3888 fstring tmp;
3890 if ( (centry = centry_start(domain, status)) == NULL )
3891 return;
3893 centry_put_string( centry, homedir );
3894 centry_put_string( centry, shell );
3895 centry_put_string( centry, gecos );
3896 centry_put_uint32( centry, gid );
3898 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
3900 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
3902 centry_free(centry);
3905 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3906 const DOM_SID *user_sid,
3907 TALLOC_CTX *ctx,
3908 ADS_STRUCT *ads, LDAPMessage *msg,
3909 char **homedir, char **shell, char **gecos,
3910 gid_t *p_gid)
3912 struct winbind_cache *cache = get_cache(domain);
3913 struct cache_entry *centry = NULL;
3914 NTSTATUS nt_status;
3915 fstring tmp;
3917 if (!cache->tdb)
3918 goto do_query;
3920 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
3921 sid_to_fstring(tmp, user_sid));
3923 if (!centry)
3924 goto do_query;
3926 *homedir = centry_string( centry, ctx );
3927 *shell = centry_string( centry, ctx );
3928 *gecos = centry_string( centry, ctx );
3929 *p_gid = centry_uint32( centry );
3931 centry_free(centry);
3933 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3934 sid_string_dbg(user_sid)));
3936 return NT_STATUS_OK;
3938 do_query:
3940 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3941 homedir, shell, gecos, p_gid );
3943 if ( NT_STATUS_IS_OK(nt_status) ) {
3944 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3945 *homedir, *shell, *gecos, *p_gid );
3948 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3949 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3950 domain->name ));
3951 set_domain_offline( domain );
3954 return nt_status;
3958 /* the cache backend methods are exposed via this structure */
3959 struct winbindd_methods cache_methods = {
3960 true,
3961 query_user_list,
3962 enum_dom_groups,
3963 enum_local_groups,
3964 name_to_sid,
3965 sid_to_name,
3966 rids_to_names,
3967 query_user,
3968 lookup_usergroups,
3969 lookup_useraliases,
3970 lookup_groupmem,
3971 sequence_number,
3972 lockout_policy,
3973 password_policy,
3974 trusted_domains