Prevent winbindd from segfaulting due to corrupted cache tdb.
[Samba/nascimento.git] / source3 / winbindd / winbindd_cache.c
blob62a68aa8aafd38b497ca20b8512a29a25d2be3d4
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
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
47 static const char *non_centry_keys[] = {
48 "SEQNUM/",
49 "DR/",
50 "DE/",
51 "WINBINDD_OFFLINE",
52 WINBINDD_CACHE_VERSION_KEYSTR,
53 NULL
56 /************************************************************************
57 Is this key a non-centry type ?
58 ************************************************************************/
60 static bool is_non_centry_key(TDB_DATA kbuf)
62 int i;
64 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
65 return False;
67 for (i = 0; non_centry_keys[i] != NULL; i++) {
68 size_t namelen = strlen(non_centry_keys[i]);
69 if (kbuf.dsize < namelen) {
70 continue;
72 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
73 return True;
76 return False;
79 /* Global online/offline state - False when online. winbindd starts up online
80 and sets this to true if the first query fails and there's an entry in
81 the cache tdb telling us to stay offline. */
83 static bool global_winbindd_offline_state;
85 struct winbind_cache {
86 TDB_CONTEXT *tdb;
89 struct cache_entry {
90 NTSTATUS status;
91 uint32 sequence_number;
92 uint8 *data;
93 uint32 len, ofs;
96 void (*smb_panic_fn)(const char *const why) = smb_panic;
98 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100 static struct winbind_cache *wcache;
102 void winbindd_check_cache_size(time_t t)
104 static time_t last_check_time;
105 struct stat st;
107 if (last_check_time == (time_t)0)
108 last_check_time = t;
110 if (t - last_check_time < 60 && t - last_check_time > 0)
111 return;
113 if (wcache == NULL || wcache->tdb == NULL) {
114 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
115 return;
118 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
119 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
120 return;
123 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
124 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
125 (unsigned long)st.st_size,
126 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
127 wcache_flush_cache();
131 /* get the winbind_cache structure */
132 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
134 struct winbind_cache *ret = wcache;
136 /* We have to know what type of domain we are dealing with first. */
138 if ( !domain->initialized ) {
139 init_dc_connection( domain );
143 OK. listen up becasue I'm only going to say this once.
144 We have the following scenarios to consider
145 (a) trusted AD domains on a Samba DC,
146 (b) trusted AD domains and we are joined to a non-kerberos domain
147 (c) trusted AD domains and we are joined to a kerberos (AD) domain
149 For (a) we can always contact the trusted domain using krb5
150 since we have the domain trust account password
152 For (b) we can only use RPC since we have no way of
153 getting a krb5 ticket in our own domain
155 For (c) we can always use krb5 since we have a kerberos trust
157 --jerry
160 if (!domain->backend) {
161 #ifdef HAVE_ADS
162 struct winbindd_domain *our_domain = domain;
164 /* find our domain first so we can figure out if we
165 are joined to a kerberized domain */
167 if ( !domain->primary )
168 our_domain = find_our_domain();
170 if ((our_domain->active_directory || IS_DC)
171 && domain->active_directory
172 && !lp_winbind_rpc_only()) {
173 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
174 domain->backend = &ads_methods;
175 } else {
176 #endif /* HAVE_ADS */
177 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
178 domain->backend = &reconnect_methods;
179 #ifdef HAVE_ADS
181 #endif /* HAVE_ADS */
184 if (ret)
185 return ret;
187 ret = SMB_XMALLOC_P(struct winbind_cache);
188 ZERO_STRUCTP(ret);
190 wcache = ret;
191 wcache_flush_cache();
193 return ret;
197 free a centry structure
199 static void centry_free(struct cache_entry *centry)
201 if (!centry)
202 return;
203 SAFE_FREE(centry->data);
204 free(centry);
207 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
209 if (centry->len - centry->ofs < nbytes) {
210 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
211 (unsigned int)nbytes,
212 centry->len - centry->ofs));
213 return False;
215 return True;
219 pull a uint32 from a cache entry
221 static uint32 centry_uint32(struct cache_entry *centry)
223 uint32 ret;
225 if (!centry_check_bytes(centry, 4)) {
226 smb_panic_fn("centry_uint32");
228 ret = IVAL(centry->data, centry->ofs);
229 centry->ofs += 4;
230 return ret;
234 pull a uint16 from a cache entry
236 static uint16 centry_uint16(struct cache_entry *centry)
238 uint16 ret;
239 if (!centry_check_bytes(centry, 2)) {
240 smb_panic_fn("centry_uint16");
242 ret = CVAL(centry->data, centry->ofs);
243 centry->ofs += 2;
244 return ret;
248 pull a uint8 from a cache entry
250 static uint8 centry_uint8(struct cache_entry *centry)
252 uint8 ret;
253 if (!centry_check_bytes(centry, 1)) {
254 smb_panic_fn("centry_uint8");
256 ret = CVAL(centry->data, centry->ofs);
257 centry->ofs += 1;
258 return ret;
262 pull a NTTIME from a cache entry
264 static NTTIME centry_nttime(struct cache_entry *centry)
266 NTTIME ret;
267 if (!centry_check_bytes(centry, 8)) {
268 smb_panic_fn("centry_nttime");
270 ret = IVAL(centry->data, centry->ofs);
271 centry->ofs += 4;
272 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
273 centry->ofs += 4;
274 return ret;
278 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
280 static time_t centry_time(struct cache_entry *centry)
282 return (time_t)centry_nttime(centry);
285 /* pull a string from a cache entry, using the supplied
286 talloc context
288 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
290 uint32 len;
291 char *ret;
293 len = centry_uint8(centry);
295 if (len == 0xFF) {
296 /* a deliberate NULL string */
297 return NULL;
300 if (!centry_check_bytes(centry, (size_t)len)) {
301 smb_panic_fn("centry_string");
304 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
305 if (!ret) {
306 smb_panic_fn("centry_string out of memory\n");
308 memcpy(ret,centry->data + centry->ofs, len);
309 ret[len] = 0;
310 centry->ofs += len;
311 return ret;
314 /* pull a hash16 from a cache entry, using the supplied
315 talloc context
317 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
319 uint32 len;
320 char *ret;
322 len = centry_uint8(centry);
324 if (len != 16) {
325 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
326 len ));
327 return NULL;
330 if (!centry_check_bytes(centry, 16)) {
331 return NULL;
334 ret = TALLOC_ARRAY(mem_ctx, char, 16);
335 if (!ret) {
336 smb_panic_fn("centry_hash out of memory\n");
338 memcpy(ret,centry->data + centry->ofs, 16);
339 centry->ofs += 16;
340 return ret;
343 /* pull a sid from a cache entry, using the supplied
344 talloc context
346 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
348 char *sid_string;
349 sid_string = centry_string(centry, mem_ctx);
350 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
351 return False;
353 return True;
358 pull a NTSTATUS from a cache entry
360 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
362 NTSTATUS status;
364 status = NT_STATUS(centry_uint32(centry));
365 return status;
369 /* the server is considered down if it can't give us a sequence number */
370 static bool wcache_server_down(struct winbindd_domain *domain)
372 bool ret;
374 if (!wcache->tdb)
375 return False;
377 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
379 if (ret)
380 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
381 domain->name ));
382 return ret;
385 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
387 TDB_DATA data;
388 fstring key;
389 uint32 time_diff;
391 if (!wcache->tdb) {
392 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
393 return NT_STATUS_UNSUCCESSFUL;
396 fstr_sprintf( key, "SEQNUM/%s", domain->name );
398 data = tdb_fetch_bystring( wcache->tdb, key );
399 if ( !data.dptr || data.dsize!=8 ) {
400 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
401 return NT_STATUS_UNSUCCESSFUL;
404 domain->sequence_number = IVAL(data.dptr, 0);
405 domain->last_seq_check = IVAL(data.dptr, 4);
407 SAFE_FREE(data.dptr);
409 /* have we expired? */
411 time_diff = now - domain->last_seq_check;
412 if ( time_diff > lp_winbind_cache_time() ) {
413 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
414 domain->name, domain->sequence_number,
415 (uint32)domain->last_seq_check));
416 return NT_STATUS_UNSUCCESSFUL;
419 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
420 domain->name, domain->sequence_number,
421 (uint32)domain->last_seq_check));
423 return NT_STATUS_OK;
426 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
428 TDB_DATA data;
429 fstring key_str;
430 uint8 buf[8];
432 if (!wcache->tdb) {
433 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
434 return NT_STATUS_UNSUCCESSFUL;
437 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
439 SIVAL(buf, 0, domain->sequence_number);
440 SIVAL(buf, 4, domain->last_seq_check);
441 data.dptr = buf;
442 data.dsize = 8;
444 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
445 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
446 return NT_STATUS_UNSUCCESSFUL;
449 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
450 domain->name, domain->sequence_number,
451 (uint32)domain->last_seq_check));
453 return NT_STATUS_OK;
457 refresh the domain sequence number. If force is True
458 then always refresh it, no matter how recently we fetched it
461 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
463 NTSTATUS status;
464 unsigned time_diff;
465 time_t t = time(NULL);
466 unsigned cache_time = lp_winbind_cache_time();
468 if ( IS_DOMAIN_OFFLINE(domain) ) {
469 return;
472 get_cache( domain );
474 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
475 /* trying to reconnect is expensive, don't do it too often */
476 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
477 cache_time *= 8;
479 #endif
481 time_diff = t - domain->last_seq_check;
483 /* see if we have to refetch the domain sequence number */
484 if (!force && (time_diff < cache_time)) {
485 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
486 goto done;
489 /* try to get the sequence number from the tdb cache first */
490 /* this will update the timestamp as well */
492 status = fetch_cache_seqnum( domain, t );
493 if ( NT_STATUS_IS_OK(status) )
494 goto done;
496 /* important! make sure that we know if this is a native
497 mode domain or not. And that we can contact it. */
499 if ( winbindd_can_contact_domain( domain ) ) {
500 status = domain->backend->sequence_number(domain,
501 &domain->sequence_number);
502 } else {
503 /* just use the current time */
504 status = NT_STATUS_OK;
505 domain->sequence_number = time(NULL);
509 /* the above call could have set our domain->backend to NULL when
510 * coming from offline to online mode, make sure to reinitialize the
511 * backend - Guenther */
512 get_cache( domain );
514 if (!NT_STATUS_IS_OK(status)) {
515 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
516 domain->sequence_number = DOM_SEQUENCE_NONE;
519 domain->last_status = status;
520 domain->last_seq_check = time(NULL);
522 /* save the new sequence number ni the cache */
523 store_cache_seqnum( domain );
525 done:
526 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
527 domain->name, domain->sequence_number));
529 return;
533 decide if a cache entry has expired
535 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
537 /* If we've been told to be offline - stay in that state... */
538 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
539 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
540 keystr, domain->name ));
541 return False;
544 /* when the domain is offline return the cached entry.
545 * This deals with transient offline states... */
547 if (!domain->online) {
548 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
549 keystr, domain->name ));
550 return False;
553 /* if the server is OK and our cache entry came from when it was down then
554 the entry is invalid */
555 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
556 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
557 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
558 keystr, domain->name ));
559 return True;
562 /* if the server is down or the cache entry is not older than the
563 current sequence number then it is OK */
564 if (wcache_server_down(domain) ||
565 centry->sequence_number == domain->sequence_number) {
566 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
567 keystr, domain->name ));
568 return False;
571 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
572 keystr, domain->name ));
574 /* it's expired */
575 return True;
578 static struct cache_entry *wcache_fetch_raw(char *kstr)
580 TDB_DATA data;
581 struct cache_entry *centry;
582 TDB_DATA key;
584 key = string_tdb_data(kstr);
585 data = tdb_fetch(wcache->tdb, key);
586 if (!data.dptr) {
587 /* a cache miss */
588 return NULL;
591 centry = SMB_XMALLOC_P(struct cache_entry);
592 centry->data = (unsigned char *)data.dptr;
593 centry->len = data.dsize;
594 centry->ofs = 0;
596 if (centry->len < 8) {
597 /* huh? corrupt cache? */
598 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
599 centry_free(centry);
600 return NULL;
603 centry->status = centry_ntstatus(centry);
604 centry->sequence_number = centry_uint32(centry);
606 return centry;
610 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
611 number and return status
613 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
614 struct winbindd_domain *domain,
615 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
616 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
617 struct winbindd_domain *domain,
618 const char *format, ...)
620 va_list ap;
621 char *kstr;
622 struct cache_entry *centry;
624 if (opt_nocache) {
625 return NULL;
628 refresh_sequence_number(domain, False);
630 va_start(ap, format);
631 smb_xvasprintf(&kstr, format, ap);
632 va_end(ap);
634 centry = wcache_fetch_raw(kstr);
635 if (centry == NULL) {
636 free(kstr);
637 return NULL;
640 if (centry_expired(domain, kstr, centry)) {
642 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
643 kstr, domain->name ));
645 centry_free(centry);
646 free(kstr);
647 return NULL;
650 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
651 kstr, domain->name ));
653 free(kstr);
654 return centry;
657 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
658 static void wcache_delete(const char *format, ...)
660 va_list ap;
661 char *kstr;
662 TDB_DATA key;
664 va_start(ap, format);
665 smb_xvasprintf(&kstr, format, ap);
666 va_end(ap);
668 key = string_tdb_data(kstr);
670 tdb_delete(wcache->tdb, key);
671 free(kstr);
675 make sure we have at least len bytes available in a centry
677 static void centry_expand(struct cache_entry *centry, uint32 len)
679 if (centry->len - centry->ofs >= len)
680 return;
681 centry->len *= 2;
682 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
683 centry->len);
684 if (!centry->data) {
685 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
686 smb_panic_fn("out of memory in centry_expand");
691 push a uint32 into a centry
693 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
695 centry_expand(centry, 4);
696 SIVAL(centry->data, centry->ofs, v);
697 centry->ofs += 4;
701 push a uint16 into a centry
703 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
705 centry_expand(centry, 2);
706 SIVAL(centry->data, centry->ofs, v);
707 centry->ofs += 2;
711 push a uint8 into a centry
713 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
715 centry_expand(centry, 1);
716 SCVAL(centry->data, centry->ofs, v);
717 centry->ofs += 1;
721 push a string into a centry
723 static void centry_put_string(struct cache_entry *centry, const char *s)
725 int len;
727 if (!s) {
728 /* null strings are marked as len 0xFFFF */
729 centry_put_uint8(centry, 0xFF);
730 return;
733 len = strlen(s);
734 /* can't handle more than 254 char strings. Truncating is probably best */
735 if (len > 254) {
736 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
737 len = 254;
739 centry_put_uint8(centry, len);
740 centry_expand(centry, len);
741 memcpy(centry->data + centry->ofs, s, len);
742 centry->ofs += len;
746 push a 16 byte hash into a centry - treat as 16 byte string.
748 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
750 centry_put_uint8(centry, 16);
751 centry_expand(centry, 16);
752 memcpy(centry->data + centry->ofs, val, 16);
753 centry->ofs += 16;
756 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
758 fstring sid_string;
759 centry_put_string(centry, sid_to_fstring(sid_string, sid));
764 put NTSTATUS into a centry
766 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
768 uint32 status_value = NT_STATUS_V(status);
769 centry_put_uint32(centry, status_value);
774 push a NTTIME into a centry
776 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
778 centry_expand(centry, 8);
779 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
780 centry->ofs += 4;
781 SIVAL(centry->data, centry->ofs, nt >> 32);
782 centry->ofs += 4;
786 push a time_t into a centry - use a 64 bit size.
787 NTTIME here is being used as a convenient 64-bit size.
789 static void centry_put_time(struct cache_entry *centry, time_t t)
791 NTTIME nt = (NTTIME)t;
792 centry_put_nttime(centry, nt);
796 start a centry for output. When finished, call centry_end()
798 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
800 struct cache_entry *centry;
802 if (!wcache->tdb)
803 return NULL;
805 centry = SMB_XMALLOC_P(struct cache_entry);
807 centry->len = 8192; /* reasonable default */
808 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
809 centry->ofs = 0;
810 centry->sequence_number = domain->sequence_number;
811 centry_put_ntstatus(centry, status);
812 centry_put_uint32(centry, centry->sequence_number);
813 return centry;
817 finish a centry and write it to the tdb
819 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
820 static void centry_end(struct cache_entry *centry, const char *format, ...)
822 va_list ap;
823 char *kstr;
824 TDB_DATA key, data;
826 if (opt_nocache) {
827 return;
830 va_start(ap, format);
831 smb_xvasprintf(&kstr, format, ap);
832 va_end(ap);
834 key = string_tdb_data(kstr);
835 data.dptr = centry->data;
836 data.dsize = centry->ofs;
838 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
839 free(kstr);
842 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
843 NTSTATUS status, const char *domain_name,
844 const char *name, const DOM_SID *sid,
845 enum lsa_SidType type)
847 struct cache_entry *centry;
848 fstring uname;
850 centry = centry_start(domain, status);
851 if (!centry)
852 return;
853 centry_put_uint32(centry, type);
854 centry_put_sid(centry, sid);
855 fstrcpy(uname, name);
856 strupper_m(uname);
857 centry_end(centry, "NS/%s/%s", domain_name, uname);
858 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
859 uname, sid_string_dbg(sid), nt_errstr(status)));
860 centry_free(centry);
863 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
864 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
866 struct cache_entry *centry;
867 fstring sid_string;
869 centry = centry_start(domain, status);
870 if (!centry)
871 return;
873 if (NT_STATUS_IS_OK(status)) {
874 centry_put_uint32(centry, type);
875 centry_put_string(centry, domain_name);
876 centry_put_string(centry, name);
879 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
880 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
881 name, nt_errstr(status)));
882 centry_free(centry);
886 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
888 struct cache_entry *centry;
889 fstring sid_string;
891 if (is_null_sid(&info->user_sid)) {
892 return;
895 centry = centry_start(domain, status);
896 if (!centry)
897 return;
898 centry_put_string(centry, info->acct_name);
899 centry_put_string(centry, info->full_name);
900 centry_put_string(centry, info->homedir);
901 centry_put_string(centry, info->shell);
902 centry_put_uint32(centry, info->primary_gid);
903 centry_put_sid(centry, &info->user_sid);
904 centry_put_sid(centry, &info->group_sid);
905 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
906 &info->user_sid));
907 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
908 centry_free(centry);
911 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
913 struct cache_entry *centry;
915 centry = centry_start(domain, status);
916 if (!centry)
917 return;
919 centry_put_nttime(centry, lockout_policy->duration);
920 centry_put_nttime(centry, lockout_policy->reset_count);
921 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
923 centry_end(centry, "LOC_POL/%s", domain->name);
925 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
927 centry_free(centry);
930 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
932 struct cache_entry *centry;
934 centry = centry_start(domain, status);
935 if (!centry)
936 return;
938 centry_put_uint16(centry, policy->min_length_password);
939 centry_put_uint16(centry, policy->password_history);
940 centry_put_uint32(centry, policy->password_properties);
941 centry_put_nttime(centry, policy->expire);
942 centry_put_nttime(centry, policy->min_passwordage);
944 centry_end(centry, "PWD_POL/%s", domain->name);
946 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
948 centry_free(centry);
951 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
953 struct winbind_cache *cache = get_cache(domain);
954 TDB_DATA data;
955 fstring key_str, tmp;
956 uint32 rid;
958 if (!cache->tdb) {
959 return NT_STATUS_INTERNAL_DB_ERROR;
962 if (is_null_sid(sid)) {
963 return NT_STATUS_INVALID_SID;
966 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
967 return NT_STATUS_INVALID_SID;
970 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
972 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
973 if (!data.dptr) {
974 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
977 SAFE_FREE(data.dptr);
978 return NT_STATUS_OK;
981 /* Lookup creds for a SID - copes with old (unsalted) creds as well
982 as new salted ones. */
984 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
985 TALLOC_CTX *mem_ctx,
986 const DOM_SID *sid,
987 const uint8 **cached_nt_pass,
988 const uint8 **cached_salt)
990 struct winbind_cache *cache = get_cache(domain);
991 struct cache_entry *centry = NULL;
992 NTSTATUS status;
993 time_t t;
994 uint32 rid;
995 fstring tmp;
997 if (!cache->tdb) {
998 return NT_STATUS_INTERNAL_DB_ERROR;
1001 if (is_null_sid(sid)) {
1002 return NT_STATUS_INVALID_SID;
1005 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1006 return NT_STATUS_INVALID_SID;
1009 /* Try and get a salted cred first. If we can't
1010 fall back to an unsalted cred. */
1012 centry = wcache_fetch(cache, domain, "CRED/%s",
1013 sid_to_fstring(tmp, sid));
1014 if (!centry) {
1015 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1016 sid_string_dbg(sid)));
1017 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1020 t = centry_time(centry);
1022 /* In the salted case this isn't actually the nt_hash itself,
1023 but the MD5 of the salt + nt_hash. Let the caller
1024 sort this out. It can tell as we only return the cached_salt
1025 if we are returning a salted cred. */
1027 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1028 if (*cached_nt_pass == NULL) {
1029 fstring sidstr;
1031 sid_to_fstring(sidstr, sid);
1033 /* Bad (old) cred cache. Delete and pretend we
1034 don't have it. */
1035 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1036 sidstr));
1037 wcache_delete("CRED/%s", sidstr);
1038 centry_free(centry);
1039 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1042 /* We only have 17 bytes more data in the salted cred case. */
1043 if (centry->len - centry->ofs == 17) {
1044 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1045 } else {
1046 *cached_salt = NULL;
1049 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1050 if (*cached_salt) {
1051 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1054 status = centry->status;
1056 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1057 sid_string_dbg(sid), nt_errstr(status) ));
1059 centry_free(centry);
1060 return status;
1063 /* Store creds for a SID - only writes out new salted ones. */
1065 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1066 TALLOC_CTX *mem_ctx,
1067 const DOM_SID *sid,
1068 const uint8 nt_pass[NT_HASH_LEN])
1070 struct cache_entry *centry;
1071 fstring sid_string;
1072 uint32 rid;
1073 uint8 cred_salt[NT_HASH_LEN];
1074 uint8 salted_hash[NT_HASH_LEN];
1076 if (is_null_sid(sid)) {
1077 return NT_STATUS_INVALID_SID;
1080 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1081 return NT_STATUS_INVALID_SID;
1084 centry = centry_start(domain, NT_STATUS_OK);
1085 if (!centry) {
1086 return NT_STATUS_INTERNAL_DB_ERROR;
1089 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1091 centry_put_time(centry, time(NULL));
1093 /* Create a salt and then salt the hash. */
1094 generate_random_buffer(cred_salt, NT_HASH_LEN);
1095 E_md5hash(cred_salt, nt_pass, salted_hash);
1097 centry_put_hash16(centry, salted_hash);
1098 centry_put_hash16(centry, cred_salt);
1099 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1101 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1103 centry_free(centry);
1105 return NT_STATUS_OK;
1109 /* Query display info. This is the basic user list fn */
1110 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1111 TALLOC_CTX *mem_ctx,
1112 uint32 *num_entries,
1113 WINBIND_USERINFO **info)
1115 struct winbind_cache *cache = get_cache(domain);
1116 struct cache_entry *centry = NULL;
1117 NTSTATUS status;
1118 unsigned int i, retry;
1120 if (!cache->tdb)
1121 goto do_query;
1123 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1124 if (!centry)
1125 goto do_query;
1127 *num_entries = centry_uint32(centry);
1129 if (*num_entries == 0)
1130 goto do_cached;
1132 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1133 if (! (*info)) {
1134 smb_panic_fn("query_user_list out of memory");
1136 for (i=0; i<(*num_entries); i++) {
1137 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1138 (*info)[i].full_name = centry_string(centry, mem_ctx);
1139 (*info)[i].homedir = centry_string(centry, mem_ctx);
1140 (*info)[i].shell = centry_string(centry, mem_ctx);
1141 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1142 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1145 do_cached:
1146 status = centry->status;
1148 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1149 domain->name, nt_errstr(status) ));
1151 centry_free(centry);
1152 return status;
1154 do_query:
1155 *num_entries = 0;
1156 *info = NULL;
1158 /* Return status value returned by seq number check */
1160 if (!NT_STATUS_IS_OK(domain->last_status))
1161 return domain->last_status;
1163 /* Put the query_user_list() in a retry loop. There appears to be
1164 * some bug either with Windows 2000 or Samba's handling of large
1165 * rpc replies. This manifests itself as sudden disconnection
1166 * at a random point in the enumeration of a large (60k) user list.
1167 * The retry loop simply tries the operation again. )-: It's not
1168 * pretty but an acceptable workaround until we work out what the
1169 * real problem is. */
1171 retry = 0;
1172 do {
1174 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1175 domain->name ));
1177 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1178 if (!NT_STATUS_IS_OK(status)) {
1179 DEBUG(3, ("query_user_list: returned 0x%08x, "
1180 "retrying\n", NT_STATUS_V(status)));
1182 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1183 DEBUG(3, ("query_user_list: flushing "
1184 "connection cache\n"));
1185 invalidate_cm_connection(&domain->conn);
1188 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1189 (retry++ < 5));
1191 /* and save it */
1192 refresh_sequence_number(domain, False);
1193 centry = centry_start(domain, status);
1194 if (!centry)
1195 goto skip_save;
1196 centry_put_uint32(centry, *num_entries);
1197 for (i=0; i<(*num_entries); i++) {
1198 centry_put_string(centry, (*info)[i].acct_name);
1199 centry_put_string(centry, (*info)[i].full_name);
1200 centry_put_string(centry, (*info)[i].homedir);
1201 centry_put_string(centry, (*info)[i].shell);
1202 centry_put_sid(centry, &(*info)[i].user_sid);
1203 centry_put_sid(centry, &(*info)[i].group_sid);
1204 if (domain->backend && domain->backend->consistent) {
1205 /* when the backend is consistent we can pre-prime some mappings */
1206 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1207 domain->name,
1208 (*info)[i].acct_name,
1209 &(*info)[i].user_sid,
1210 SID_NAME_USER);
1211 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1212 &(*info)[i].user_sid,
1213 domain->name,
1214 (*info)[i].acct_name,
1215 SID_NAME_USER);
1216 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1219 centry_end(centry, "UL/%s", domain->name);
1220 centry_free(centry);
1222 skip_save:
1223 return status;
1226 /* list all domain groups */
1227 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1228 TALLOC_CTX *mem_ctx,
1229 uint32 *num_entries,
1230 struct acct_info **info)
1232 struct winbind_cache *cache = get_cache(domain);
1233 struct cache_entry *centry = NULL;
1234 NTSTATUS status;
1235 unsigned int i;
1237 if (!cache->tdb)
1238 goto do_query;
1240 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1241 if (!centry)
1242 goto do_query;
1244 *num_entries = centry_uint32(centry);
1246 if (*num_entries == 0)
1247 goto do_cached;
1249 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1250 if (! (*info)) {
1251 smb_panic_fn("enum_dom_groups out of memory");
1253 for (i=0; i<(*num_entries); i++) {
1254 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1255 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1256 (*info)[i].rid = centry_uint32(centry);
1259 do_cached:
1260 status = centry->status;
1262 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1263 domain->name, nt_errstr(status) ));
1265 centry_free(centry);
1266 return status;
1268 do_query:
1269 *num_entries = 0;
1270 *info = NULL;
1272 /* Return status value returned by seq number check */
1274 if (!NT_STATUS_IS_OK(domain->last_status))
1275 return domain->last_status;
1277 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1278 domain->name ));
1280 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1282 /* and save it */
1283 refresh_sequence_number(domain, False);
1284 centry = centry_start(domain, status);
1285 if (!centry)
1286 goto skip_save;
1287 centry_put_uint32(centry, *num_entries);
1288 for (i=0; i<(*num_entries); i++) {
1289 centry_put_string(centry, (*info)[i].acct_name);
1290 centry_put_string(centry, (*info)[i].acct_desc);
1291 centry_put_uint32(centry, (*info)[i].rid);
1293 centry_end(centry, "GL/%s/domain", domain->name);
1294 centry_free(centry);
1296 skip_save:
1297 return status;
1300 /* list all domain groups */
1301 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1302 TALLOC_CTX *mem_ctx,
1303 uint32 *num_entries,
1304 struct acct_info **info)
1306 struct winbind_cache *cache = get_cache(domain);
1307 struct cache_entry *centry = NULL;
1308 NTSTATUS status;
1309 unsigned int i;
1311 if (!cache->tdb)
1312 goto do_query;
1314 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1315 if (!centry)
1316 goto do_query;
1318 *num_entries = centry_uint32(centry);
1320 if (*num_entries == 0)
1321 goto do_cached;
1323 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1324 if (! (*info)) {
1325 smb_panic_fn("enum_dom_groups out of memory");
1327 for (i=0; i<(*num_entries); i++) {
1328 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1329 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1330 (*info)[i].rid = centry_uint32(centry);
1333 do_cached:
1335 /* If we are returning cached data and the domain controller
1336 is down then we don't know whether the data is up to date
1337 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1338 indicate this. */
1340 if (wcache_server_down(domain)) {
1341 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1342 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1343 } else
1344 status = centry->status;
1346 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1347 domain->name, nt_errstr(status) ));
1349 centry_free(centry);
1350 return status;
1352 do_query:
1353 *num_entries = 0;
1354 *info = NULL;
1356 /* Return status value returned by seq number check */
1358 if (!NT_STATUS_IS_OK(domain->last_status))
1359 return domain->last_status;
1361 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1362 domain->name ));
1364 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1366 /* and save it */
1367 refresh_sequence_number(domain, False);
1368 centry = centry_start(domain, status);
1369 if (!centry)
1370 goto skip_save;
1371 centry_put_uint32(centry, *num_entries);
1372 for (i=0; i<(*num_entries); i++) {
1373 centry_put_string(centry, (*info)[i].acct_name);
1374 centry_put_string(centry, (*info)[i].acct_desc);
1375 centry_put_uint32(centry, (*info)[i].rid);
1377 centry_end(centry, "GL/%s/local", domain->name);
1378 centry_free(centry);
1380 skip_save:
1381 return status;
1384 /* convert a single name to a sid in a domain */
1385 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1386 TALLOC_CTX *mem_ctx,
1387 enum winbindd_cmd orig_cmd,
1388 const char *domain_name,
1389 const char *name,
1390 DOM_SID *sid,
1391 enum lsa_SidType *type)
1393 struct winbind_cache *cache = get_cache(domain);
1394 struct cache_entry *centry = NULL;
1395 NTSTATUS status;
1396 fstring uname;
1398 if (!cache->tdb)
1399 goto do_query;
1401 fstrcpy(uname, name);
1402 strupper_m(uname);
1403 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1404 if (!centry)
1405 goto do_query;
1407 status = centry->status;
1408 if (NT_STATUS_IS_OK(status)) {
1409 *type = (enum lsa_SidType)centry_uint32(centry);
1410 centry_sid(centry, mem_ctx, sid);
1413 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1414 domain->name, nt_errstr(status) ));
1416 centry_free(centry);
1417 return status;
1419 do_query:
1420 ZERO_STRUCTP(sid);
1422 /* If the seq number check indicated that there is a problem
1423 * with this DC, then return that status... except for
1424 * access_denied. This is special because the dc may be in
1425 * "restrict anonymous = 1" mode, in which case it will deny
1426 * most unauthenticated operations, but *will* allow the LSA
1427 * name-to-sid that we try as a fallback. */
1429 if (!(NT_STATUS_IS_OK(domain->last_status)
1430 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1431 return domain->last_status;
1433 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1434 domain->name ));
1436 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1437 domain_name, name, sid, type);
1439 /* and save it */
1440 refresh_sequence_number(domain, False);
1442 if (domain->online &&
1443 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1444 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1446 /* Only save the reverse mapping if this was not a UPN */
1447 if (!strchr(name, '@')) {
1448 strupper_m(CONST_DISCARD(char *,domain_name));
1449 strlower_m(CONST_DISCARD(char *,name));
1450 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1454 return status;
1457 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1458 given */
1459 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1460 TALLOC_CTX *mem_ctx,
1461 const DOM_SID *sid,
1462 char **domain_name,
1463 char **name,
1464 enum lsa_SidType *type)
1466 struct winbind_cache *cache = get_cache(domain);
1467 struct cache_entry *centry = NULL;
1468 NTSTATUS status;
1469 fstring sid_string;
1471 if (!cache->tdb)
1472 goto do_query;
1474 centry = wcache_fetch(cache, domain, "SN/%s",
1475 sid_to_fstring(sid_string, sid));
1476 if (!centry)
1477 goto do_query;
1479 status = centry->status;
1480 if (NT_STATUS_IS_OK(status)) {
1481 *type = (enum lsa_SidType)centry_uint32(centry);
1482 *domain_name = centry_string(centry, mem_ctx);
1483 *name = centry_string(centry, mem_ctx);
1486 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1487 domain->name, nt_errstr(status) ));
1489 centry_free(centry);
1490 return status;
1492 do_query:
1493 *name = NULL;
1494 *domain_name = NULL;
1496 /* If the seq number check indicated that there is a problem
1497 * with this DC, then return that status... except for
1498 * access_denied. This is special because the dc may be in
1499 * "restrict anonymous = 1" mode, in which case it will deny
1500 * most unauthenticated operations, but *will* allow the LSA
1501 * sid-to-name that we try as a fallback. */
1503 if (!(NT_STATUS_IS_OK(domain->last_status)
1504 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1505 return domain->last_status;
1507 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1508 domain->name ));
1510 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1512 /* and save it */
1513 refresh_sequence_number(domain, False);
1514 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1516 /* We can't save the name to sid mapping here, as with sid history a
1517 * later name2sid would give the wrong sid. */
1519 return status;
1522 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1523 TALLOC_CTX *mem_ctx,
1524 const DOM_SID *domain_sid,
1525 uint32 *rids,
1526 size_t num_rids,
1527 char **domain_name,
1528 char ***names,
1529 enum lsa_SidType **types)
1531 struct winbind_cache *cache = get_cache(domain);
1532 size_t i;
1533 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1534 bool have_mapped;
1535 bool have_unmapped;
1537 *domain_name = NULL;
1538 *names = NULL;
1539 *types = NULL;
1541 if (!cache->tdb) {
1542 goto do_query;
1545 if (num_rids == 0) {
1546 return NT_STATUS_OK;
1549 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1550 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1552 if ((*names == NULL) || (*types == NULL)) {
1553 result = NT_STATUS_NO_MEMORY;
1554 goto error;
1557 have_mapped = have_unmapped = False;
1559 for (i=0; i<num_rids; i++) {
1560 DOM_SID sid;
1561 struct cache_entry *centry;
1562 fstring tmp;
1564 if (!sid_compose(&sid, domain_sid, rids[i])) {
1565 result = NT_STATUS_INTERNAL_ERROR;
1566 goto error;
1569 centry = wcache_fetch(cache, domain, "SN/%s",
1570 sid_to_fstring(tmp, &sid));
1571 if (!centry) {
1572 goto do_query;
1575 (*types)[i] = SID_NAME_UNKNOWN;
1576 (*names)[i] = talloc_strdup(*names, "");
1578 if (NT_STATUS_IS_OK(centry->status)) {
1579 char *dom;
1580 have_mapped = True;
1581 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1583 dom = centry_string(centry, mem_ctx);
1584 if (*domain_name == NULL) {
1585 *domain_name = dom;
1586 } else {
1587 talloc_free(dom);
1590 (*names)[i] = centry_string(centry, *names);
1592 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1593 have_unmapped = True;
1595 } else {
1596 /* something's definitely wrong */
1597 result = centry->status;
1598 goto error;
1601 centry_free(centry);
1604 if (!have_mapped) {
1605 return NT_STATUS_NONE_MAPPED;
1607 if (!have_unmapped) {
1608 return NT_STATUS_OK;
1610 return STATUS_SOME_UNMAPPED;
1612 do_query:
1614 TALLOC_FREE(*names);
1615 TALLOC_FREE(*types);
1617 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1618 rids, num_rids, domain_name,
1619 names, types);
1622 None of the queried rids has been found so save all negative entries
1624 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1625 for (i = 0; i < num_rids; i++) {
1626 DOM_SID sid;
1627 const char *name = "";
1628 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1629 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1631 if (!sid_compose(&sid, domain_sid, rids[i])) {
1632 return NT_STATUS_INTERNAL_ERROR;
1635 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1636 name, type);
1639 return result;
1643 Some or all of the queried rids have been found.
1645 if (!NT_STATUS_IS_OK(result) &&
1646 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1647 return result;
1650 refresh_sequence_number(domain, False);
1652 for (i=0; i<num_rids; i++) {
1653 DOM_SID sid;
1654 NTSTATUS status;
1656 if (!sid_compose(&sid, domain_sid, rids[i])) {
1657 result = NT_STATUS_INTERNAL_ERROR;
1658 goto error;
1661 status = (*types)[i] == SID_NAME_UNKNOWN ?
1662 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1664 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1665 (*names)[i], (*types)[i]);
1668 return result;
1670 error:
1672 TALLOC_FREE(*names);
1673 TALLOC_FREE(*types);
1674 return result;
1677 /* Lookup user information from a rid */
1678 static NTSTATUS query_user(struct winbindd_domain *domain,
1679 TALLOC_CTX *mem_ctx,
1680 const DOM_SID *user_sid,
1681 WINBIND_USERINFO *info)
1683 struct winbind_cache *cache = get_cache(domain);
1684 struct cache_entry *centry = NULL;
1685 NTSTATUS status;
1686 fstring tmp;
1688 if (!cache->tdb)
1689 goto do_query;
1691 centry = wcache_fetch(cache, domain, "U/%s",
1692 sid_to_fstring(tmp, user_sid));
1694 /* If we have an access denied cache entry and a cached info3 in the
1695 samlogon cache then do a query. This will force the rpc back end
1696 to return the info3 data. */
1698 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1699 netsamlogon_cache_have(user_sid)) {
1700 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1701 domain->last_status = NT_STATUS_OK;
1702 centry_free(centry);
1703 goto do_query;
1706 if (!centry)
1707 goto do_query;
1709 /* if status is not ok then this is a negative hit
1710 and the rest of the data doesn't matter */
1711 status = centry->status;
1712 if (NT_STATUS_IS_OK(status)) {
1713 info->acct_name = centry_string(centry, mem_ctx);
1714 info->full_name = centry_string(centry, mem_ctx);
1715 info->homedir = centry_string(centry, mem_ctx);
1716 info->shell = centry_string(centry, mem_ctx);
1717 info->primary_gid = centry_uint32(centry);
1718 centry_sid(centry, mem_ctx, &info->user_sid);
1719 centry_sid(centry, mem_ctx, &info->group_sid);
1722 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1723 domain->name, nt_errstr(status) ));
1725 centry_free(centry);
1726 return status;
1728 do_query:
1729 ZERO_STRUCTP(info);
1731 /* Return status value returned by seq number check */
1733 if (!NT_STATUS_IS_OK(domain->last_status))
1734 return domain->last_status;
1736 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1737 domain->name ));
1739 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1741 /* and save it */
1742 refresh_sequence_number(domain, False);
1743 wcache_save_user(domain, status, info);
1745 return status;
1749 /* Lookup groups a user is a member of. */
1750 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1751 TALLOC_CTX *mem_ctx,
1752 const DOM_SID *user_sid,
1753 uint32 *num_groups, DOM_SID **user_gids)
1755 struct winbind_cache *cache = get_cache(domain);
1756 struct cache_entry *centry = NULL;
1757 NTSTATUS status;
1758 unsigned int i;
1759 fstring sid_string;
1761 if (!cache->tdb)
1762 goto do_query;
1764 centry = wcache_fetch(cache, domain, "UG/%s",
1765 sid_to_fstring(sid_string, user_sid));
1767 /* If we have an access denied cache entry and a cached info3 in the
1768 samlogon cache then do a query. This will force the rpc back end
1769 to return the info3 data. */
1771 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1772 netsamlogon_cache_have(user_sid)) {
1773 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1774 domain->last_status = NT_STATUS_OK;
1775 centry_free(centry);
1776 goto do_query;
1779 if (!centry)
1780 goto do_query;
1782 *num_groups = centry_uint32(centry);
1784 if (*num_groups == 0)
1785 goto do_cached;
1787 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1788 if (! (*user_gids)) {
1789 smb_panic_fn("lookup_usergroups out of memory");
1791 for (i=0; i<(*num_groups); i++) {
1792 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1795 do_cached:
1796 status = centry->status;
1798 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1799 domain->name, nt_errstr(status) ));
1801 centry_free(centry);
1802 return status;
1804 do_query:
1805 (*num_groups) = 0;
1806 (*user_gids) = NULL;
1808 /* Return status value returned by seq number check */
1810 if (!NT_STATUS_IS_OK(domain->last_status))
1811 return domain->last_status;
1813 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1814 domain->name ));
1816 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1818 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1819 goto skip_save;
1821 /* and save it */
1822 refresh_sequence_number(domain, False);
1823 centry = centry_start(domain, status);
1824 if (!centry)
1825 goto skip_save;
1827 centry_put_uint32(centry, *num_groups);
1828 for (i=0; i<(*num_groups); i++) {
1829 centry_put_sid(centry, &(*user_gids)[i]);
1832 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
1833 centry_free(centry);
1835 skip_save:
1836 return status;
1839 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1840 TALLOC_CTX *mem_ctx,
1841 uint32 num_sids, const DOM_SID *sids,
1842 uint32 *num_aliases, uint32 **alias_rids)
1844 struct winbind_cache *cache = get_cache(domain);
1845 struct cache_entry *centry = NULL;
1846 NTSTATUS status;
1847 char *sidlist = talloc_strdup(mem_ctx, "");
1848 int i;
1850 if (!cache->tdb)
1851 goto do_query;
1853 if (num_sids == 0) {
1854 *num_aliases = 0;
1855 *alias_rids = NULL;
1856 return NT_STATUS_OK;
1859 /* We need to cache indexed by the whole list of SIDs, the aliases
1860 * resulting might come from any of the SIDs. */
1862 for (i=0; i<num_sids; i++) {
1863 fstring tmp;
1864 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1865 sid_to_fstring(tmp, &sids[i]));
1866 if (sidlist == NULL)
1867 return NT_STATUS_NO_MEMORY;
1870 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1872 if (!centry)
1873 goto do_query;
1875 *num_aliases = centry_uint32(centry);
1876 *alias_rids = NULL;
1878 if (*num_aliases) {
1879 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1881 if ((*alias_rids) == NULL) {
1882 centry_free(centry);
1883 return NT_STATUS_NO_MEMORY;
1885 } else {
1886 (*alias_rids) = NULL;
1889 for (i=0; i<(*num_aliases); i++)
1890 (*alias_rids)[i] = centry_uint32(centry);
1892 status = centry->status;
1894 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1895 "status %s\n", domain->name, nt_errstr(status)));
1897 centry_free(centry);
1898 return status;
1900 do_query:
1901 (*num_aliases) = 0;
1902 (*alias_rids) = NULL;
1904 if (!NT_STATUS_IS_OK(domain->last_status))
1905 return domain->last_status;
1907 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1908 "for domain %s\n", domain->name ));
1910 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1911 num_sids, sids,
1912 num_aliases, alias_rids);
1914 /* and save it */
1915 refresh_sequence_number(domain, False);
1916 centry = centry_start(domain, status);
1917 if (!centry)
1918 goto skip_save;
1919 centry_put_uint32(centry, *num_aliases);
1920 for (i=0; i<(*num_aliases); i++)
1921 centry_put_uint32(centry, (*alias_rids)[i]);
1922 centry_end(centry, "UA%s", sidlist);
1923 centry_free(centry);
1925 skip_save:
1926 return status;
1930 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1931 TALLOC_CTX *mem_ctx,
1932 const DOM_SID *group_sid, uint32 *num_names,
1933 DOM_SID **sid_mem, char ***names,
1934 uint32 **name_types)
1936 struct winbind_cache *cache = get_cache(domain);
1937 struct cache_entry *centry = NULL;
1938 NTSTATUS status;
1939 unsigned int i;
1940 fstring sid_string;
1942 if (!cache->tdb)
1943 goto do_query;
1945 centry = wcache_fetch(cache, domain, "GM/%s",
1946 sid_to_fstring(sid_string, group_sid));
1947 if (!centry)
1948 goto do_query;
1950 *num_names = centry_uint32(centry);
1952 if (*num_names == 0)
1953 goto do_cached;
1955 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1956 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1957 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1959 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1960 smb_panic_fn("lookup_groupmem out of memory");
1963 for (i=0; i<(*num_names); i++) {
1964 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1965 (*names)[i] = centry_string(centry, mem_ctx);
1966 (*name_types)[i] = centry_uint32(centry);
1969 do_cached:
1970 status = centry->status;
1972 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1973 domain->name, nt_errstr(status)));
1975 centry_free(centry);
1976 return status;
1978 do_query:
1979 (*num_names) = 0;
1980 (*sid_mem) = NULL;
1981 (*names) = NULL;
1982 (*name_types) = NULL;
1984 /* Return status value returned by seq number check */
1986 if (!NT_STATUS_IS_OK(domain->last_status))
1987 return domain->last_status;
1989 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1990 domain->name ));
1992 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1993 sid_mem, names, name_types);
1995 /* and save it */
1996 refresh_sequence_number(domain, False);
1997 centry = centry_start(domain, status);
1998 if (!centry)
1999 goto skip_save;
2000 centry_put_uint32(centry, *num_names);
2001 for (i=0; i<(*num_names); i++) {
2002 centry_put_sid(centry, &(*sid_mem)[i]);
2003 centry_put_string(centry, (*names)[i]);
2004 centry_put_uint32(centry, (*name_types)[i]);
2006 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2007 centry_free(centry);
2009 skip_save:
2010 return status;
2013 /* find the sequence number for a domain */
2014 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2016 refresh_sequence_number(domain, False);
2018 *seq = domain->sequence_number;
2020 return NT_STATUS_OK;
2023 /* enumerate trusted domains
2024 * (we need to have the list of trustdoms in the cache when we go offline) -
2025 * Guenther */
2026 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2027 TALLOC_CTX *mem_ctx,
2028 uint32 *num_domains,
2029 char ***names,
2030 char ***alt_names,
2031 DOM_SID **dom_sids)
2033 struct winbind_cache *cache = get_cache(domain);
2034 struct cache_entry *centry = NULL;
2035 NTSTATUS status;
2036 int i;
2038 if (!cache->tdb)
2039 goto do_query;
2041 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2043 if (!centry) {
2044 goto do_query;
2047 *num_domains = centry_uint32(centry);
2049 if (*num_domains) {
2050 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2051 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2052 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2054 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2055 smb_panic_fn("trusted_domains out of memory");
2057 } else {
2058 (*names) = NULL;
2059 (*alt_names) = NULL;
2060 (*dom_sids) = NULL;
2063 for (i=0; i<(*num_domains); i++) {
2064 (*names)[i] = centry_string(centry, mem_ctx);
2065 (*alt_names)[i] = centry_string(centry, mem_ctx);
2066 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
2069 status = centry->status;
2071 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2072 domain->name, *num_domains, nt_errstr(status) ));
2074 centry_free(centry);
2075 return status;
2077 do_query:
2078 (*num_domains) = 0;
2079 (*dom_sids) = NULL;
2080 (*names) = NULL;
2081 (*alt_names) = NULL;
2083 /* Return status value returned by seq number check */
2085 if (!NT_STATUS_IS_OK(domain->last_status))
2086 return domain->last_status;
2088 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2089 domain->name ));
2091 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2092 names, alt_names, dom_sids);
2094 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2095 * so that the generic centry handling still applies correctly -
2096 * Guenther*/
2098 if (!NT_STATUS_IS_ERR(status)) {
2099 status = NT_STATUS_OK;
2103 #if 0 /* Disabled as we want the trust dom list to be managed by
2104 the main parent and always to make the query. --jerry */
2106 /* and save it */
2107 refresh_sequence_number(domain, False);
2109 centry = centry_start(domain, status);
2110 if (!centry)
2111 goto skip_save;
2113 centry_put_uint32(centry, *num_domains);
2115 for (i=0; i<(*num_domains); i++) {
2116 centry_put_string(centry, (*names)[i]);
2117 centry_put_string(centry, (*alt_names)[i]);
2118 centry_put_sid(centry, &(*dom_sids)[i]);
2121 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2123 centry_free(centry);
2125 skip_save:
2126 #endif
2128 return status;
2131 /* get lockout policy */
2132 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2133 TALLOC_CTX *mem_ctx,
2134 SAM_UNK_INFO_12 *policy){
2135 struct winbind_cache *cache = get_cache(domain);
2136 struct cache_entry *centry = NULL;
2137 NTSTATUS status;
2139 if (!cache->tdb)
2140 goto do_query;
2142 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2144 if (!centry)
2145 goto do_query;
2147 policy->duration = centry_nttime(centry);
2148 policy->reset_count = centry_nttime(centry);
2149 policy->bad_attempt_lockout = centry_uint16(centry);
2151 status = centry->status;
2153 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2154 domain->name, nt_errstr(status) ));
2156 centry_free(centry);
2157 return status;
2159 do_query:
2160 ZERO_STRUCTP(policy);
2162 /* Return status value returned by seq number check */
2164 if (!NT_STATUS_IS_OK(domain->last_status))
2165 return domain->last_status;
2167 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2168 domain->name ));
2170 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2172 /* and save it */
2173 refresh_sequence_number(domain, False);
2174 wcache_save_lockout_policy(domain, status, policy);
2176 return status;
2179 /* get password policy */
2180 static NTSTATUS password_policy(struct winbindd_domain *domain,
2181 TALLOC_CTX *mem_ctx,
2182 SAM_UNK_INFO_1 *policy)
2184 struct winbind_cache *cache = get_cache(domain);
2185 struct cache_entry *centry = NULL;
2186 NTSTATUS status;
2188 if (!cache->tdb)
2189 goto do_query;
2191 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2193 if (!centry)
2194 goto do_query;
2196 policy->min_length_password = centry_uint16(centry);
2197 policy->password_history = centry_uint16(centry);
2198 policy->password_properties = centry_uint32(centry);
2199 policy->expire = centry_nttime(centry);
2200 policy->min_passwordage = centry_nttime(centry);
2202 status = centry->status;
2204 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2205 domain->name, nt_errstr(status) ));
2207 centry_free(centry);
2208 return status;
2210 do_query:
2211 ZERO_STRUCTP(policy);
2213 /* Return status value returned by seq number check */
2215 if (!NT_STATUS_IS_OK(domain->last_status))
2216 return domain->last_status;
2218 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2219 domain->name ));
2221 status = domain->backend->password_policy(domain, mem_ctx, policy);
2223 /* and save it */
2224 refresh_sequence_number(domain, False);
2225 wcache_save_password_policy(domain, status, policy);
2227 return status;
2231 /* Invalidate cached user and group lists coherently */
2233 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2234 void *state)
2236 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2237 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2238 tdb_delete(the_tdb, kbuf);
2240 return 0;
2243 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2245 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2246 NET_USER_INFO_3 *info3)
2248 struct winbind_cache *cache;
2250 /* dont clear cached U/SID and UG/SID entries when we want to logon
2251 * offline - gd */
2253 if (lp_winbind_offline_logon()) {
2254 return;
2257 if (!domain)
2258 return;
2260 cache = get_cache(domain);
2261 netsamlogon_clear_cached_user(cache->tdb, info3);
2264 int wcache_invalidate_cache(void)
2266 struct winbindd_domain *domain;
2268 for (domain = domain_list(); domain; domain = domain->next) {
2269 struct winbind_cache *cache = get_cache(domain);
2271 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2272 "entries for %s\n", domain->name));
2273 if (cache) {
2274 if (cache->tdb) {
2275 tdb_traverse(cache->tdb, traverse_fn, NULL);
2276 } else {
2277 return -1;
2281 return 0;
2284 bool init_wcache(void)
2286 if (wcache == NULL) {
2287 wcache = SMB_XMALLOC_P(struct winbind_cache);
2288 ZERO_STRUCTP(wcache);
2291 if (wcache->tdb != NULL)
2292 return True;
2294 /* when working offline we must not clear the cache on restart */
2295 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2296 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2297 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2298 O_RDWR|O_CREAT, 0600);
2300 if (wcache->tdb == NULL) {
2301 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2302 return False;
2305 return True;
2308 /************************************************************************
2309 This is called by the parent to initialize the cache file.
2310 We don't need sophisticated locking here as we know we're the
2311 only opener.
2312 ************************************************************************/
2314 bool initialize_winbindd_cache(void)
2316 bool cache_bad = True;
2317 uint32 vers;
2319 if (!init_wcache()) {
2320 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2321 return False;
2324 /* Check version number. */
2325 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2326 vers == WINBINDD_CACHE_VERSION) {
2327 cache_bad = False;
2330 if (cache_bad) {
2331 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2332 "and re-creating with version number %d\n",
2333 WINBINDD_CACHE_VERSION ));
2335 tdb_close(wcache->tdb);
2336 wcache->tdb = NULL;
2338 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2339 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2340 lock_path("winbindd_cache.tdb"),
2341 strerror(errno) ));
2342 return False;
2344 if (!init_wcache()) {
2345 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2346 "init_wcache failed.\n"));
2347 return False;
2350 /* Write the version. */
2351 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2352 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2353 tdb_errorstr(wcache->tdb) ));
2354 return False;
2358 tdb_close(wcache->tdb);
2359 wcache->tdb = NULL;
2360 return True;
2363 void close_winbindd_cache()
2365 if (!wcache)
2366 return;
2367 if (wcache->tdb) {
2368 tdb_close(wcache->tdb);
2369 wcache->tdb = NULL;
2373 void cache_store_response(pid_t pid, struct winbindd_response *response)
2375 fstring key_str;
2377 if (!init_wcache())
2378 return;
2380 DEBUG(10, ("Storing response for pid %d, len %d\n",
2381 pid, response->length));
2383 fstr_sprintf(key_str, "DR/%d", pid);
2384 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2385 make_tdb_data((uint8 *)response, sizeof(*response)),
2386 TDB_REPLACE) == -1)
2387 return;
2389 if (response->length == sizeof(*response))
2390 return;
2392 /* There's extra data */
2394 DEBUG(10, ("Storing extra data: len=%d\n",
2395 (int)(response->length - sizeof(*response))));
2397 fstr_sprintf(key_str, "DE/%d", pid);
2398 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2399 make_tdb_data((uint8 *)response->extra_data.data,
2400 response->length - sizeof(*response)),
2401 TDB_REPLACE) == 0)
2402 return;
2404 /* We could not store the extra data, make sure the tdb does not
2405 * contain a main record with wrong dangling extra data */
2407 fstr_sprintf(key_str, "DR/%d", pid);
2408 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2410 return;
2413 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2415 TDB_DATA data;
2416 fstring key_str;
2418 if (!init_wcache())
2419 return False;
2421 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2423 fstr_sprintf(key_str, "DR/%d", pid);
2424 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2426 if (data.dptr == NULL)
2427 return False;
2429 if (data.dsize != sizeof(*response))
2430 return False;
2432 memcpy(response, data.dptr, data.dsize);
2433 SAFE_FREE(data.dptr);
2435 if (response->length == sizeof(*response)) {
2436 response->extra_data.data = NULL;
2437 return True;
2440 /* There's extra data */
2442 DEBUG(10, ("Retrieving extra data length=%d\n",
2443 (int)(response->length - sizeof(*response))));
2445 fstr_sprintf(key_str, "DE/%d", pid);
2446 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2448 if (data.dptr == NULL) {
2449 DEBUG(0, ("Did not find extra data\n"));
2450 return False;
2453 if (data.dsize != (response->length - sizeof(*response))) {
2454 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2455 SAFE_FREE(data.dptr);
2456 return False;
2459 dump_data(11, (uint8 *)data.dptr, data.dsize);
2461 response->extra_data.data = data.dptr;
2462 return True;
2465 void cache_cleanup_response(pid_t pid)
2467 fstring key_str;
2469 if (!init_wcache())
2470 return;
2472 fstr_sprintf(key_str, "DR/%d", pid);
2473 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2475 fstr_sprintf(key_str, "DE/%d", pid);
2476 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2478 return;
2482 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2483 char **domain_name, char **name,
2484 enum lsa_SidType *type)
2486 struct winbindd_domain *domain;
2487 struct winbind_cache *cache;
2488 struct cache_entry *centry = NULL;
2489 NTSTATUS status;
2490 fstring tmp;
2492 domain = find_lookup_domain_from_sid(sid);
2493 if (domain == NULL) {
2494 return False;
2497 cache = get_cache(domain);
2499 if (cache->tdb == NULL) {
2500 return False;
2503 centry = wcache_fetch(cache, domain, "SN/%s",
2504 sid_to_fstring(tmp, sid));
2505 if (centry == NULL) {
2506 return False;
2509 if (NT_STATUS_IS_OK(centry->status)) {
2510 *type = (enum lsa_SidType)centry_uint32(centry);
2511 *domain_name = centry_string(centry, mem_ctx);
2512 *name = centry_string(centry, mem_ctx);
2515 status = centry->status;
2516 centry_free(centry);
2517 return NT_STATUS_IS_OK(status);
2520 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2521 const char *domain_name,
2522 const char *name,
2523 DOM_SID *sid,
2524 enum lsa_SidType *type)
2526 struct winbindd_domain *domain;
2527 struct winbind_cache *cache;
2528 struct cache_entry *centry = NULL;
2529 NTSTATUS status;
2530 fstring uname;
2531 bool original_online_state;
2533 domain = find_lookup_domain_from_name(domain_name);
2534 if (domain == NULL) {
2535 return False;
2538 cache = get_cache(domain);
2540 if (cache->tdb == NULL) {
2541 return False;
2544 fstrcpy(uname, name);
2545 strupper_m(uname);
2547 /* If we are doing a cached logon, temporarily set the domain
2548 offline so the cache won't expire the entry */
2550 original_online_state = domain->online;
2551 domain->online = False;
2552 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2553 domain->online = original_online_state;
2555 if (centry == NULL) {
2556 return False;
2559 if (NT_STATUS_IS_OK(centry->status)) {
2560 *type = (enum lsa_SidType)centry_uint32(centry);
2561 centry_sid(centry, mem_ctx, sid);
2564 status = centry->status;
2565 centry_free(centry);
2567 return NT_STATUS_IS_OK(status);
2570 void cache_name2sid(struct winbindd_domain *domain,
2571 const char *domain_name, const char *name,
2572 enum lsa_SidType type, const DOM_SID *sid)
2574 refresh_sequence_number(domain, False);
2575 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2576 sid, type);
2580 * The original idea that this cache only contains centries has
2581 * been blurred - now other stuff gets put in here. Ensure we
2582 * ignore these things on cleanup.
2585 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2586 TDB_DATA dbuf, void *state)
2588 struct cache_entry *centry;
2590 if (is_non_centry_key(kbuf)) {
2591 return 0;
2594 centry = wcache_fetch_raw((char *)kbuf.dptr);
2595 if (!centry) {
2596 return 0;
2599 if (!NT_STATUS_IS_OK(centry->status)) {
2600 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2601 tdb_delete(the_tdb, kbuf);
2604 centry_free(centry);
2605 return 0;
2608 /* flush the cache */
2609 void wcache_flush_cache(void)
2611 if (!wcache)
2612 return;
2613 if (wcache->tdb) {
2614 tdb_close(wcache->tdb);
2615 wcache->tdb = NULL;
2617 if (opt_nocache)
2618 return;
2620 /* when working offline we must not clear the cache on restart */
2621 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2622 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2623 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2624 O_RDWR|O_CREAT, 0600);
2626 if (!wcache->tdb) {
2627 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2628 return;
2631 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2633 DEBUG(10,("wcache_flush_cache success\n"));
2636 /* Count cached creds */
2638 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2639 void *state)
2641 int *cred_count = (int*)state;
2643 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2644 (*cred_count)++;
2646 return 0;
2649 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2651 struct winbind_cache *cache = get_cache(domain);
2653 *count = 0;
2655 if (!cache->tdb) {
2656 return NT_STATUS_INTERNAL_DB_ERROR;
2659 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2661 return NT_STATUS_OK;
2664 struct cred_list {
2665 struct cred_list *prev, *next;
2666 TDB_DATA key;
2667 fstring name;
2668 time_t created;
2670 static struct cred_list *wcache_cred_list;
2672 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2673 void *state)
2675 struct cred_list *cred;
2677 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2679 cred = SMB_MALLOC_P(struct cred_list);
2680 if (cred == NULL) {
2681 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2682 return -1;
2685 ZERO_STRUCTP(cred);
2687 /* save a copy of the key */
2689 fstrcpy(cred->name, (const char *)kbuf.dptr);
2690 DLIST_ADD(wcache_cred_list, cred);
2693 return 0;
2696 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2698 struct winbind_cache *cache = get_cache(domain);
2699 NTSTATUS status;
2700 int ret;
2701 struct cred_list *cred, *oldest = NULL;
2703 if (!cache->tdb) {
2704 return NT_STATUS_INTERNAL_DB_ERROR;
2707 /* we possibly already have an entry */
2708 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2710 fstring key_str, tmp;
2712 DEBUG(11,("we already have an entry, deleting that\n"));
2714 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2716 tdb_delete(cache->tdb, string_tdb_data(key_str));
2718 return NT_STATUS_OK;
2721 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2722 if (ret == 0) {
2723 return NT_STATUS_OK;
2724 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2725 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2728 ZERO_STRUCTP(oldest);
2730 for (cred = wcache_cred_list; cred; cred = cred->next) {
2732 TDB_DATA data;
2733 time_t t;
2735 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2736 if (!data.dptr) {
2737 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2738 cred->name));
2739 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2740 goto done;
2743 t = IVAL(data.dptr, 0);
2744 SAFE_FREE(data.dptr);
2746 if (!oldest) {
2747 oldest = SMB_MALLOC_P(struct cred_list);
2748 if (oldest == NULL) {
2749 status = NT_STATUS_NO_MEMORY;
2750 goto done;
2753 fstrcpy(oldest->name, cred->name);
2754 oldest->created = t;
2755 continue;
2758 if (t < oldest->created) {
2759 fstrcpy(oldest->name, cred->name);
2760 oldest->created = t;
2764 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2765 status = NT_STATUS_OK;
2766 } else {
2767 status = NT_STATUS_UNSUCCESSFUL;
2769 done:
2770 SAFE_FREE(wcache_cred_list);
2771 SAFE_FREE(oldest);
2773 return status;
2776 /* Change the global online/offline state. */
2777 bool set_global_winbindd_state_offline(void)
2779 TDB_DATA data;
2781 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2783 /* Only go offline if someone has created
2784 the key "WINBINDD_OFFLINE" in the cache tdb. */
2786 if (wcache == NULL || wcache->tdb == NULL) {
2787 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2788 return False;
2791 if (!lp_winbind_offline_logon()) {
2792 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2793 return False;
2796 if (global_winbindd_offline_state) {
2797 /* Already offline. */
2798 return True;
2801 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2803 if (!data.dptr || data.dsize != 4) {
2804 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2805 SAFE_FREE(data.dptr);
2806 return False;
2807 } else {
2808 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2809 global_winbindd_offline_state = True;
2810 SAFE_FREE(data.dptr);
2811 return True;
2815 void set_global_winbindd_state_online(void)
2817 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2819 if (!lp_winbind_offline_logon()) {
2820 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2821 return;
2824 if (!global_winbindd_offline_state) {
2825 /* Already online. */
2826 return;
2828 global_winbindd_offline_state = False;
2830 if (!wcache->tdb) {
2831 return;
2834 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2835 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2838 bool get_global_winbindd_state_offline(void)
2840 return global_winbindd_offline_state;
2843 /***********************************************************************
2844 Validate functions for all possible cache tdb keys.
2845 ***********************************************************************/
2847 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2848 struct tdb_validation_status *state)
2850 struct cache_entry *centry;
2852 centry = SMB_XMALLOC_P(struct cache_entry);
2853 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2854 if (!centry->data) {
2855 SAFE_FREE(centry);
2856 return NULL;
2858 centry->len = data.dsize;
2859 centry->ofs = 0;
2861 if (centry->len < 8) {
2862 /* huh? corrupt cache? */
2863 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2864 centry_free(centry);
2865 state->bad_entry = True;
2866 state->success = False;
2867 return NULL;
2870 centry->status = NT_STATUS(centry_uint32(centry));
2871 centry->sequence_number = centry_uint32(centry);
2872 return centry;
2875 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2876 struct tdb_validation_status *state)
2878 if (dbuf.dsize != 8) {
2879 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2880 keystr, (unsigned int)dbuf.dsize ));
2881 state->bad_entry = True;
2882 return 1;
2884 return 0;
2887 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2888 struct tdb_validation_status *state)
2890 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2891 if (!centry) {
2892 return 1;
2895 (void)centry_uint32(centry);
2896 if (NT_STATUS_IS_OK(centry->status)) {
2897 DOM_SID sid;
2898 (void)centry_sid(centry, mem_ctx, &sid);
2901 centry_free(centry);
2903 if (!(state->success)) {
2904 return 1;
2906 DEBUG(10,("validate_ns: %s ok\n", keystr));
2907 return 0;
2910 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2911 struct tdb_validation_status *state)
2913 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2914 if (!centry) {
2915 return 1;
2918 if (NT_STATUS_IS_OK(centry->status)) {
2919 (void)centry_uint32(centry);
2920 (void)centry_string(centry, mem_ctx);
2921 (void)centry_string(centry, mem_ctx);
2924 centry_free(centry);
2926 if (!(state->success)) {
2927 return 1;
2929 DEBUG(10,("validate_sn: %s ok\n", keystr));
2930 return 0;
2933 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2934 struct tdb_validation_status *state)
2936 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2937 DOM_SID sid;
2939 if (!centry) {
2940 return 1;
2943 (void)centry_string(centry, mem_ctx);
2944 (void)centry_string(centry, mem_ctx);
2945 (void)centry_string(centry, mem_ctx);
2946 (void)centry_string(centry, mem_ctx);
2947 (void)centry_uint32(centry);
2948 (void)centry_sid(centry, mem_ctx, &sid);
2949 (void)centry_sid(centry, mem_ctx, &sid);
2951 centry_free(centry);
2953 if (!(state->success)) {
2954 return 1;
2956 DEBUG(10,("validate_u: %s ok\n", keystr));
2957 return 0;
2960 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2961 struct tdb_validation_status *state)
2963 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2965 if (!centry) {
2966 return 1;
2969 (void)centry_nttime(centry);
2970 (void)centry_nttime(centry);
2971 (void)centry_uint16(centry);
2973 centry_free(centry);
2975 if (!(state->success)) {
2976 return 1;
2978 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2979 return 0;
2982 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2983 struct tdb_validation_status *state)
2985 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2987 if (!centry) {
2988 return 1;
2991 (void)centry_uint16(centry);
2992 (void)centry_uint16(centry);
2993 (void)centry_uint32(centry);
2994 (void)centry_nttime(centry);
2995 (void)centry_nttime(centry);
2997 centry_free(centry);
2999 if (!(state->success)) {
3000 return 1;
3002 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3003 return 0;
3006 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3007 struct tdb_validation_status *state)
3009 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3011 if (!centry) {
3012 return 1;
3015 (void)centry_time(centry);
3016 (void)centry_hash16(centry, mem_ctx);
3018 /* We only have 17 bytes more data in the salted cred case. */
3019 if (centry->len - centry->ofs == 17) {
3020 (void)centry_hash16(centry, mem_ctx);
3023 centry_free(centry);
3025 if (!(state->success)) {
3026 return 1;
3028 DEBUG(10,("validate_cred: %s ok\n", keystr));
3029 return 0;
3032 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3033 struct tdb_validation_status *state)
3035 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3036 int32 num_entries, i;
3038 if (!centry) {
3039 return 1;
3042 num_entries = (int32)centry_uint32(centry);
3044 for (i=0; i< num_entries; i++) {
3045 DOM_SID sid;
3046 (void)centry_string(centry, mem_ctx);
3047 (void)centry_string(centry, mem_ctx);
3048 (void)centry_string(centry, mem_ctx);
3049 (void)centry_string(centry, mem_ctx);
3050 (void)centry_sid(centry, mem_ctx, &sid);
3051 (void)centry_sid(centry, mem_ctx, &sid);
3054 centry_free(centry);
3056 if (!(state->success)) {
3057 return 1;
3059 DEBUG(10,("validate_ul: %s ok\n", keystr));
3060 return 0;
3063 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3064 struct tdb_validation_status *state)
3066 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3067 int32 num_entries, i;
3069 if (!centry) {
3070 return 1;
3073 num_entries = centry_uint32(centry);
3075 for (i=0; i< num_entries; i++) {
3076 (void)centry_string(centry, mem_ctx);
3077 (void)centry_string(centry, mem_ctx);
3078 (void)centry_uint32(centry);
3081 centry_free(centry);
3083 if (!(state->success)) {
3084 return 1;
3086 DEBUG(10,("validate_gl: %s ok\n", keystr));
3087 return 0;
3090 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3091 struct tdb_validation_status *state)
3093 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3094 int32 num_groups, i;
3096 if (!centry) {
3097 return 1;
3100 num_groups = centry_uint32(centry);
3102 for (i=0; i< num_groups; i++) {
3103 DOM_SID sid;
3104 centry_sid(centry, mem_ctx, &sid);
3107 centry_free(centry);
3109 if (!(state->success)) {
3110 return 1;
3112 DEBUG(10,("validate_ug: %s ok\n", keystr));
3113 return 0;
3116 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3117 struct tdb_validation_status *state)
3119 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3120 int32 num_aliases, i;
3122 if (!centry) {
3123 return 1;
3126 num_aliases = centry_uint32(centry);
3128 for (i=0; i < num_aliases; i++) {
3129 (void)centry_uint32(centry);
3132 centry_free(centry);
3134 if (!(state->success)) {
3135 return 1;
3137 DEBUG(10,("validate_ua: %s ok\n", keystr));
3138 return 0;
3141 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3142 struct tdb_validation_status *state)
3144 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3145 int32 num_names, i;
3147 if (!centry) {
3148 return 1;
3151 num_names = centry_uint32(centry);
3153 for (i=0; i< num_names; i++) {
3154 DOM_SID sid;
3155 centry_sid(centry, mem_ctx, &sid);
3156 (void)centry_string(centry, mem_ctx);
3157 (void)centry_uint32(centry);
3160 centry_free(centry);
3162 if (!(state->success)) {
3163 return 1;
3165 DEBUG(10,("validate_gm: %s ok\n", keystr));
3166 return 0;
3169 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3170 struct tdb_validation_status *state)
3172 /* Can't say anything about this other than must be nonzero. */
3173 if (dbuf.dsize == 0) {
3174 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3175 keystr));
3176 state->bad_entry = True;
3177 state->success = False;
3178 return 1;
3181 DEBUG(10,("validate_dr: %s ok\n", keystr));
3182 return 0;
3185 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3186 struct tdb_validation_status *state)
3188 /* Can't say anything about this other than must be nonzero. */
3189 if (dbuf.dsize == 0) {
3190 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3191 keystr));
3192 state->bad_entry = True;
3193 state->success = False;
3194 return 1;
3197 DEBUG(10,("validate_de: %s ok\n", keystr));
3198 return 0;
3201 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3202 struct tdb_validation_status *state)
3204 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3205 int32 num_domains, i;
3207 if (!centry) {
3208 return 1;
3211 num_domains = centry_uint32(centry);
3213 for (i=0; i< num_domains; i++) {
3214 DOM_SID sid;
3215 (void)centry_string(centry, mem_ctx);
3216 (void)centry_string(centry, mem_ctx);
3217 (void)centry_sid(centry, mem_ctx, &sid);
3220 centry_free(centry);
3222 if (!(state->success)) {
3223 return 1;
3225 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3226 return 0;
3229 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3230 TDB_DATA dbuf,
3231 struct tdb_validation_status *state)
3233 if (dbuf.dsize == 0) {
3234 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3235 "key %s (len ==0) ?\n", keystr));
3236 state->bad_entry = True;
3237 state->success = False;
3238 return 1;
3241 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3242 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3243 return 0;
3246 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3247 struct tdb_validation_status *state)
3249 if (dbuf.dsize != 4) {
3250 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3251 keystr, (unsigned int)dbuf.dsize ));
3252 state->bad_entry = True;
3253 state->success = False;
3254 return 1;
3256 DEBUG(10,("validate_offline: %s ok\n", keystr));
3257 return 0;
3260 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3261 struct tdb_validation_status *state)
3263 if (dbuf.dsize != 4) {
3264 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3265 "key %s (len %u != 4) ?\n",
3266 keystr, (unsigned int)dbuf.dsize));
3267 state->bad_entry = True;
3268 state->success = False;
3269 return 1;
3272 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3273 return 0;
3276 /***********************************************************************
3277 A list of all possible cache tdb keys with associated validation
3278 functions.
3279 ***********************************************************************/
3281 struct key_val_struct {
3282 const char *keyname;
3283 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3284 } key_val[] = {
3285 {"SEQNUM/", validate_seqnum},
3286 {"NS/", validate_ns},
3287 {"SN/", validate_sn},
3288 {"U/", validate_u},
3289 {"LOC_POL/", validate_loc_pol},
3290 {"PWD_POL/", validate_pwd_pol},
3291 {"CRED/", validate_cred},
3292 {"UL/", validate_ul},
3293 {"GL/", validate_gl},
3294 {"UG/", validate_ug},
3295 {"UA", validate_ua},
3296 {"GM/", validate_gm},
3297 {"DR/", validate_dr},
3298 {"DE/", validate_de},
3299 {"TRUSTDOMS/", validate_trustdoms},
3300 {"TRUSTDOMCACHE/", validate_trustdomcache},
3301 {"WINBINDD_OFFLINE", validate_offline},
3302 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3303 {NULL, NULL}
3306 /***********************************************************************
3307 Function to look at every entry in the tdb and validate it as far as
3308 possible.
3309 ***********************************************************************/
3311 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3313 int i;
3314 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3316 /* Paranoia check. */
3317 if (kbuf.dsize > 1024) {
3318 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3319 (unsigned int)kbuf.dsize ));
3320 return 1;
3323 for (i = 0; key_val[i].keyname; i++) {
3324 size_t namelen = strlen(key_val[i].keyname);
3325 if (kbuf.dsize >= namelen && (
3326 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3327 TALLOC_CTX *mem_ctx;
3328 char *keystr;
3329 int ret;
3331 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3332 if (!keystr) {
3333 return 1;
3335 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3336 keystr[kbuf.dsize] = '\0';
3338 mem_ctx = talloc_init("validate_ctx");
3339 if (!mem_ctx) {
3340 SAFE_FREE(keystr);
3341 return 1;
3344 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3345 v_state);
3347 SAFE_FREE(keystr);
3348 talloc_destroy(mem_ctx);
3349 return ret;
3353 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3354 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3355 DEBUG(0,("data :\n"));
3356 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3357 v_state->unknown_key = True;
3358 v_state->success = False;
3359 return 1; /* terminate. */
3362 static void validate_panic(const char *const why)
3364 DEBUG(0,("validating cache: would panic %s\n", why ));
3365 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3366 exit(47);
3369 /***********************************************************************
3370 Try and validate every entry in the winbindd cache. If we fail here,
3371 delete the cache tdb and return non-zero.
3372 ***********************************************************************/
3374 int winbindd_validate_cache(void)
3376 int ret = -1;
3377 const char *tdb_path = lock_path("winbindd_cache.tdb");
3378 TDB_CONTEXT *tdb = NULL;
3380 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3381 smb_panic_fn = validate_panic;
3384 tdb = tdb_open_log(tdb_path,
3385 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3386 ( lp_winbind_offline_logon()
3387 ? TDB_DEFAULT
3388 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3389 O_RDWR|O_CREAT,
3390 0600);
3391 if (!tdb) {
3392 DEBUG(0, ("winbindd_validate_cache: "
3393 "error opening/initializing tdb\n"));
3394 goto done;
3396 tdb_close(tdb);
3398 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3400 if (ret != 0) {
3401 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3402 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3403 unlink(tdb_path);
3406 done:
3407 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3408 smb_panic_fn = smb_panic;
3409 return ret;
3412 /***********************************************************************
3413 Try and validate every entry in the winbindd cache.
3414 ***********************************************************************/
3416 int winbindd_validate_cache_nobackup(void)
3418 int ret = -1;
3419 const char *tdb_path = lock_path("winbindd_cache.tdb");
3421 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3422 smb_panic_fn = validate_panic;
3425 if (wcache == NULL || wcache->tdb == NULL) {
3426 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3427 } else {
3428 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3431 if (ret != 0) {
3432 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3433 "successful.\n"));
3436 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3437 "function\n"));
3438 smb_panic_fn = smb_panic;
3439 return ret;
3442 /*********************************************************************
3443 ********************************************************************/
3445 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3446 struct winbindd_tdc_domain **domains,
3447 size_t *num_domains )
3449 struct winbindd_tdc_domain *list = NULL;
3450 size_t idx;
3451 int i;
3452 bool set_only = False;
3454 /* don't allow duplicates */
3456 idx = *num_domains;
3457 list = *domains;
3459 for ( i=0; i< (*num_domains); i++ ) {
3460 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3461 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3462 new_dom->name));
3463 idx = i;
3464 set_only = True;
3466 break;
3470 if ( !set_only ) {
3471 if ( !*domains ) {
3472 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3473 idx = 0;
3474 } else {
3475 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3476 struct winbindd_tdc_domain,
3477 (*num_domains)+1);
3478 idx = *num_domains;
3481 ZERO_STRUCT( list[idx] );
3484 if ( !list )
3485 return False;
3487 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3488 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3490 if ( !is_null_sid( &new_dom->sid ) )
3491 sid_copy( &list[idx].sid, &new_dom->sid );
3493 if ( new_dom->domain_flags != 0x0 )
3494 list[idx].trust_flags = new_dom->domain_flags;
3496 if ( new_dom->domain_type != 0x0 )
3497 list[idx].trust_type = new_dom->domain_type;
3499 if ( new_dom->domain_trust_attribs != 0x0 )
3500 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3502 if ( !set_only ) {
3503 *domains = list;
3504 *num_domains = idx + 1;
3507 return True;
3510 /*********************************************************************
3511 ********************************************************************/
3513 static TDB_DATA make_tdc_key( const char *domain_name )
3515 char *keystr = NULL;
3516 TDB_DATA key = { NULL, 0 };
3518 if ( !domain_name ) {
3519 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3520 return key;
3524 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3525 key = string_term_tdb_data(keystr);
3527 return key;
3530 /*********************************************************************
3531 ********************************************************************/
3533 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3534 size_t num_domains,
3535 unsigned char **buf )
3537 unsigned char *buffer = NULL;
3538 int len = 0;
3539 int buflen = 0;
3540 int i = 0;
3542 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3543 (int)num_domains));
3545 buflen = 0;
3547 again:
3548 len = 0;
3550 /* Store the number of array items first */
3551 len += tdb_pack( buffer+len, buflen-len, "d",
3552 num_domains );
3554 /* now pack each domain trust record */
3555 for ( i=0; i<num_domains; i++ ) {
3557 fstring tmp;
3559 if ( buflen > 0 ) {
3560 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3561 domains[i].domain_name,
3562 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3565 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3566 domains[i].domain_name,
3567 domains[i].dns_name,
3568 sid_to_fstring(tmp, &domains[i].sid),
3569 domains[i].trust_flags,
3570 domains[i].trust_attribs,
3571 domains[i].trust_type );
3574 if ( buflen < len ) {
3575 SAFE_FREE(buffer);
3576 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3577 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3578 buflen = -1;
3579 goto done;
3581 buflen = len;
3582 goto again;
3585 *buf = buffer;
3587 done:
3588 return buflen;
3591 /*********************************************************************
3592 ********************************************************************/
3594 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3595 struct winbindd_tdc_domain **domains )
3597 fstring domain_name, dns_name, sid_string;
3598 uint32 type, attribs, flags;
3599 int num_domains;
3600 int len = 0;
3601 int i;
3602 struct winbindd_tdc_domain *list = NULL;
3604 /* get the number of domains */
3605 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3606 if ( len == -1 ) {
3607 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3608 return 0;
3611 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3612 if ( !list ) {
3613 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3614 return 0;
3617 for ( i=0; i<num_domains; i++ ) {
3618 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3619 domain_name,
3620 dns_name,
3621 sid_string,
3622 &flags,
3623 &attribs,
3624 &type );
3626 if ( len == -1 ) {
3627 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3628 TALLOC_FREE( list );
3629 return 0;
3632 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3633 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3634 domain_name, dns_name, sid_string,
3635 flags, attribs, type));
3637 list[i].domain_name = talloc_strdup( list, domain_name );
3638 list[i].dns_name = talloc_strdup( list, dns_name );
3639 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3640 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3641 domain_name));
3643 list[i].trust_flags = flags;
3644 list[i].trust_attribs = attribs;
3645 list[i].trust_type = type;
3648 *domains = list;
3650 return num_domains;
3653 /*********************************************************************
3654 ********************************************************************/
3656 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3658 TDB_DATA key = make_tdc_key( lp_workgroup() );
3659 TDB_DATA data = { NULL, 0 };
3660 int ret;
3662 if ( !key.dptr )
3663 return False;
3665 /* See if we were asked to delete the cache entry */
3667 if ( !domains ) {
3668 ret = tdb_delete( wcache->tdb, key );
3669 goto done;
3672 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3674 if ( !data.dptr ) {
3675 ret = -1;
3676 goto done;
3679 ret = tdb_store( wcache->tdb, key, data, 0 );
3681 done:
3682 SAFE_FREE( data.dptr );
3683 SAFE_FREE( key.dptr );
3685 return ( ret != -1 );
3688 /*********************************************************************
3689 ********************************************************************/
3691 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3693 TDB_DATA key = make_tdc_key( lp_workgroup() );
3694 TDB_DATA data = { NULL, 0 };
3696 *domains = NULL;
3697 *num_domains = 0;
3699 if ( !key.dptr )
3700 return False;
3702 data = tdb_fetch( wcache->tdb, key );
3704 SAFE_FREE( key.dptr );
3706 if ( !data.dptr )
3707 return False;
3709 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3711 SAFE_FREE( data.dptr );
3713 if ( !*domains )
3714 return False;
3716 return True;
3719 /*********************************************************************
3720 ********************************************************************/
3722 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3724 struct winbindd_tdc_domain *dom_list = NULL;
3725 size_t num_domains = 0;
3726 bool ret = False;
3728 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3729 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3730 domain->name, domain->alt_name,
3731 sid_string_dbg(&domain->sid),
3732 domain->domain_flags,
3733 domain->domain_trust_attribs,
3734 domain->domain_type));
3736 if ( !init_wcache() ) {
3737 return False;
3740 /* fetch the list */
3742 wcache_tdc_fetch_list( &dom_list, &num_domains );
3744 /* add the new domain */
3746 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3747 goto done;
3750 /* pack the domain */
3752 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3753 goto done;
3756 /* Success */
3758 ret = True;
3759 done:
3760 TALLOC_FREE( dom_list );
3762 return ret;
3765 /*********************************************************************
3766 ********************************************************************/
3768 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3770 struct winbindd_tdc_domain *dom_list = NULL;
3771 size_t num_domains = 0;
3772 int i;
3773 struct winbindd_tdc_domain *d = NULL;
3775 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3777 if ( !init_wcache() ) {
3778 return False;
3781 /* fetch the list */
3783 wcache_tdc_fetch_list( &dom_list, &num_domains );
3785 for ( i=0; i<num_domains; i++ ) {
3786 if ( strequal(name, dom_list[i].domain_name) ||
3787 strequal(name, dom_list[i].dns_name) )
3789 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3790 name));
3792 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3793 if ( !d )
3794 break;
3796 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3797 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3798 sid_copy( &d->sid, &dom_list[i].sid );
3799 d->trust_flags = dom_list[i].trust_flags;
3800 d->trust_type = dom_list[i].trust_type;
3801 d->trust_attribs = dom_list[i].trust_attribs;
3803 break;
3807 TALLOC_FREE( dom_list );
3809 return d;
3813 /*********************************************************************
3814 ********************************************************************/
3816 void wcache_tdc_clear( void )
3818 if ( !init_wcache() )
3819 return;
3821 wcache_tdc_store_list( NULL, 0 );
3823 return;
3827 /*********************************************************************
3828 ********************************************************************/
3830 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3831 NTSTATUS status,
3832 const DOM_SID *user_sid,
3833 const char *homedir,
3834 const char *shell,
3835 const char *gecos,
3836 uint32 gid)
3838 struct cache_entry *centry;
3839 fstring tmp;
3841 if ( (centry = centry_start(domain, status)) == NULL )
3842 return;
3844 centry_put_string( centry, homedir );
3845 centry_put_string( centry, shell );
3846 centry_put_string( centry, gecos );
3847 centry_put_uint32( centry, gid );
3849 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
3851 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
3853 centry_free(centry);
3856 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3857 const DOM_SID *user_sid,
3858 TALLOC_CTX *ctx,
3859 ADS_STRUCT *ads, LDAPMessage *msg,
3860 char **homedir, char **shell, char **gecos,
3861 gid_t *p_gid)
3863 struct winbind_cache *cache = get_cache(domain);
3864 struct cache_entry *centry = NULL;
3865 NTSTATUS nt_status;
3866 fstring tmp;
3868 if (!cache->tdb)
3869 goto do_query;
3871 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
3872 sid_to_fstring(tmp, user_sid));
3874 if (!centry)
3875 goto do_query;
3877 *homedir = centry_string( centry, ctx );
3878 *shell = centry_string( centry, ctx );
3879 *gecos = centry_string( centry, ctx );
3880 *p_gid = centry_uint32( centry );
3882 centry_free(centry);
3884 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3885 sid_string_dbg(user_sid)));
3887 return NT_STATUS_OK;
3889 do_query:
3891 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3892 homedir, shell, gecos, p_gid );
3894 if ( NT_STATUS_IS_OK(nt_status) ) {
3895 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3896 *homedir, *shell, *gecos, *p_gid );
3899 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3900 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3901 domain->name ));
3902 set_domain_offline( domain );
3905 return nt_status;
3909 /* the cache backend methods are exposed via this structure */
3910 struct winbindd_methods cache_methods = {
3911 True,
3912 query_user_list,
3913 enum_dom_groups,
3914 enum_local_groups,
3915 name_to_sid,
3916 sid_to_name,
3917 rids_to_names,
3918 query_user,
3919 lookup_usergroups,
3920 lookup_useraliases,
3921 lookup_groupmem,
3922 sequence_number,
3923 lockout_policy,
3924 password_policy,
3925 trusted_domains