r23600: First step in abstracting the winbindd cache validation
[Samba.git] / source / nsswitch / winbindd_cache.c
blob122432d95b27e661f6049b4018f1ceb51782218a
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 2 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, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "includes.h"
28 #include "winbindd.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 #define WINBINDD_CACHE_VERSION 1
34 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
36 extern struct winbindd_methods reconnect_methods;
37 extern BOOL opt_nocache;
38 #ifdef HAVE_ADS
39 extern struct winbindd_methods ads_methods;
40 #endif
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys[] = {
49 "SEQNUM/",
50 "DR/",
51 "DE/",
52 "WINBINDD_OFFLINE",
53 WINBINDD_CACHE_VERSION_KEYSTR,
54 NULL
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static BOOL is_non_centry_key(TDB_DATA kbuf)
63 int i;
65 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66 return False;
68 for (i = 0; non_centry_keys[i] != NULL; i++) {
69 size_t namelen = strlen(non_centry_keys[i]);
70 if (kbuf.dsize < namelen) {
71 continue;
73 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74 return True;
77 return False;
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static BOOL global_winbindd_offline_state;
86 struct winbind_cache {
87 TDB_CONTEXT *tdb;
90 struct cache_entry {
91 NTSTATUS status;
92 uint32 sequence_number;
93 uint8 *data;
94 uint32 len, ofs;
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache *wcache;
103 void winbindd_check_cache_size(time_t t)
105 static time_t last_check_time;
106 struct stat st;
108 if (last_check_time == (time_t)0)
109 last_check_time = t;
111 if (t - last_check_time < 60 && t - last_check_time > 0)
112 return;
114 if (wcache == NULL || wcache->tdb == NULL) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116 return;
119 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121 return;
124 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st.st_size,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
135 struct winbind_cache *ret = wcache;
137 /* We have to know what type of domain we are dealing with first. */
139 if ( !domain->initialized ) {
140 init_dc_connection( domain );
144 OK. listen up becasue I'm only going to say this once.
145 We have the following scenarios to consider
146 (a) trusted AD domains on a Samba DC,
147 (b) trusted AD domains and we are joined to a non-kerberos domain
148 (c) trusted AD domains and we are joined to a kerberos (AD) domain
150 For (a) we can always contact the trusted domain using krb5
151 since we have the domain trust account password
153 For (b) we can only use RPC since we have no way of
154 getting a krb5 ticket in our own domain
156 For (c) we can always use krb5 since we have a kerberos trust
158 --jerry
161 if (!domain->backend) {
162 #ifdef HAVE_ADS
163 struct winbindd_domain *our_domain = domain;
165 /* find our domain first so we can figure out if we
166 are joined to a kerberized domain */
168 if ( !domain->primary )
169 our_domain = find_our_domain();
171 if ((our_domain->active_directory || IS_DC)
172 && domain->active_directory
173 && !lp_winbind_rpc_only()) {
174 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
175 domain->backend = &ads_methods;
176 } else {
177 #endif /* HAVE_ADS */
178 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
179 domain->backend = &reconnect_methods;
180 #ifdef HAVE_ADS
182 #endif /* HAVE_ADS */
185 if (ret)
186 return ret;
188 ret = SMB_XMALLOC_P(struct winbind_cache);
189 ZERO_STRUCTP(ret);
191 wcache = ret;
192 wcache_flush_cache();
194 return ret;
198 free a centry structure
200 static void centry_free(struct cache_entry *centry)
202 if (!centry)
203 return;
204 SAFE_FREE(centry->data);
205 free(centry);
208 static BOOL centry_check_bytes(struct cache_entry *centry, size_t nbytes)
210 if (centry->len - centry->ofs < nbytes) {
211 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
212 (unsigned int)nbytes,
213 centry->len - centry->ofs));
214 return False;
216 return True;
220 pull a uint32 from a cache entry
222 static uint32 centry_uint32(struct cache_entry *centry)
224 uint32 ret;
226 if (!centry_check_bytes(centry, 4)) {
227 smb_panic_fn("centry_uint32");
229 ret = IVAL(centry->data, centry->ofs);
230 centry->ofs += 4;
231 return ret;
235 pull a uint16 from a cache entry
237 static uint16 centry_uint16(struct cache_entry *centry)
239 uint16 ret;
240 if (!centry_check_bytes(centry, 2)) {
241 smb_panic_fn("centry_uint16");
243 ret = CVAL(centry->data, centry->ofs);
244 centry->ofs += 2;
245 return ret;
249 pull a uint8 from a cache entry
251 static uint8 centry_uint8(struct cache_entry *centry)
253 uint8 ret;
254 if (!centry_check_bytes(centry, 1)) {
255 smb_panic_fn("centry_uint8");
257 ret = CVAL(centry->data, centry->ofs);
258 centry->ofs += 1;
259 return ret;
263 pull a NTTIME from a cache entry
265 static NTTIME centry_nttime(struct cache_entry *centry)
267 NTTIME ret;
268 if (!centry_check_bytes(centry, 8)) {
269 smb_panic_fn("centry_nttime");
271 ret = IVAL(centry->data, centry->ofs);
272 centry->ofs += 4;
273 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
274 centry->ofs += 4;
275 return ret;
279 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
281 static time_t centry_time(struct cache_entry *centry)
283 return (time_t)centry_nttime(centry);
286 /* pull a string from a cache entry, using the supplied
287 talloc context
289 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
291 uint32 len;
292 char *ret;
294 len = centry_uint8(centry);
296 if (len == 0xFF) {
297 /* a deliberate NULL string */
298 return NULL;
301 if (!centry_check_bytes(centry, (size_t)len)) {
302 smb_panic_fn("centry_string");
305 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
306 if (!ret) {
307 smb_panic_fn("centry_string out of memory\n");
309 memcpy(ret,centry->data + centry->ofs, len);
310 ret[len] = 0;
311 centry->ofs += len;
312 return ret;
315 /* pull a hash16 from a cache entry, using the supplied
316 talloc context
318 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
320 uint32 len;
321 char *ret;
323 len = centry_uint8(centry);
325 if (len != 16) {
326 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
327 len ));
328 return NULL;
331 if (!centry_check_bytes(centry, 16)) {
332 return NULL;
335 ret = TALLOC_ARRAY(mem_ctx, char, 16);
336 if (!ret) {
337 smb_panic_fn("centry_hash out of memory\n");
339 memcpy(ret,centry->data + centry->ofs, 16);
340 centry->ofs += 16;
341 return ret;
344 /* pull a sid from a cache entry, using the supplied
345 talloc context
347 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
349 char *sid_string;
350 sid_string = centry_string(centry, mem_ctx);
351 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
352 return False;
354 return True;
357 /* the server is considered down if it can't give us a sequence number */
358 static BOOL wcache_server_down(struct winbindd_domain *domain)
360 BOOL ret;
362 if (!wcache->tdb)
363 return False;
365 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
367 if (ret)
368 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
369 domain->name ));
370 return ret;
373 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
375 TDB_DATA data;
376 fstring key;
377 uint32 time_diff;
379 if (!wcache->tdb) {
380 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
381 return NT_STATUS_UNSUCCESSFUL;
384 fstr_sprintf( key, "SEQNUM/%s", domain->name );
386 data = tdb_fetch_bystring( wcache->tdb, key );
387 if ( !data.dptr || data.dsize!=8 ) {
388 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
389 return NT_STATUS_UNSUCCESSFUL;
392 domain->sequence_number = IVAL(data.dptr, 0);
393 domain->last_seq_check = IVAL(data.dptr, 4);
395 SAFE_FREE(data.dptr);
397 /* have we expired? */
399 time_diff = now - domain->last_seq_check;
400 if ( time_diff > lp_winbind_cache_time() ) {
401 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
402 domain->name, domain->sequence_number,
403 (uint32)domain->last_seq_check));
404 return NT_STATUS_UNSUCCESSFUL;
407 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
408 domain->name, domain->sequence_number,
409 (uint32)domain->last_seq_check));
411 return NT_STATUS_OK;
414 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
416 TDB_DATA data;
417 fstring key_str;
418 uint8 buf[8];
420 if (!wcache->tdb) {
421 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
422 return NT_STATUS_UNSUCCESSFUL;
425 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
427 SIVAL(buf, 0, domain->sequence_number);
428 SIVAL(buf, 4, domain->last_seq_check);
429 data.dptr = buf;
430 data.dsize = 8;
432 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
433 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
434 return NT_STATUS_UNSUCCESSFUL;
437 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
438 domain->name, domain->sequence_number,
439 (uint32)domain->last_seq_check));
441 return NT_STATUS_OK;
445 refresh the domain sequence number. If force is True
446 then always refresh it, no matter how recently we fetched it
449 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
451 NTSTATUS status;
452 unsigned time_diff;
453 time_t t = time(NULL);
454 unsigned cache_time = lp_winbind_cache_time();
456 if ( IS_DOMAIN_OFFLINE(domain) ) {
457 return;
460 get_cache( domain );
462 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
463 /* trying to reconnect is expensive, don't do it too often */
464 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
465 cache_time *= 8;
467 #endif
469 time_diff = t - domain->last_seq_check;
471 /* see if we have to refetch the domain sequence number */
472 if (!force && (time_diff < cache_time)) {
473 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
474 goto done;
477 /* try to get the sequence number from the tdb cache first */
478 /* this will update the timestamp as well */
480 status = fetch_cache_seqnum( domain, t );
481 if ( NT_STATUS_IS_OK(status) )
482 goto done;
484 /* important! make sure that we know if this is a native
485 mode domain or not. And that we can contact it. */
487 if ( winbindd_can_contact_domain( domain ) ) {
488 status = domain->backend->sequence_number(domain,
489 &domain->sequence_number);
490 } else {
491 /* just use the current time */
492 status = NT_STATUS_OK;
493 domain->sequence_number = time(NULL);
497 /* the above call could have set our domain->backend to NULL when
498 * coming from offline to online mode, make sure to reinitialize the
499 * backend - Guenther */
500 get_cache( domain );
502 if (!NT_STATUS_IS_OK(status)) {
503 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
504 domain->sequence_number = DOM_SEQUENCE_NONE;
507 domain->last_status = status;
508 domain->last_seq_check = time(NULL);
510 /* save the new sequence number ni the cache */
511 store_cache_seqnum( domain );
513 done:
514 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
515 domain->name, domain->sequence_number));
517 return;
521 decide if a cache entry has expired
523 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
525 /* If we've been told to be offline - stay in that state... */
526 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
527 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
528 keystr, domain->name ));
529 return False;
532 /* when the domain is offline return the cached entry.
533 * This deals with transient offline states... */
535 if (!domain->online) {
536 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
537 keystr, domain->name ));
538 return False;
541 /* if the server is OK and our cache entry came from when it was down then
542 the entry is invalid */
543 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
544 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
545 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
546 keystr, domain->name ));
547 return True;
550 /* if the server is down or the cache entry is not older than the
551 current sequence number then it is OK */
552 if (wcache_server_down(domain) ||
553 centry->sequence_number == domain->sequence_number) {
554 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
555 keystr, domain->name ));
556 return False;
559 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
560 keystr, domain->name ));
562 /* it's expired */
563 return True;
566 static struct cache_entry *wcache_fetch_raw(char *kstr)
568 TDB_DATA data;
569 struct cache_entry *centry;
570 TDB_DATA key;
572 key = string_tdb_data(kstr);
573 data = tdb_fetch(wcache->tdb, key);
574 if (!data.dptr) {
575 /* a cache miss */
576 return NULL;
579 centry = SMB_XMALLOC_P(struct cache_entry);
580 centry->data = (unsigned char *)data.dptr;
581 centry->len = data.dsize;
582 centry->ofs = 0;
584 if (centry->len < 8) {
585 /* huh? corrupt cache? */
586 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
587 centry_free(centry);
588 return NULL;
591 centry->status = NT_STATUS(centry_uint32(centry));
592 centry->sequence_number = centry_uint32(centry);
594 return centry;
598 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
599 number and return status
601 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
602 struct winbindd_domain *domain,
603 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
604 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
605 struct winbindd_domain *domain,
606 const char *format, ...)
608 va_list ap;
609 char *kstr;
610 struct cache_entry *centry;
612 if (opt_nocache) {
613 return NULL;
616 refresh_sequence_number(domain, False);
618 va_start(ap, format);
619 smb_xvasprintf(&kstr, format, ap);
620 va_end(ap);
622 centry = wcache_fetch_raw(kstr);
623 if (centry == NULL) {
624 free(kstr);
625 return NULL;
628 if (centry_expired(domain, kstr, centry)) {
630 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
631 kstr, domain->name ));
633 centry_free(centry);
634 free(kstr);
635 return NULL;
638 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
639 kstr, domain->name ));
641 free(kstr);
642 return centry;
645 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
646 static void wcache_delete(const char *format, ...)
648 va_list ap;
649 char *kstr;
650 TDB_DATA key;
652 va_start(ap, format);
653 smb_xvasprintf(&kstr, format, ap);
654 va_end(ap);
656 key = string_tdb_data(kstr);
658 tdb_delete(wcache->tdb, key);
659 free(kstr);
663 make sure we have at least len bytes available in a centry
665 static void centry_expand(struct cache_entry *centry, uint32 len)
667 if (centry->len - centry->ofs >= len)
668 return;
669 centry->len *= 2;
670 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
671 centry->len);
672 if (!centry->data) {
673 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
674 smb_panic_fn("out of memory in centry_expand");
679 push a uint32 into a centry
681 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
683 centry_expand(centry, 4);
684 SIVAL(centry->data, centry->ofs, v);
685 centry->ofs += 4;
689 push a uint16 into a centry
691 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
693 centry_expand(centry, 2);
694 SIVAL(centry->data, centry->ofs, v);
695 centry->ofs += 2;
699 push a uint8 into a centry
701 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
703 centry_expand(centry, 1);
704 SCVAL(centry->data, centry->ofs, v);
705 centry->ofs += 1;
709 push a string into a centry
711 static void centry_put_string(struct cache_entry *centry, const char *s)
713 int len;
715 if (!s) {
716 /* null strings are marked as len 0xFFFF */
717 centry_put_uint8(centry, 0xFF);
718 return;
721 len = strlen(s);
722 /* can't handle more than 254 char strings. Truncating is probably best */
723 if (len > 254) {
724 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
725 len = 254;
727 centry_put_uint8(centry, len);
728 centry_expand(centry, len);
729 memcpy(centry->data + centry->ofs, s, len);
730 centry->ofs += len;
734 push a 16 byte hash into a centry - treat as 16 byte string.
736 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
738 centry_put_uint8(centry, 16);
739 centry_expand(centry, 16);
740 memcpy(centry->data + centry->ofs, val, 16);
741 centry->ofs += 16;
744 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
746 fstring sid_string;
747 centry_put_string(centry, sid_to_string(sid_string, sid));
751 push a NTTIME into a centry
753 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
755 centry_expand(centry, 8);
756 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
757 centry->ofs += 4;
758 SIVAL(centry->data, centry->ofs, nt >> 32);
759 centry->ofs += 4;
763 push a time_t into a centry - use a 64 bit size.
764 NTTIME here is being used as a convenient 64-bit size.
766 static void centry_put_time(struct cache_entry *centry, time_t t)
768 NTTIME nt = (NTTIME)t;
769 centry_put_nttime(centry, nt);
773 start a centry for output. When finished, call centry_end()
775 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
777 struct cache_entry *centry;
779 if (!wcache->tdb)
780 return NULL;
782 centry = SMB_XMALLOC_P(struct cache_entry);
784 centry->len = 8192; /* reasonable default */
785 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
786 centry->ofs = 0;
787 centry->sequence_number = domain->sequence_number;
788 centry_put_uint32(centry, NT_STATUS_V(status));
789 centry_put_uint32(centry, centry->sequence_number);
790 return centry;
794 finish a centry and write it to the tdb
796 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
797 static void centry_end(struct cache_entry *centry, const char *format, ...)
799 va_list ap;
800 char *kstr;
801 TDB_DATA key, data;
803 va_start(ap, format);
804 smb_xvasprintf(&kstr, format, ap);
805 va_end(ap);
807 key = string_tdb_data(kstr);
808 data.dptr = centry->data;
809 data.dsize = centry->ofs;
811 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
812 free(kstr);
815 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
816 NTSTATUS status, const char *domain_name,
817 const char *name, const DOM_SID *sid,
818 enum lsa_SidType type)
820 struct cache_entry *centry;
821 fstring uname;
823 centry = centry_start(domain, status);
824 if (!centry)
825 return;
826 centry_put_uint32(centry, type);
827 centry_put_sid(centry, sid);
828 fstrcpy(uname, name);
829 strupper_m(uname);
830 centry_end(centry, "NS/%s/%s", domain_name, uname);
831 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname,
832 sid_string_static(sid), nt_errstr(status)));
833 centry_free(centry);
836 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
837 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
839 struct cache_entry *centry;
840 fstring sid_string;
842 if (is_null_sid(sid)) {
843 return;
846 centry = centry_start(domain, status);
847 if (!centry)
848 return;
849 if (NT_STATUS_IS_OK(status)) {
850 centry_put_uint32(centry, type);
851 centry_put_string(centry, domain_name);
852 centry_put_string(centry, name);
854 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
855 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
856 name, nt_errstr(status)));
857 centry_free(centry);
861 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
863 struct cache_entry *centry;
864 fstring sid_string;
866 if (is_null_sid(&info->user_sid)) {
867 return;
870 centry = centry_start(domain, status);
871 if (!centry)
872 return;
873 centry_put_string(centry, info->acct_name);
874 centry_put_string(centry, info->full_name);
875 centry_put_string(centry, info->homedir);
876 centry_put_string(centry, info->shell);
877 centry_put_uint32(centry, info->primary_gid);
878 centry_put_sid(centry, &info->user_sid);
879 centry_put_sid(centry, &info->group_sid);
880 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
881 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
882 centry_free(centry);
885 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
887 struct cache_entry *centry;
889 centry = centry_start(domain, status);
890 if (!centry)
891 return;
893 centry_put_nttime(centry, lockout_policy->duration);
894 centry_put_nttime(centry, lockout_policy->reset_count);
895 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
897 centry_end(centry, "LOC_POL/%s", domain->name);
899 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
901 centry_free(centry);
904 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
906 struct cache_entry *centry;
908 centry = centry_start(domain, status);
909 if (!centry)
910 return;
912 centry_put_uint16(centry, policy->min_length_password);
913 centry_put_uint16(centry, policy->password_history);
914 centry_put_uint32(centry, policy->password_properties);
915 centry_put_nttime(centry, policy->expire);
916 centry_put_nttime(centry, policy->min_passwordage);
918 centry_end(centry, "PWD_POL/%s", domain->name);
920 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
922 centry_free(centry);
925 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
927 struct winbind_cache *cache = get_cache(domain);
928 TDB_DATA data;
929 fstring key_str;
930 uint32 rid;
932 if (!cache->tdb) {
933 return NT_STATUS_INTERNAL_DB_ERROR;
936 if (is_null_sid(sid)) {
937 return NT_STATUS_INVALID_SID;
940 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
941 return NT_STATUS_INVALID_SID;
944 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
946 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
947 if (!data.dptr) {
948 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
951 SAFE_FREE(data.dptr);
952 return NT_STATUS_OK;
955 /* Lookup creds for a SID - copes with old (unsalted) creds as well
956 as new salted ones. */
958 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
959 TALLOC_CTX *mem_ctx,
960 const DOM_SID *sid,
961 const uint8 **cached_nt_pass,
962 const uint8 **cached_salt)
964 struct winbind_cache *cache = get_cache(domain);
965 struct cache_entry *centry = NULL;
966 NTSTATUS status;
967 time_t t;
968 uint32 rid;
970 if (!cache->tdb) {
971 return NT_STATUS_INTERNAL_DB_ERROR;
974 if (is_null_sid(sid)) {
975 return NT_STATUS_INVALID_SID;
978 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
979 return NT_STATUS_INVALID_SID;
982 /* Try and get a salted cred first. If we can't
983 fall back to an unsalted cred. */
985 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
986 if (!centry) {
987 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
988 sid_string_static(sid)));
989 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
992 t = centry_time(centry);
994 /* In the salted case this isn't actually the nt_hash itself,
995 but the MD5 of the salt + nt_hash. Let the caller
996 sort this out. It can tell as we only return the cached_salt
997 if we are returning a salted cred. */
999 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1000 if (*cached_nt_pass == NULL) {
1001 const char *sidstr = sid_string_static(sid);
1003 /* Bad (old) cred cache. Delete and pretend we
1004 don't have it. */
1005 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1006 sidstr));
1007 wcache_delete("CRED/%s", sidstr);
1008 centry_free(centry);
1009 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1012 /* We only have 17 bytes more data in the salted cred case. */
1013 if (centry->len - centry->ofs == 17) {
1014 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1015 } else {
1016 *cached_salt = NULL;
1019 #if DEBUG_PASSWORD
1020 dump_data(100, *cached_nt_pass, NT_HASH_LEN);
1021 if (*cached_salt) {
1022 dump_data(100, *cached_salt, NT_HASH_LEN);
1024 #endif
1025 status = centry->status;
1027 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1028 sid_string_static(sid), nt_errstr(status) ));
1030 centry_free(centry);
1031 return status;
1034 /* Store creds for a SID - only writes out new salted ones. */
1036 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1037 TALLOC_CTX *mem_ctx,
1038 const DOM_SID *sid,
1039 const uint8 nt_pass[NT_HASH_LEN])
1041 struct cache_entry *centry;
1042 fstring sid_string;
1043 uint32 rid;
1044 uint8 cred_salt[NT_HASH_LEN];
1045 uint8 salted_hash[NT_HASH_LEN];
1047 if (is_null_sid(sid)) {
1048 return NT_STATUS_INVALID_SID;
1051 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1052 return NT_STATUS_INVALID_SID;
1055 centry = centry_start(domain, NT_STATUS_OK);
1056 if (!centry) {
1057 return NT_STATUS_INTERNAL_DB_ERROR;
1060 #if DEBUG_PASSWORD
1061 dump_data(100, nt_pass, NT_HASH_LEN);
1062 #endif
1064 centry_put_time(centry, time(NULL));
1066 /* Create a salt and then salt the hash. */
1067 generate_random_buffer(cred_salt, NT_HASH_LEN);
1068 E_md5hash(cred_salt, nt_pass, salted_hash);
1070 centry_put_hash16(centry, salted_hash);
1071 centry_put_hash16(centry, cred_salt);
1072 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1074 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1076 centry_free(centry);
1078 return NT_STATUS_OK;
1082 /* Query display info. This is the basic user list fn */
1083 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1084 TALLOC_CTX *mem_ctx,
1085 uint32 *num_entries,
1086 WINBIND_USERINFO **info)
1088 struct winbind_cache *cache = get_cache(domain);
1089 struct cache_entry *centry = NULL;
1090 NTSTATUS status;
1091 unsigned int i, retry;
1093 if (!cache->tdb)
1094 goto do_query;
1096 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1097 if (!centry)
1098 goto do_query;
1100 *num_entries = centry_uint32(centry);
1102 if (*num_entries == 0)
1103 goto do_cached;
1105 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1106 if (! (*info)) {
1107 smb_panic_fn("query_user_list out of memory");
1109 for (i=0; i<(*num_entries); i++) {
1110 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1111 (*info)[i].full_name = centry_string(centry, mem_ctx);
1112 (*info)[i].homedir = centry_string(centry, mem_ctx);
1113 (*info)[i].shell = centry_string(centry, mem_ctx);
1114 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1115 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1118 do_cached:
1119 status = centry->status;
1121 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1122 domain->name, nt_errstr(status) ));
1124 centry_free(centry);
1125 return status;
1127 do_query:
1128 *num_entries = 0;
1129 *info = NULL;
1131 /* Return status value returned by seq number check */
1133 if (!NT_STATUS_IS_OK(domain->last_status))
1134 return domain->last_status;
1136 /* Put the query_user_list() in a retry loop. There appears to be
1137 * some bug either with Windows 2000 or Samba's handling of large
1138 * rpc replies. This manifests itself as sudden disconnection
1139 * at a random point in the enumeration of a large (60k) user list.
1140 * The retry loop simply tries the operation again. )-: It's not
1141 * pretty but an acceptable workaround until we work out what the
1142 * real problem is. */
1144 retry = 0;
1145 do {
1147 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1148 domain->name ));
1150 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1151 if (!NT_STATUS_IS_OK(status))
1152 DEBUG(3, ("query_user_list: returned 0x%08x, "
1153 "retrying\n", NT_STATUS_V(status)));
1154 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1155 DEBUG(3, ("query_user_list: flushing "
1156 "connection cache\n"));
1157 invalidate_cm_connection(&domain->conn);
1160 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1161 (retry++ < 5));
1163 /* and save it */
1164 refresh_sequence_number(domain, False);
1165 centry = centry_start(domain, status);
1166 if (!centry)
1167 goto skip_save;
1168 centry_put_uint32(centry, *num_entries);
1169 for (i=0; i<(*num_entries); i++) {
1170 centry_put_string(centry, (*info)[i].acct_name);
1171 centry_put_string(centry, (*info)[i].full_name);
1172 centry_put_string(centry, (*info)[i].homedir);
1173 centry_put_string(centry, (*info)[i].shell);
1174 centry_put_sid(centry, &(*info)[i].user_sid);
1175 centry_put_sid(centry, &(*info)[i].group_sid);
1176 if (domain->backend && domain->backend->consistent) {
1177 /* when the backend is consistent we can pre-prime some mappings */
1178 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1179 domain->name,
1180 (*info)[i].acct_name,
1181 &(*info)[i].user_sid,
1182 SID_NAME_USER);
1183 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1184 &(*info)[i].user_sid,
1185 domain->name,
1186 (*info)[i].acct_name,
1187 SID_NAME_USER);
1188 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1191 centry_end(centry, "UL/%s", domain->name);
1192 centry_free(centry);
1194 skip_save:
1195 return status;
1198 /* list all domain groups */
1199 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1200 TALLOC_CTX *mem_ctx,
1201 uint32 *num_entries,
1202 struct acct_info **info)
1204 struct winbind_cache *cache = get_cache(domain);
1205 struct cache_entry *centry = NULL;
1206 NTSTATUS status;
1207 unsigned int i;
1209 if (!cache->tdb)
1210 goto do_query;
1212 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1213 if (!centry)
1214 goto do_query;
1216 *num_entries = centry_uint32(centry);
1218 if (*num_entries == 0)
1219 goto do_cached;
1221 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1222 if (! (*info)) {
1223 smb_panic_fn("enum_dom_groups out of memory");
1225 for (i=0; i<(*num_entries); i++) {
1226 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1227 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1228 (*info)[i].rid = centry_uint32(centry);
1231 do_cached:
1232 status = centry->status;
1234 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1235 domain->name, nt_errstr(status) ));
1237 centry_free(centry);
1238 return status;
1240 do_query:
1241 *num_entries = 0;
1242 *info = NULL;
1244 /* Return status value returned by seq number check */
1246 if (!NT_STATUS_IS_OK(domain->last_status))
1247 return domain->last_status;
1249 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1250 domain->name ));
1252 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1254 /* and save it */
1255 refresh_sequence_number(domain, False);
1256 centry = centry_start(domain, status);
1257 if (!centry)
1258 goto skip_save;
1259 centry_put_uint32(centry, *num_entries);
1260 for (i=0; i<(*num_entries); i++) {
1261 centry_put_string(centry, (*info)[i].acct_name);
1262 centry_put_string(centry, (*info)[i].acct_desc);
1263 centry_put_uint32(centry, (*info)[i].rid);
1265 centry_end(centry, "GL/%s/domain", domain->name);
1266 centry_free(centry);
1268 skip_save:
1269 return status;
1272 /* list all domain groups */
1273 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1274 TALLOC_CTX *mem_ctx,
1275 uint32 *num_entries,
1276 struct acct_info **info)
1278 struct winbind_cache *cache = get_cache(domain);
1279 struct cache_entry *centry = NULL;
1280 NTSTATUS status;
1281 unsigned int i;
1283 if (!cache->tdb)
1284 goto do_query;
1286 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1287 if (!centry)
1288 goto do_query;
1290 *num_entries = centry_uint32(centry);
1292 if (*num_entries == 0)
1293 goto do_cached;
1295 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1296 if (! (*info)) {
1297 smb_panic_fn("enum_dom_groups out of memory");
1299 for (i=0; i<(*num_entries); i++) {
1300 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1301 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1302 (*info)[i].rid = centry_uint32(centry);
1305 do_cached:
1307 /* If we are returning cached data and the domain controller
1308 is down then we don't know whether the data is up to date
1309 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1310 indicate this. */
1312 if (wcache_server_down(domain)) {
1313 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1314 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1315 } else
1316 status = centry->status;
1318 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1319 domain->name, nt_errstr(status) ));
1321 centry_free(centry);
1322 return status;
1324 do_query:
1325 *num_entries = 0;
1326 *info = NULL;
1328 /* Return status value returned by seq number check */
1330 if (!NT_STATUS_IS_OK(domain->last_status))
1331 return domain->last_status;
1333 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1334 domain->name ));
1336 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1338 /* and save it */
1339 refresh_sequence_number(domain, False);
1340 centry = centry_start(domain, status);
1341 if (!centry)
1342 goto skip_save;
1343 centry_put_uint32(centry, *num_entries);
1344 for (i=0; i<(*num_entries); i++) {
1345 centry_put_string(centry, (*info)[i].acct_name);
1346 centry_put_string(centry, (*info)[i].acct_desc);
1347 centry_put_uint32(centry, (*info)[i].rid);
1349 centry_end(centry, "GL/%s/local", domain->name);
1350 centry_free(centry);
1352 skip_save:
1353 return status;
1356 /* convert a single name to a sid in a domain */
1357 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1358 TALLOC_CTX *mem_ctx,
1359 enum winbindd_cmd orig_cmd,
1360 const char *domain_name,
1361 const char *name,
1362 DOM_SID *sid,
1363 enum lsa_SidType *type)
1365 struct winbind_cache *cache = get_cache(domain);
1366 struct cache_entry *centry = NULL;
1367 NTSTATUS status;
1368 fstring uname;
1370 if (!cache->tdb)
1371 goto do_query;
1373 fstrcpy(uname, name);
1374 strupper_m(uname);
1375 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1376 if (!centry)
1377 goto do_query;
1378 *type = (enum lsa_SidType)centry_uint32(centry);
1379 status = centry->status;
1380 if (NT_STATUS_IS_OK(status)) {
1381 centry_sid(centry, mem_ctx, sid);
1384 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1385 domain->name, nt_errstr(status) ));
1387 centry_free(centry);
1388 return status;
1390 do_query:
1391 ZERO_STRUCTP(sid);
1393 /* If the seq number check indicated that there is a problem
1394 * with this DC, then return that status... except for
1395 * access_denied. This is special because the dc may be in
1396 * "restrict anonymous = 1" mode, in which case it will deny
1397 * most unauthenticated operations, but *will* allow the LSA
1398 * name-to-sid that we try as a fallback. */
1400 if (!(NT_STATUS_IS_OK(domain->last_status)
1401 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1402 return domain->last_status;
1404 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1405 domain->name ));
1407 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1408 domain_name, name, sid, type);
1410 /* and save it */
1411 refresh_sequence_number(domain, False);
1413 if (domain->online && !is_null_sid(sid)) {
1414 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1417 if (NT_STATUS_IS_OK(status)) {
1418 strupper_m(CONST_DISCARD(char *,domain_name));
1419 strlower_m(CONST_DISCARD(char *,name));
1420 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1423 return status;
1426 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1427 given */
1428 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1429 TALLOC_CTX *mem_ctx,
1430 const DOM_SID *sid,
1431 char **domain_name,
1432 char **name,
1433 enum lsa_SidType *type)
1435 struct winbind_cache *cache = get_cache(domain);
1436 struct cache_entry *centry = NULL;
1437 NTSTATUS status;
1438 fstring sid_string;
1440 if (!cache->tdb)
1441 goto do_query;
1443 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1444 if (!centry)
1445 goto do_query;
1446 if (NT_STATUS_IS_OK(centry->status)) {
1447 *type = (enum lsa_SidType)centry_uint32(centry);
1448 *domain_name = centry_string(centry, mem_ctx);
1449 *name = centry_string(centry, mem_ctx);
1451 status = centry->status;
1453 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1454 domain->name, nt_errstr(status) ));
1456 centry_free(centry);
1457 return status;
1459 do_query:
1460 *name = NULL;
1461 *domain_name = NULL;
1463 /* If the seq number check indicated that there is a problem
1464 * with this DC, then return that status... except for
1465 * access_denied. This is special because the dc may be in
1466 * "restrict anonymous = 1" mode, in which case it will deny
1467 * most unauthenticated operations, but *will* allow the LSA
1468 * sid-to-name that we try as a fallback. */
1470 if (!(NT_STATUS_IS_OK(domain->last_status)
1471 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1472 return domain->last_status;
1474 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1475 domain->name ));
1477 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1479 /* and save it */
1480 refresh_sequence_number(domain, False);
1481 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1483 /* We can't save the name to sid mapping here, as with sid history a
1484 * later name2sid would give the wrong sid. */
1486 return status;
1489 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1490 TALLOC_CTX *mem_ctx,
1491 const DOM_SID *domain_sid,
1492 uint32 *rids,
1493 size_t num_rids,
1494 char **domain_name,
1495 char ***names,
1496 enum lsa_SidType **types)
1498 struct winbind_cache *cache = get_cache(domain);
1499 size_t i;
1500 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1501 BOOL have_mapped;
1502 BOOL have_unmapped;
1504 *domain_name = NULL;
1505 *names = NULL;
1506 *types = NULL;
1508 if (!cache->tdb) {
1509 goto do_query;
1512 if (num_rids == 0) {
1513 return NT_STATUS_OK;
1516 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1517 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1519 if ((*names == NULL) || (*types == NULL)) {
1520 result = NT_STATUS_NO_MEMORY;
1521 goto error;
1524 have_mapped = have_unmapped = False;
1526 for (i=0; i<num_rids; i++) {
1527 DOM_SID sid;
1528 struct cache_entry *centry;
1530 if (!sid_compose(&sid, domain_sid, rids[i])) {
1531 result = NT_STATUS_INTERNAL_ERROR;
1532 goto error;
1535 centry = wcache_fetch(cache, domain, "SN/%s",
1536 sid_string_static(&sid));
1537 if (!centry) {
1538 goto do_query;
1541 (*types)[i] = SID_NAME_UNKNOWN;
1542 (*names)[i] = talloc_strdup(*names, "");
1544 if (NT_STATUS_IS_OK(centry->status)) {
1545 char *dom;
1546 have_mapped = True;
1547 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1548 dom = centry_string(centry, mem_ctx);
1549 if (*domain_name == NULL) {
1550 *domain_name = dom;
1551 } else {
1552 talloc_free(dom);
1554 (*names)[i] = centry_string(centry, *names);
1555 } else {
1556 have_unmapped = True;
1559 centry_free(centry);
1562 if (!have_mapped) {
1563 return NT_STATUS_NONE_MAPPED;
1565 if (!have_unmapped) {
1566 return NT_STATUS_OK;
1568 return STATUS_SOME_UNMAPPED;
1570 do_query:
1572 TALLOC_FREE(*names);
1573 TALLOC_FREE(*types);
1575 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1576 rids, num_rids, domain_name,
1577 names, types);
1579 if (!NT_STATUS_IS_OK(result) &&
1580 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1581 return result;
1584 refresh_sequence_number(domain, False);
1586 for (i=0; i<num_rids; i++) {
1587 DOM_SID sid;
1588 NTSTATUS status;
1590 if (!sid_compose(&sid, domain_sid, rids[i])) {
1591 result = NT_STATUS_INTERNAL_ERROR;
1592 goto error;
1595 status = (*types)[i] == SID_NAME_UNKNOWN ?
1596 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1598 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1599 (*names)[i], (*types)[i]);
1602 return result;
1604 error:
1606 TALLOC_FREE(*names);
1607 TALLOC_FREE(*types);
1608 return result;
1611 /* Lookup user information from a rid */
1612 static NTSTATUS query_user(struct winbindd_domain *domain,
1613 TALLOC_CTX *mem_ctx,
1614 const DOM_SID *user_sid,
1615 WINBIND_USERINFO *info)
1617 struct winbind_cache *cache = get_cache(domain);
1618 struct cache_entry *centry = NULL;
1619 NTSTATUS status;
1621 if (!cache->tdb)
1622 goto do_query;
1624 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1626 /* If we have an access denied cache entry and a cached info3 in the
1627 samlogon cache then do a query. This will force the rpc back end
1628 to return the info3 data. */
1630 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1631 netsamlogon_cache_have(user_sid)) {
1632 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1633 domain->last_status = NT_STATUS_OK;
1634 centry_free(centry);
1635 goto do_query;
1638 if (!centry)
1639 goto do_query;
1641 info->acct_name = centry_string(centry, mem_ctx);
1642 info->full_name = centry_string(centry, mem_ctx);
1643 info->homedir = centry_string(centry, mem_ctx);
1644 info->shell = centry_string(centry, mem_ctx);
1645 info->primary_gid = centry_uint32(centry);
1646 centry_sid(centry, mem_ctx, &info->user_sid);
1647 centry_sid(centry, mem_ctx, &info->group_sid);
1648 status = centry->status;
1650 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1651 domain->name, nt_errstr(status) ));
1653 centry_free(centry);
1654 return status;
1656 do_query:
1657 ZERO_STRUCTP(info);
1659 /* Return status value returned by seq number check */
1661 if (!NT_STATUS_IS_OK(domain->last_status))
1662 return domain->last_status;
1664 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1665 domain->name ));
1667 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1669 /* and save it */
1670 refresh_sequence_number(domain, False);
1671 wcache_save_user(domain, status, info);
1673 return status;
1677 /* Lookup groups a user is a member of. */
1678 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1679 TALLOC_CTX *mem_ctx,
1680 const DOM_SID *user_sid,
1681 uint32 *num_groups, DOM_SID **user_gids)
1683 struct winbind_cache *cache = get_cache(domain);
1684 struct cache_entry *centry = NULL;
1685 NTSTATUS status;
1686 unsigned int i;
1687 fstring sid_string;
1689 if (!cache->tdb)
1690 goto do_query;
1692 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, 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, ("lookup_usergroups: 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 *num_groups = centry_uint32(centry);
1711 if (*num_groups == 0)
1712 goto do_cached;
1714 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1715 if (! (*user_gids)) {
1716 smb_panic_fn("lookup_usergroups out of memory");
1718 for (i=0; i<(*num_groups); i++) {
1719 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1722 do_cached:
1723 status = centry->status;
1725 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1726 domain->name, nt_errstr(status) ));
1728 centry_free(centry);
1729 return status;
1731 do_query:
1732 (*num_groups) = 0;
1733 (*user_gids) = NULL;
1735 /* Return status value returned by seq number check */
1737 if (!NT_STATUS_IS_OK(domain->last_status))
1738 return domain->last_status;
1740 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1741 domain->name ));
1743 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1745 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1746 goto skip_save;
1748 /* and save it */
1749 refresh_sequence_number(domain, False);
1750 centry = centry_start(domain, status);
1751 if (!centry)
1752 goto skip_save;
1753 centry_put_uint32(centry, *num_groups);
1754 for (i=0; i<(*num_groups); i++) {
1755 centry_put_sid(centry, &(*user_gids)[i]);
1757 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1758 centry_free(centry);
1760 skip_save:
1761 return status;
1764 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1765 TALLOC_CTX *mem_ctx,
1766 uint32 num_sids, const DOM_SID *sids,
1767 uint32 *num_aliases, uint32 **alias_rids)
1769 struct winbind_cache *cache = get_cache(domain);
1770 struct cache_entry *centry = NULL;
1771 NTSTATUS status;
1772 char *sidlist = talloc_strdup(mem_ctx, "");
1773 int i;
1775 if (!cache->tdb)
1776 goto do_query;
1778 if (num_sids == 0) {
1779 *num_aliases = 0;
1780 *alias_rids = NULL;
1781 return NT_STATUS_OK;
1784 /* We need to cache indexed by the whole list of SIDs, the aliases
1785 * resulting might come from any of the SIDs. */
1787 for (i=0; i<num_sids; i++) {
1788 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1789 sid_string_static(&sids[i]));
1790 if (sidlist == NULL)
1791 return NT_STATUS_NO_MEMORY;
1794 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1796 if (!centry)
1797 goto do_query;
1799 *num_aliases = centry_uint32(centry);
1800 *alias_rids = NULL;
1802 if (*num_aliases) {
1803 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1805 if ((*alias_rids) == NULL) {
1806 centry_free(centry);
1807 return NT_STATUS_NO_MEMORY;
1809 } else {
1810 (*alias_rids) = NULL;
1813 for (i=0; i<(*num_aliases); i++)
1814 (*alias_rids)[i] = centry_uint32(centry);
1816 status = centry->status;
1818 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1819 "status %s\n", domain->name, nt_errstr(status)));
1821 centry_free(centry);
1822 return status;
1824 do_query:
1825 (*num_aliases) = 0;
1826 (*alias_rids) = NULL;
1828 if (!NT_STATUS_IS_OK(domain->last_status))
1829 return domain->last_status;
1831 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1832 "for domain %s\n", domain->name ));
1834 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1835 num_sids, sids,
1836 num_aliases, alias_rids);
1838 /* and save it */
1839 refresh_sequence_number(domain, False);
1840 centry = centry_start(domain, status);
1841 if (!centry)
1842 goto skip_save;
1843 centry_put_uint32(centry, *num_aliases);
1844 for (i=0; i<(*num_aliases); i++)
1845 centry_put_uint32(centry, (*alias_rids)[i]);
1846 centry_end(centry, "UA%s", sidlist);
1847 centry_free(centry);
1849 skip_save:
1850 return status;
1854 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1855 TALLOC_CTX *mem_ctx,
1856 const DOM_SID *group_sid, uint32 *num_names,
1857 DOM_SID **sid_mem, char ***names,
1858 uint32 **name_types)
1860 struct winbind_cache *cache = get_cache(domain);
1861 struct cache_entry *centry = NULL;
1862 NTSTATUS status;
1863 unsigned int i;
1864 fstring sid_string;
1866 if (!cache->tdb)
1867 goto do_query;
1869 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1870 if (!centry)
1871 goto do_query;
1873 *num_names = centry_uint32(centry);
1875 if (*num_names == 0)
1876 goto do_cached;
1878 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1879 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1880 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1882 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1883 smb_panic_fn("lookup_groupmem out of memory");
1886 for (i=0; i<(*num_names); i++) {
1887 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1888 (*names)[i] = centry_string(centry, mem_ctx);
1889 (*name_types)[i] = centry_uint32(centry);
1892 do_cached:
1893 status = centry->status;
1895 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1896 domain->name, nt_errstr(status)));
1898 centry_free(centry);
1899 return status;
1901 do_query:
1902 (*num_names) = 0;
1903 (*sid_mem) = NULL;
1904 (*names) = NULL;
1905 (*name_types) = NULL;
1907 /* Return status value returned by seq number check */
1909 if (!NT_STATUS_IS_OK(domain->last_status))
1910 return domain->last_status;
1912 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1913 domain->name ));
1915 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1916 sid_mem, names, name_types);
1918 /* and save it */
1919 refresh_sequence_number(domain, False);
1920 centry = centry_start(domain, status);
1921 if (!centry)
1922 goto skip_save;
1923 centry_put_uint32(centry, *num_names);
1924 for (i=0; i<(*num_names); i++) {
1925 centry_put_sid(centry, &(*sid_mem)[i]);
1926 centry_put_string(centry, (*names)[i]);
1927 centry_put_uint32(centry, (*name_types)[i]);
1929 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1930 centry_free(centry);
1932 skip_save:
1933 return status;
1936 /* find the sequence number for a domain */
1937 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1939 refresh_sequence_number(domain, False);
1941 *seq = domain->sequence_number;
1943 return NT_STATUS_OK;
1946 /* enumerate trusted domains
1947 * (we need to have the list of trustdoms in the cache when we go offline) -
1948 * Guenther */
1949 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1950 TALLOC_CTX *mem_ctx,
1951 uint32 *num_domains,
1952 char ***names,
1953 char ***alt_names,
1954 DOM_SID **dom_sids)
1956 struct winbind_cache *cache = get_cache(domain);
1957 struct cache_entry *centry = NULL;
1958 NTSTATUS status;
1959 int i;
1961 if (!cache->tdb)
1962 goto do_query;
1964 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1966 if (!centry) {
1967 goto do_query;
1970 *num_domains = centry_uint32(centry);
1972 if (*num_domains) {
1973 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1974 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1975 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1977 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1978 smb_panic_fn("trusted_domains out of memory");
1980 } else {
1981 (*names) = NULL;
1982 (*alt_names) = NULL;
1983 (*dom_sids) = NULL;
1986 for (i=0; i<(*num_domains); i++) {
1987 (*names)[i] = centry_string(centry, mem_ctx);
1988 (*alt_names)[i] = centry_string(centry, mem_ctx);
1989 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1992 status = centry->status;
1994 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1995 domain->name, *num_domains, nt_errstr(status) ));
1997 centry_free(centry);
1998 return status;
2000 do_query:
2001 (*num_domains) = 0;
2002 (*dom_sids) = NULL;
2003 (*names) = NULL;
2004 (*alt_names) = NULL;
2006 /* Return status value returned by seq number check */
2008 if (!NT_STATUS_IS_OK(domain->last_status))
2009 return domain->last_status;
2011 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2012 domain->name ));
2014 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2015 names, alt_names, dom_sids);
2017 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2018 * so that the generic centry handling still applies correctly -
2019 * Guenther*/
2021 if (!NT_STATUS_IS_ERR(status)) {
2022 status = NT_STATUS_OK;
2026 #if 0 /* Disabled as we want the trust dom list to be managed by
2027 the main parent and always to make the query. --jerry */
2029 /* and save it */
2030 refresh_sequence_number(domain, False);
2032 centry = centry_start(domain, status);
2033 if (!centry)
2034 goto skip_save;
2036 centry_put_uint32(centry, *num_domains);
2038 for (i=0; i<(*num_domains); i++) {
2039 centry_put_string(centry, (*names)[i]);
2040 centry_put_string(centry, (*alt_names)[i]);
2041 centry_put_sid(centry, &(*dom_sids)[i]);
2044 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2046 centry_free(centry);
2048 skip_save:
2049 #endif
2051 return status;
2054 /* get lockout policy */
2055 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2056 TALLOC_CTX *mem_ctx,
2057 SAM_UNK_INFO_12 *policy){
2058 struct winbind_cache *cache = get_cache(domain);
2059 struct cache_entry *centry = NULL;
2060 NTSTATUS status;
2062 if (!cache->tdb)
2063 goto do_query;
2065 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2067 if (!centry)
2068 goto do_query;
2070 policy->duration = centry_nttime(centry);
2071 policy->reset_count = centry_nttime(centry);
2072 policy->bad_attempt_lockout = centry_uint16(centry);
2074 status = centry->status;
2076 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2077 domain->name, nt_errstr(status) ));
2079 centry_free(centry);
2080 return status;
2082 do_query:
2083 ZERO_STRUCTP(policy);
2085 /* Return status value returned by seq number check */
2087 if (!NT_STATUS_IS_OK(domain->last_status))
2088 return domain->last_status;
2090 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2091 domain->name ));
2093 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2095 /* and save it */
2096 refresh_sequence_number(domain, False);
2097 wcache_save_lockout_policy(domain, status, policy);
2099 return status;
2102 /* get password policy */
2103 static NTSTATUS password_policy(struct winbindd_domain *domain,
2104 TALLOC_CTX *mem_ctx,
2105 SAM_UNK_INFO_1 *policy)
2107 struct winbind_cache *cache = get_cache(domain);
2108 struct cache_entry *centry = NULL;
2109 NTSTATUS status;
2111 if (!cache->tdb)
2112 goto do_query;
2114 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2116 if (!centry)
2117 goto do_query;
2119 policy->min_length_password = centry_uint16(centry);
2120 policy->password_history = centry_uint16(centry);
2121 policy->password_properties = centry_uint32(centry);
2122 policy->expire = centry_nttime(centry);
2123 policy->min_passwordage = centry_nttime(centry);
2125 status = centry->status;
2127 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2128 domain->name, nt_errstr(status) ));
2130 centry_free(centry);
2131 return status;
2133 do_query:
2134 ZERO_STRUCTP(policy);
2136 /* Return status value returned by seq number check */
2138 if (!NT_STATUS_IS_OK(domain->last_status))
2139 return domain->last_status;
2141 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2142 domain->name ));
2144 status = domain->backend->password_policy(domain, mem_ctx, policy);
2146 /* and save it */
2147 refresh_sequence_number(domain, False);
2148 wcache_save_password_policy(domain, status, policy);
2150 return status;
2154 /* Invalidate cached user and group lists coherently */
2156 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2157 void *state)
2159 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2160 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2161 tdb_delete(the_tdb, kbuf);
2163 return 0;
2166 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2168 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2169 NET_USER_INFO_3 *info3)
2171 struct winbind_cache *cache;
2173 /* dont clear cached U/SID and UG/SID entries when we want to logon
2174 * offline - gd */
2176 if (lp_winbind_offline_logon()) {
2177 return;
2180 if (!domain)
2181 return;
2183 cache = get_cache(domain);
2184 netsamlogon_clear_cached_user(cache->tdb, info3);
2187 void wcache_invalidate_cache(void)
2189 struct winbindd_domain *domain;
2191 for (domain = domain_list(); domain; domain = domain->next) {
2192 struct winbind_cache *cache = get_cache(domain);
2194 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2195 "entries for %s\n", domain->name));
2196 if (cache)
2197 tdb_traverse(cache->tdb, traverse_fn, NULL);
2201 BOOL init_wcache(void)
2203 if (wcache == NULL) {
2204 wcache = SMB_XMALLOC_P(struct winbind_cache);
2205 ZERO_STRUCTP(wcache);
2208 if (wcache->tdb != NULL)
2209 return True;
2211 /* when working offline we must not clear the cache on restart */
2212 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2213 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2214 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2215 O_RDWR|O_CREAT, 0600);
2217 if (wcache->tdb == NULL) {
2218 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2219 return False;
2222 return True;
2225 /************************************************************************
2226 This is called by the parent to initialize the cache file.
2227 We don't need sophisticated locking here as we know we're the
2228 only opener.
2229 ************************************************************************/
2231 BOOL initialize_winbindd_cache(void)
2233 BOOL cache_bad = True;
2234 uint32 vers;
2236 if (!init_wcache()) {
2237 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2238 return False;
2241 /* Check version number. */
2242 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2243 vers == WINBINDD_CACHE_VERSION) {
2244 cache_bad = False;
2247 if (cache_bad) {
2248 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2249 "and re-creating with version number %d\n",
2250 WINBINDD_CACHE_VERSION ));
2252 tdb_close(wcache->tdb);
2253 wcache->tdb = NULL;
2255 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2256 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2257 lock_path("winbindd_cache.tdb"),
2258 strerror(errno) ));
2259 return False;
2261 if (!init_wcache()) {
2262 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2263 "init_wcache failed.\n"));
2264 return False;
2267 /* Write the version. */
2268 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2269 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2270 tdb_errorstr(wcache->tdb) ));
2271 return False;
2275 tdb_close(wcache->tdb);
2276 wcache->tdb = NULL;
2277 return True;
2280 void cache_store_response(pid_t pid, struct winbindd_response *response)
2282 fstring key_str;
2284 if (!init_wcache())
2285 return;
2287 DEBUG(10, ("Storing response for pid %d, len %d\n",
2288 pid, response->length));
2290 fstr_sprintf(key_str, "DR/%d", pid);
2291 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2292 make_tdb_data((uint8 *)response, sizeof(*response)),
2293 TDB_REPLACE) == -1)
2294 return;
2296 if (response->length == sizeof(*response))
2297 return;
2299 /* There's extra data */
2301 DEBUG(10, ("Storing extra data: len=%d\n",
2302 (int)(response->length - sizeof(*response))));
2304 fstr_sprintf(key_str, "DE/%d", pid);
2305 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2306 make_tdb_data((uint8 *)response->extra_data.data,
2307 response->length - sizeof(*response)),
2308 TDB_REPLACE) == 0)
2309 return;
2311 /* We could not store the extra data, make sure the tdb does not
2312 * contain a main record with wrong dangling extra data */
2314 fstr_sprintf(key_str, "DR/%d", pid);
2315 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2317 return;
2320 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2322 TDB_DATA data;
2323 fstring key_str;
2325 if (!init_wcache())
2326 return False;
2328 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2330 fstr_sprintf(key_str, "DR/%d", pid);
2331 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2333 if (data.dptr == NULL)
2334 return False;
2336 if (data.dsize != sizeof(*response))
2337 return False;
2339 memcpy(response, data.dptr, data.dsize);
2340 SAFE_FREE(data.dptr);
2342 if (response->length == sizeof(*response)) {
2343 response->extra_data.data = NULL;
2344 return True;
2347 /* There's extra data */
2349 DEBUG(10, ("Retrieving extra data length=%d\n",
2350 (int)(response->length - sizeof(*response))));
2352 fstr_sprintf(key_str, "DE/%d", pid);
2353 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2355 if (data.dptr == NULL) {
2356 DEBUG(0, ("Did not find extra data\n"));
2357 return False;
2360 if (data.dsize != (response->length - sizeof(*response))) {
2361 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2362 SAFE_FREE(data.dptr);
2363 return False;
2366 dump_data(11, (uint8 *)data.dptr, data.dsize);
2368 response->extra_data.data = data.dptr;
2369 return True;
2372 void cache_cleanup_response(pid_t pid)
2374 fstring key_str;
2376 if (!init_wcache())
2377 return;
2379 fstr_sprintf(key_str, "DR/%d", pid);
2380 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2382 fstr_sprintf(key_str, "DE/%d", pid);
2383 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2385 return;
2389 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2390 char **domain_name, char **name,
2391 enum lsa_SidType *type)
2393 struct winbindd_domain *domain;
2394 struct winbind_cache *cache;
2395 struct cache_entry *centry = NULL;
2396 NTSTATUS status;
2398 domain = find_lookup_domain_from_sid(sid);
2399 if (domain == NULL) {
2400 return False;
2403 cache = get_cache(domain);
2405 if (cache->tdb == NULL) {
2406 return False;
2409 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2410 if (centry == NULL) {
2411 return False;
2414 if (NT_STATUS_IS_OK(centry->status)) {
2415 *type = (enum lsa_SidType)centry_uint32(centry);
2416 *domain_name = centry_string(centry, mem_ctx);
2417 *name = centry_string(centry, mem_ctx);
2420 status = centry->status;
2421 centry_free(centry);
2422 return NT_STATUS_IS_OK(status);
2425 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2426 const char *domain_name,
2427 const char *name,
2428 DOM_SID *sid,
2429 enum lsa_SidType *type)
2431 struct winbindd_domain *domain;
2432 struct winbind_cache *cache;
2433 struct cache_entry *centry = NULL;
2434 NTSTATUS status;
2435 fstring uname;
2436 BOOL original_online_state;
2438 domain = find_lookup_domain_from_name(domain_name);
2439 if (domain == NULL) {
2440 return False;
2443 cache = get_cache(domain);
2445 if (cache->tdb == NULL) {
2446 return False;
2449 fstrcpy(uname, name);
2450 strupper_m(uname);
2452 /* If we are doing a cached logon, temporarily set the domain
2453 offline so the cache won't expire the entry */
2455 original_online_state = domain->online;
2456 domain->online = False;
2457 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2458 domain->online = original_online_state;
2460 if (centry == NULL) {
2461 return False;
2464 if (NT_STATUS_IS_OK(centry->status)) {
2465 *type = (enum lsa_SidType)centry_uint32(centry);
2466 centry_sid(centry, mem_ctx, sid);
2469 status = centry->status;
2470 centry_free(centry);
2472 return NT_STATUS_IS_OK(status);
2475 void cache_name2sid(struct winbindd_domain *domain,
2476 const char *domain_name, const char *name,
2477 enum lsa_SidType type, const DOM_SID *sid)
2479 refresh_sequence_number(domain, False);
2480 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2481 sid, type);
2485 * The original idea that this cache only contains centries has
2486 * been blurred - now other stuff gets put in here. Ensure we
2487 * ignore these things on cleanup.
2490 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2491 TDB_DATA dbuf, void *state)
2493 struct cache_entry *centry;
2495 if (is_non_centry_key(kbuf)) {
2496 return 0;
2499 centry = wcache_fetch_raw((char *)kbuf.dptr);
2500 if (!centry) {
2501 return 0;
2504 if (!NT_STATUS_IS_OK(centry->status)) {
2505 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2506 tdb_delete(the_tdb, kbuf);
2509 centry_free(centry);
2510 return 0;
2513 /* flush the cache */
2514 void wcache_flush_cache(void)
2516 if (!wcache)
2517 return;
2518 if (wcache->tdb) {
2519 tdb_close(wcache->tdb);
2520 wcache->tdb = NULL;
2522 if (opt_nocache)
2523 return;
2525 /* when working offline we must not clear the cache on restart */
2526 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2527 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2528 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2529 O_RDWR|O_CREAT, 0600);
2531 if (!wcache->tdb) {
2532 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2533 return;
2536 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2538 DEBUG(10,("wcache_flush_cache success\n"));
2541 /* Count cached creds */
2543 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2544 void *state)
2546 int *cred_count = (int*)state;
2548 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2549 (*cred_count)++;
2551 return 0;
2554 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2556 struct winbind_cache *cache = get_cache(domain);
2558 *count = 0;
2560 if (!cache->tdb) {
2561 return NT_STATUS_INTERNAL_DB_ERROR;
2564 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2566 return NT_STATUS_OK;
2569 struct cred_list {
2570 struct cred_list *prev, *next;
2571 TDB_DATA key;
2572 fstring name;
2573 time_t created;
2575 static struct cred_list *wcache_cred_list;
2577 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2578 void *state)
2580 struct cred_list *cred;
2582 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2584 cred = SMB_MALLOC_P(struct cred_list);
2585 if (cred == NULL) {
2586 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2587 return -1;
2590 ZERO_STRUCTP(cred);
2592 /* save a copy of the key */
2594 fstrcpy(cred->name, (const char *)kbuf.dptr);
2595 DLIST_ADD(wcache_cred_list, cred);
2598 return 0;
2601 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2603 struct winbind_cache *cache = get_cache(domain);
2604 NTSTATUS status;
2605 int ret;
2606 struct cred_list *cred, *oldest = NULL;
2608 if (!cache->tdb) {
2609 return NT_STATUS_INTERNAL_DB_ERROR;
2612 /* we possibly already have an entry */
2613 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2615 fstring key_str;
2617 DEBUG(11,("we already have an entry, deleting that\n"));
2619 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2621 tdb_delete(cache->tdb, string_tdb_data(key_str));
2623 return NT_STATUS_OK;
2626 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2627 if (ret == 0) {
2628 return NT_STATUS_OK;
2629 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2630 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2633 ZERO_STRUCTP(oldest);
2635 for (cred = wcache_cred_list; cred; cred = cred->next) {
2637 TDB_DATA data;
2638 time_t t;
2640 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2641 if (!data.dptr) {
2642 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2643 cred->name));
2644 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2645 goto done;
2648 t = IVAL(data.dptr, 0);
2649 SAFE_FREE(data.dptr);
2651 if (!oldest) {
2652 oldest = SMB_MALLOC_P(struct cred_list);
2653 if (oldest == NULL) {
2654 status = NT_STATUS_NO_MEMORY;
2655 goto done;
2658 fstrcpy(oldest->name, cred->name);
2659 oldest->created = t;
2660 continue;
2663 if (t < oldest->created) {
2664 fstrcpy(oldest->name, cred->name);
2665 oldest->created = t;
2669 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2670 status = NT_STATUS_OK;
2671 } else {
2672 status = NT_STATUS_UNSUCCESSFUL;
2674 done:
2675 SAFE_FREE(wcache_cred_list);
2676 SAFE_FREE(oldest);
2678 return status;
2681 /* Change the global online/offline state. */
2682 BOOL set_global_winbindd_state_offline(void)
2684 TDB_DATA data;
2686 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2688 /* Only go offline if someone has created
2689 the key "WINBINDD_OFFLINE" in the cache tdb. */
2691 if (wcache == NULL || wcache->tdb == NULL) {
2692 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2693 return False;
2696 if (!lp_winbind_offline_logon()) {
2697 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2698 return False;
2701 if (global_winbindd_offline_state) {
2702 /* Already offline. */
2703 return True;
2706 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2708 if (!data.dptr || data.dsize != 4) {
2709 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2710 SAFE_FREE(data.dptr);
2711 return False;
2712 } else {
2713 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2714 global_winbindd_offline_state = True;
2715 SAFE_FREE(data.dptr);
2716 return True;
2720 void set_global_winbindd_state_online(void)
2722 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2724 if (!lp_winbind_offline_logon()) {
2725 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2726 return;
2729 if (!global_winbindd_offline_state) {
2730 /* Already online. */
2731 return;
2733 global_winbindd_offline_state = False;
2735 if (!wcache->tdb) {
2736 return;
2739 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2740 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2743 BOOL get_global_winbindd_state_offline(void)
2745 return global_winbindd_offline_state;
2748 /***********************************************************************
2749 Validate functions for all possible cache tdb keys.
2750 ***********************************************************************/
2752 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2753 struct tdb_validation_status *state)
2755 struct cache_entry *centry;
2757 centry = SMB_XMALLOC_P(struct cache_entry);
2758 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2759 if (!centry->data) {
2760 SAFE_FREE(centry);
2761 return NULL;
2763 centry->len = data.dsize;
2764 centry->ofs = 0;
2766 if (centry->len < 8) {
2767 /* huh? corrupt cache? */
2768 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2769 centry_free(centry);
2770 state->bad_entry = True;
2771 state->success = False;
2772 return NULL;
2775 centry->status = NT_STATUS(centry_uint32(centry));
2776 centry->sequence_number = centry_uint32(centry);
2777 return centry;
2780 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2781 struct tdb_validation_status *state)
2783 if (dbuf.dsize != 8) {
2784 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2785 keystr, (unsigned int)dbuf.dsize ));
2786 state->bad_entry = True;
2787 return 1;
2789 return 0;
2792 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2793 struct tdb_validation_status *state)
2795 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2796 if (!centry) {
2797 return 1;
2800 (void)centry_uint32(centry);
2801 if (NT_STATUS_IS_OK(centry->status)) {
2802 DOM_SID sid;
2803 (void)centry_sid(centry, mem_ctx, &sid);
2806 centry_free(centry);
2808 if (!(state->success)) {
2809 return 1;
2811 DEBUG(10,("validate_ns: %s ok\n", keystr));
2812 return 0;
2815 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2816 struct tdb_validation_status *state)
2818 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2819 if (!centry) {
2820 return 1;
2823 if (NT_STATUS_IS_OK(centry->status)) {
2824 (void)centry_uint32(centry);
2825 (void)centry_string(centry, mem_ctx);
2826 (void)centry_string(centry, mem_ctx);
2829 centry_free(centry);
2831 if (!(state->success)) {
2832 return 1;
2834 DEBUG(10,("validate_sn: %s ok\n", keystr));
2835 return 0;
2838 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2839 struct tdb_validation_status *state)
2841 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2842 DOM_SID sid;
2844 if (!centry) {
2845 return 1;
2848 (void)centry_string(centry, mem_ctx);
2849 (void)centry_string(centry, mem_ctx);
2850 (void)centry_string(centry, mem_ctx);
2851 (void)centry_string(centry, mem_ctx);
2852 (void)centry_uint32(centry);
2853 (void)centry_sid(centry, mem_ctx, &sid);
2854 (void)centry_sid(centry, mem_ctx, &sid);
2856 centry_free(centry);
2858 if (!(state->success)) {
2859 return 1;
2861 DEBUG(10,("validate_u: %s ok\n", keystr));
2862 return 0;
2865 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2866 struct tdb_validation_status *state)
2868 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2870 if (!centry) {
2871 return 1;
2874 (void)centry_nttime(centry);
2875 (void)centry_nttime(centry);
2876 (void)centry_uint16(centry);
2878 centry_free(centry);
2880 if (!(state->success)) {
2881 return 1;
2883 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2884 return 0;
2887 static int validate_pwd_pol(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);
2892 if (!centry) {
2893 return 1;
2896 (void)centry_uint16(centry);
2897 (void)centry_uint16(centry);
2898 (void)centry_uint32(centry);
2899 (void)centry_nttime(centry);
2900 (void)centry_nttime(centry);
2902 centry_free(centry);
2904 if (!(state->success)) {
2905 return 1;
2907 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
2908 return 0;
2911 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2912 struct tdb_validation_status *state)
2914 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2916 if (!centry) {
2917 return 1;
2920 (void)centry_time(centry);
2921 (void)centry_hash16(centry, mem_ctx);
2923 /* We only have 17 bytes more data in the salted cred case. */
2924 if (centry->len - centry->ofs == 17) {
2925 (void)centry_hash16(centry, mem_ctx);
2928 centry_free(centry);
2930 if (!(state->success)) {
2931 return 1;
2933 DEBUG(10,("validate_cred: %s ok\n", keystr));
2934 return 0;
2937 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2938 struct tdb_validation_status *state)
2940 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2941 int32 num_entries, i;
2943 if (!centry) {
2944 return 1;
2947 num_entries = (int32)centry_uint32(centry);
2949 for (i=0; i< num_entries; i++) {
2950 DOM_SID sid;
2951 (void)centry_string(centry, mem_ctx);
2952 (void)centry_string(centry, mem_ctx);
2953 (void)centry_string(centry, mem_ctx);
2954 (void)centry_string(centry, mem_ctx);
2955 (void)centry_sid(centry, mem_ctx, &sid);
2956 (void)centry_sid(centry, mem_ctx, &sid);
2959 centry_free(centry);
2961 if (!(state->success)) {
2962 return 1;
2964 DEBUG(10,("validate_ul: %s ok\n", keystr));
2965 return 0;
2968 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2969 struct tdb_validation_status *state)
2971 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2972 int32 num_entries, i;
2974 if (!centry) {
2975 return 1;
2978 num_entries = centry_uint32(centry);
2980 for (i=0; i< num_entries; i++) {
2981 (void)centry_string(centry, mem_ctx);
2982 (void)centry_string(centry, mem_ctx);
2983 (void)centry_uint32(centry);
2986 centry_free(centry);
2988 if (!(state->success)) {
2989 return 1;
2991 DEBUG(10,("validate_gl: %s ok\n", keystr));
2992 return 0;
2995 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2996 struct tdb_validation_status *state)
2998 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2999 int32 num_groups, i;
3001 if (!centry) {
3002 return 1;
3005 num_groups = centry_uint32(centry);
3007 for (i=0; i< num_groups; i++) {
3008 DOM_SID sid;
3009 centry_sid(centry, mem_ctx, &sid);
3012 centry_free(centry);
3014 if (!(state->success)) {
3015 return 1;
3017 DEBUG(10,("validate_ug: %s ok\n", keystr));
3018 return 0;
3021 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3022 struct tdb_validation_status *state)
3024 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3025 int32 num_aliases, i;
3027 if (!centry) {
3028 return 1;
3031 num_aliases = centry_uint32(centry);
3033 for (i=0; i < num_aliases; i++) {
3034 (void)centry_uint32(centry);
3037 centry_free(centry);
3039 if (!(state->success)) {
3040 return 1;
3042 DEBUG(10,("validate_ua: %s ok\n", keystr));
3043 return 0;
3046 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3047 struct tdb_validation_status *state)
3049 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3050 int32 num_names, i;
3052 if (!centry) {
3053 return 1;
3056 num_names = centry_uint32(centry);
3058 for (i=0; i< num_names; i++) {
3059 DOM_SID sid;
3060 centry_sid(centry, mem_ctx, &sid);
3061 (void)centry_string(centry, mem_ctx);
3062 (void)centry_uint32(centry);
3065 centry_free(centry);
3067 if (!(state->success)) {
3068 return 1;
3070 DEBUG(10,("validate_gm: %s ok\n", keystr));
3071 return 0;
3074 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3075 struct tdb_validation_status *state)
3077 /* Can't say anything about this other than must be nonzero. */
3078 if (dbuf.dsize == 0) {
3079 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3080 keystr));
3081 state->bad_entry = True;
3082 state->success = False;
3083 return 1;
3086 DEBUG(10,("validate_dr: %s ok\n", keystr));
3087 return 0;
3090 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3091 struct tdb_validation_status *state)
3093 /* Can't say anything about this other than must be nonzero. */
3094 if (dbuf.dsize == 0) {
3095 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3096 keystr));
3097 state->bad_entry = True;
3098 state->success = False;
3099 return 1;
3102 DEBUG(10,("validate_de: %s ok\n", keystr));
3103 return 0;
3106 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3107 struct tdb_validation_status *state)
3109 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3110 int32 num_domains, i;
3112 if (!centry) {
3113 return 1;
3116 num_domains = centry_uint32(centry);
3118 for (i=0; i< num_domains; i++) {
3119 DOM_SID sid;
3120 (void)centry_string(centry, mem_ctx);
3121 (void)centry_string(centry, mem_ctx);
3122 (void)centry_sid(centry, mem_ctx, &sid);
3125 centry_free(centry);
3127 if (!(state->success)) {
3128 return 1;
3130 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3131 return 0;
3134 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3135 TDB_DATA dbuf,
3136 struct tdb_validation_status *state)
3138 if (dbuf.dsize == 0) {
3139 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3140 "key %s (len ==0) ?\n", keystr));
3141 state->bad_entry = True;
3142 state->success = False;
3143 return 1;
3146 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3147 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3148 return 0;
3151 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3152 struct tdb_validation_status *state)
3154 if (dbuf.dsize != 4) {
3155 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3156 keystr, (unsigned int)dbuf.dsize ));
3157 state->bad_entry = True;
3158 state->success = False;
3159 return 1;
3161 DEBUG(10,("validate_offline: %s ok\n", keystr));
3162 return 0;
3165 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3166 struct tdb_validation_status *state)
3168 if (dbuf.dsize != 4) {
3169 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3170 "key %s (len %u != 4) ?\n",
3171 keystr, (unsigned int)dbuf.dsize));
3172 state->bad_entry = True;
3173 state->success = False;
3174 return 1;
3177 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3178 return 0;
3181 /***********************************************************************
3182 A list of all possible cache tdb keys with associated validation
3183 functions.
3184 ***********************************************************************/
3186 struct key_val_struct {
3187 const char *keyname;
3188 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3189 } key_val[] = {
3190 {"SEQNUM/", validate_seqnum},
3191 {"NS/", validate_ns},
3192 {"SN/", validate_sn},
3193 {"U/", validate_u},
3194 {"LOC_POL/", validate_loc_pol},
3195 {"PWD_POL/", validate_pwd_pol},
3196 {"CRED/", validate_cred},
3197 {"UL/", validate_ul},
3198 {"GL/", validate_gl},
3199 {"UG/", validate_ug},
3200 {"UA", validate_ua},
3201 {"GM/", validate_gm},
3202 {"DR/", validate_dr},
3203 {"DE/", validate_de},
3204 {"TRUSTDOMS/", validate_trustdoms},
3205 {"TRUSTDOMCACHE/", validate_trustdomcache},
3206 {"WINBINDD_OFFLINE", validate_offline},
3207 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3208 {NULL, NULL}
3211 /***********************************************************************
3212 Function to look at every entry in the tdb and validate it as far as
3213 possible.
3214 ***********************************************************************/
3216 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3218 int i;
3219 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3221 /* Paranoia check. */
3222 if (kbuf.dsize > 1024) {
3223 DEBUG(0,("cache_traverse_validate_fn: key length too large (%u) > 1024\n\n",
3224 (unsigned int)kbuf.dsize ));
3225 return 1;
3228 for (i = 0; key_val[i].keyname; i++) {
3229 size_t namelen = strlen(key_val[i].keyname);
3230 if (kbuf.dsize >= namelen && (
3231 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3232 TALLOC_CTX *mem_ctx;
3233 char *keystr;
3234 int ret;
3236 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3237 if (!keystr) {
3238 return 1;
3240 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3241 keystr[kbuf.dsize] = '\0';
3243 mem_ctx = talloc_init("validate_ctx");
3244 if (!mem_ctx) {
3245 SAFE_FREE(keystr);
3246 return 1;
3249 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3250 v_state);
3252 SAFE_FREE(keystr);
3253 talloc_destroy(mem_ctx);
3254 return ret;
3258 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3259 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3260 DEBUG(0,("data :\n"));
3261 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3262 v_state->unknown_key = True;
3263 v_state->success = False;
3264 return 1; /* terminate. */
3267 static void validate_panic(const char *const why)
3269 DEBUG(0,("validating cache: would panic %s\n", why ));
3270 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3271 exit(47);
3274 /***********************************************************************
3275 Try and validate every entry in the winbindd cache. If we fail here,
3276 delete the cache tdb and return non-zero - the caller (main winbindd
3277 function) will restart us as we don't know if we crashed or not.
3278 ***********************************************************************/
3280 int winbindd_validate_cache(void)
3282 int ret;
3284 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3285 smb_panic_fn = validate_panic;
3287 ret = tdb_validate(lock_path("winbindd_cache.tdb"),
3288 cache_traverse_validate_fn);
3290 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3291 smb_panic_fn = smb_panic;
3292 return ret;
3295 /*********************************************************************
3296 ********************************************************************/
3298 static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3299 struct winbindd_tdc_domain **domains,
3300 size_t *num_domains )
3302 struct winbindd_tdc_domain *list = NULL;
3303 size_t idx;
3304 int i;
3305 BOOL set_only = False;
3307 /* don't allow duplicates */
3309 idx = *num_domains;
3310 list = *domains;
3312 for ( i=0; i< (*num_domains); i++ ) {
3313 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3314 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3315 new_dom->name));
3316 idx = i;
3317 set_only = True;
3319 break;
3323 if ( !set_only ) {
3324 if ( !*domains ) {
3325 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3326 idx = 0;
3327 } else {
3328 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3329 struct winbindd_tdc_domain,
3330 (*num_domains)+1);
3331 idx = *num_domains;
3334 ZERO_STRUCT( list[idx] );
3337 if ( !list )
3338 return False;
3340 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3341 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3343 if ( !is_null_sid( &new_dom->sid ) )
3344 sid_copy( &list[idx].sid, &new_dom->sid );
3346 if ( new_dom->domain_flags != 0x0 )
3347 list[idx].trust_flags = new_dom->domain_flags;
3349 if ( new_dom->domain_type != 0x0 )
3350 list[idx].trust_type = new_dom->domain_type;
3352 if ( new_dom->domain_trust_attribs != 0x0 )
3353 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3355 if ( !set_only ) {
3356 *domains = list;
3357 *num_domains = idx + 1;
3360 return True;
3363 /*********************************************************************
3364 ********************************************************************/
3366 static TDB_DATA make_tdc_key( const char *domain_name )
3368 char *keystr = NULL;
3369 TDB_DATA key = { NULL, 0 };
3371 if ( !domain_name ) {
3372 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3373 return key;
3377 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3378 key = string_term_tdb_data(keystr);
3380 return key;
3383 /*********************************************************************
3384 ********************************************************************/
3386 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3387 size_t num_domains,
3388 unsigned char **buf )
3390 unsigned char *buffer = NULL;
3391 int len = 0;
3392 int buflen = 0;
3393 int i = 0;
3395 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3396 (int)num_domains));
3398 buflen = 0;
3400 again:
3401 len = 0;
3403 /* Store the number of array items first */
3404 len += tdb_pack( buffer+len, buflen-len, "d",
3405 num_domains );
3407 /* now pack each domain trust record */
3408 for ( i=0; i<num_domains; i++ ) {
3410 if ( buflen > 0 ) {
3411 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3412 domains[i].domain_name,
3413 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3416 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3417 domains[i].domain_name,
3418 domains[i].dns_name,
3419 sid_string_static(&domains[i].sid),
3420 domains[i].trust_flags,
3421 domains[i].trust_attribs,
3422 domains[i].trust_type );
3425 if ( buflen < len ) {
3426 SAFE_FREE(buffer);
3427 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3428 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3429 buflen = -1;
3430 goto done;
3432 buflen = len;
3433 goto again;
3436 *buf = buffer;
3438 done:
3439 return buflen;
3442 /*********************************************************************
3443 ********************************************************************/
3445 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3446 struct winbindd_tdc_domain **domains )
3448 fstring domain_name, dns_name, sid_string;
3449 uint32 type, attribs, flags;
3450 int num_domains;
3451 int len = 0;
3452 int i;
3453 struct winbindd_tdc_domain *list = NULL;
3455 /* get the number of domains */
3456 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3457 if ( len == -1 ) {
3458 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3459 return 0;
3462 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3463 if ( !list ) {
3464 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3465 return 0;
3468 for ( i=0; i<num_domains; i++ ) {
3469 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3470 domain_name,
3471 dns_name,
3472 sid_string,
3473 &flags,
3474 &attribs,
3475 &type );
3477 if ( len == -1 ) {
3478 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3479 TALLOC_FREE( list );
3480 return 0;
3483 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3484 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3485 domain_name, dns_name, sid_string,
3486 flags, attribs, type));
3488 list[i].domain_name = talloc_strdup( list, domain_name );
3489 list[i].dns_name = talloc_strdup( list, dns_name );
3490 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3491 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3492 domain_name));
3494 list[i].trust_flags = flags;
3495 list[i].trust_attribs = attribs;
3496 list[i].trust_type = type;
3499 *domains = list;
3501 return num_domains;
3504 /*********************************************************************
3505 ********************************************************************/
3507 static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3509 TDB_DATA key = make_tdc_key( lp_workgroup() );
3510 TDB_DATA data = { NULL, 0 };
3511 int ret;
3513 if ( !key.dptr )
3514 return False;
3516 /* See if we were asked to delete the cache entry */
3518 if ( !domains ) {
3519 ret = tdb_delete( wcache->tdb, key );
3520 goto done;
3523 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3525 if ( !data.dptr ) {
3526 ret = -1;
3527 goto done;
3530 ret = tdb_store( wcache->tdb, key, data, 0 );
3532 done:
3533 SAFE_FREE( data.dptr );
3534 SAFE_FREE( key.dptr );
3536 return ( ret != -1 );
3539 /*********************************************************************
3540 ********************************************************************/
3542 BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3544 TDB_DATA key = make_tdc_key( lp_workgroup() );
3545 TDB_DATA data = { NULL, 0 };
3547 *domains = NULL;
3548 *num_domains = 0;
3550 if ( !key.dptr )
3551 return False;
3553 data = tdb_fetch( wcache->tdb, key );
3555 SAFE_FREE( key.dptr );
3557 if ( !data.dptr )
3558 return False;
3560 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3562 SAFE_FREE( data.dptr );
3564 if ( !*domains )
3565 return False;
3567 return True;
3570 /*********************************************************************
3571 ********************************************************************/
3573 BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
3575 struct winbindd_tdc_domain *dom_list = NULL;
3576 size_t num_domains = 0;
3577 BOOL ret = False;
3579 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3580 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3581 domain->name, domain->alt_name,
3582 sid_string_static(&domain->sid),
3583 domain->domain_flags,
3584 domain->domain_trust_attribs,
3585 domain->domain_type));
3587 if ( !init_wcache() ) {
3588 return False;
3591 /* fetch the list */
3593 wcache_tdc_fetch_list( &dom_list, &num_domains );
3595 /* add the new domain */
3597 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3598 goto done;
3601 /* pack the domain */
3603 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3604 goto done;
3607 /* Success */
3609 ret = True;
3610 done:
3611 TALLOC_FREE( dom_list );
3613 return ret;
3616 /*********************************************************************
3617 ********************************************************************/
3619 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3621 struct winbindd_tdc_domain *dom_list = NULL;
3622 size_t num_domains = 0;
3623 int i;
3624 struct winbindd_tdc_domain *d = NULL;
3626 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3628 if ( !init_wcache() ) {
3629 return False;
3632 /* fetch the list */
3634 wcache_tdc_fetch_list( &dom_list, &num_domains );
3636 for ( i=0; i<num_domains; i++ ) {
3637 if ( strequal(name, dom_list[i].domain_name) ||
3638 strequal(name, dom_list[i].dns_name) )
3640 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3641 name));
3643 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3644 if ( !d )
3645 break;
3647 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3648 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3649 sid_copy( &d->sid, &dom_list[i].sid );
3650 d->trust_flags = dom_list[i].trust_flags;
3651 d->trust_type = dom_list[i].trust_type;
3652 d->trust_attribs = dom_list[i].trust_attribs;
3654 break;
3658 TALLOC_FREE( dom_list );
3660 return d;
3664 /*********************************************************************
3665 ********************************************************************/
3667 void wcache_tdc_clear( void )
3669 if ( !init_wcache() )
3670 return;
3672 wcache_tdc_store_list( NULL, 0 );
3674 return;
3678 /*********************************************************************
3679 ********************************************************************/
3681 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3682 NTSTATUS status,
3683 const DOM_SID *user_sid,
3684 const char *homedir,
3685 const char *shell,
3686 const char *gecos,
3687 uint32 gid)
3689 struct cache_entry *centry;
3691 if ( (centry = centry_start(domain, status)) == NULL )
3692 return;
3694 centry_put_string( centry, homedir );
3695 centry_put_string( centry, shell );
3696 centry_put_string( centry, gecos );
3697 centry_put_uint32( centry, gid );
3699 centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
3701 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
3703 centry_free(centry);
3706 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3707 const DOM_SID *user_sid,
3708 TALLOC_CTX *ctx,
3709 ADS_STRUCT *ads, LDAPMessage *msg,
3710 char **homedir, char **shell, char **gecos,
3711 gid_t *p_gid)
3713 struct winbind_cache *cache = get_cache(domain);
3714 struct cache_entry *centry = NULL;
3715 NTSTATUS nt_status;
3717 if (!cache->tdb)
3718 goto do_query;
3720 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));
3722 if (!centry)
3723 goto do_query;
3725 *homedir = centry_string( centry, ctx );
3726 *shell = centry_string( centry, ctx );
3727 *gecos = centry_string( centry, ctx );
3728 *p_gid = centry_uint32( centry );
3730 centry_free(centry);
3732 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3733 sid_string_static(user_sid)));
3735 return NT_STATUS_OK;
3737 do_query:
3739 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3740 homedir, shell, gecos, p_gid );
3742 if ( NT_STATUS_IS_OK(nt_status) ) {
3743 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3744 *homedir, *shell, *gecos, *p_gid );
3747 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3748 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3749 domain->name ));
3750 set_domain_offline( domain );
3753 return nt_status;
3757 /* the cache backend methods are exposed via this structure */
3758 struct winbindd_methods cache_methods = {
3759 True,
3760 query_user_list,
3761 enum_dom_groups,
3762 enum_local_groups,
3763 name_to_sid,
3764 sid_to_name,
3765 rids_to_names,
3766 query_user,
3767 lookup_usergroups,
3768 lookup_useraliases,
3769 lookup_groupmem,
3770 sequence_number,
3771 lockout_policy,
3772 password_policy,
3773 trusted_domains