[GLUE] Rsync SAMBA_3_0 SVN r25598 in order to create the v3-0-test branch.
[Samba.git] / source / nsswitch / winbindd_cache.c
bloba88a061686c7fb06432cfa645269ed33f4ad4b3b
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "includes.h"
27 #include "winbindd.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods;
36 extern BOOL opt_nocache;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
47 static const char *non_centry_keys[] = {
48 "SEQNUM/",
49 "DR/",
50 "DE/",
51 "WINBINDD_OFFLINE",
52 WINBINDD_CACHE_VERSION_KEYSTR,
53 NULL
56 /************************************************************************
57 Is this key a non-centry type ?
58 ************************************************************************/
60 static BOOL is_non_centry_key(TDB_DATA kbuf)
62 int i;
64 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
65 return False;
67 for (i = 0; non_centry_keys[i] != NULL; i++) {
68 size_t namelen = strlen(non_centry_keys[i]);
69 if (kbuf.dsize < namelen) {
70 continue;
72 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
73 return True;
76 return False;
79 /* Global online/offline state - False when online. winbindd starts up online
80 and sets this to true if the first query fails and there's an entry in
81 the cache tdb telling us to stay offline. */
83 static BOOL global_winbindd_offline_state;
85 struct winbind_cache {
86 TDB_CONTEXT *tdb;
89 struct cache_entry {
90 NTSTATUS status;
91 uint32 sequence_number;
92 uint8 *data;
93 uint32 len, ofs;
96 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
98 static struct winbind_cache *wcache;
100 void winbindd_check_cache_size(time_t t)
102 static time_t last_check_time;
103 struct stat st;
105 if (last_check_time == (time_t)0)
106 last_check_time = t;
108 if (t - last_check_time < 60 && t - last_check_time > 0)
109 return;
111 if (wcache == NULL || wcache->tdb == NULL) {
112 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
113 return;
116 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
117 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
118 return;
121 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
122 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
123 (unsigned long)st.st_size,
124 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
125 wcache_flush_cache();
129 /* get the winbind_cache structure */
130 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
132 struct winbind_cache *ret = wcache;
133 #ifdef HAVE_ADS
134 struct winbindd_domain *our_domain = domain;
135 #endif
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 /* find our domain first so we can figure out if we
164 are joined to a kerberized domain */
166 if ( !domain->primary )
167 our_domain = find_our_domain();
169 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
170 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
171 domain->backend = &ads_methods;
172 } else {
173 #endif /* HAVE_ADS */
174 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
175 domain->backend = &reconnect_methods;
176 #ifdef HAVE_ADS
178 #endif /* HAVE_ADS */
181 if (ret)
182 return ret;
184 ret = SMB_XMALLOC_P(struct winbind_cache);
185 ZERO_STRUCTP(ret);
187 wcache = ret;
188 wcache_flush_cache();
190 return ret;
194 free a centry structure
196 static void centry_free(struct cache_entry *centry)
198 if (!centry)
199 return;
200 SAFE_FREE(centry->data);
201 free(centry);
205 pull a uint32 from a cache entry
207 static uint32 centry_uint32(struct cache_entry *centry)
209 uint32 ret;
210 if (centry->len - centry->ofs < 4) {
211 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
212 centry->len - centry->ofs));
213 smb_panic("centry_uint32");
215 ret = IVAL(centry->data, centry->ofs);
216 centry->ofs += 4;
217 return ret;
221 pull a uint16 from a cache entry
223 static uint16 centry_uint16(struct cache_entry *centry)
225 uint16 ret;
226 if (centry->len - centry->ofs < 2) {
227 DEBUG(0,("centry corruption? needed 2 bytes, have %d\n",
228 centry->len - centry->ofs));
229 smb_panic("centry_uint16");
231 ret = CVAL(centry->data, centry->ofs);
232 centry->ofs += 2;
233 return ret;
237 pull a uint8 from a cache entry
239 static uint8 centry_uint8(struct cache_entry *centry)
241 uint8 ret;
242 if (centry->len - centry->ofs < 1) {
243 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
244 centry->len - centry->ofs));
245 smb_panic("centry_uint32");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 1;
249 return ret;
253 pull a NTTIME from a cache entry
255 static NTTIME centry_nttime(struct cache_entry *centry)
257 NTTIME ret;
258 if (centry->len - centry->ofs < 8) {
259 DEBUG(0,("centry corruption? needed 8 bytes, have %d\n",
260 centry->len - centry->ofs));
261 smb_panic("centry_nttime");
263 ret = IVAL(centry->data, centry->ofs);
264 centry->ofs += 4;
265 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
266 centry->ofs += 4;
267 return ret;
271 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
273 static time_t centry_time(struct cache_entry *centry)
275 return (time_t)centry_nttime(centry);
278 /* pull a string from a cache entry, using the supplied
279 talloc context
281 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
283 uint32 len;
284 char *ret;
286 len = centry_uint8(centry);
288 if (len == 0xFF) {
289 /* a deliberate NULL string */
290 return NULL;
293 if (centry->len - centry->ofs < len) {
294 DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
295 len, centry->len - centry->ofs));
296 smb_panic("centry_string");
299 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
300 if (!ret) {
301 smb_panic("centry_string out of memory\n");
303 memcpy(ret,centry->data + centry->ofs, len);
304 ret[len] = 0;
305 centry->ofs += len;
306 return ret;
309 /* pull a hash16 from a cache entry, using the supplied
310 talloc context
312 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
314 uint32 len;
315 char *ret;
317 len = centry_uint8(centry);
319 if (len != 16) {
320 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
321 len ));
322 return NULL;
325 if (centry->len - centry->ofs < 16) {
326 DEBUG(0,("centry corruption? needed 16 bytes, have %d\n",
327 centry->len - centry->ofs));
328 return NULL;
331 ret = TALLOC_ARRAY(mem_ctx, char, 16);
332 if (!ret) {
333 smb_panic("centry_hash out of memory\n");
335 memcpy(ret,centry->data + centry->ofs, 16);
336 centry->ofs += 16;
337 return ret;
340 /* pull a sid from a cache entry, using the supplied
341 talloc context
343 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
345 char *sid_string;
346 sid_string = centry_string(centry, mem_ctx);
347 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
348 return False;
350 return True;
353 /* the server is considered down if it can't give us a sequence number */
354 static BOOL wcache_server_down(struct winbindd_domain *domain)
356 BOOL ret;
358 if (!wcache->tdb)
359 return False;
361 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
363 if (ret)
364 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
365 domain->name ));
366 return ret;
369 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
371 TDB_DATA data;
372 fstring key;
373 uint32 time_diff;
375 if (!wcache->tdb) {
376 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
377 return NT_STATUS_UNSUCCESSFUL;
380 fstr_sprintf( key, "SEQNUM/%s", domain->name );
382 data = tdb_fetch_bystring( wcache->tdb, key );
383 if ( !data.dptr || data.dsize!=8 ) {
384 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
385 return NT_STATUS_UNSUCCESSFUL;
388 domain->sequence_number = IVAL(data.dptr, 0);
389 domain->last_seq_check = IVAL(data.dptr, 4);
391 SAFE_FREE(data.dptr);
393 /* have we expired? */
395 time_diff = now - domain->last_seq_check;
396 if ( time_diff > lp_winbind_cache_time() ) {
397 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
398 domain->name, domain->sequence_number,
399 (uint32)domain->last_seq_check));
400 return NT_STATUS_UNSUCCESSFUL;
403 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
404 domain->name, domain->sequence_number,
405 (uint32)domain->last_seq_check));
407 return NT_STATUS_OK;
410 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
412 TDB_DATA data;
413 fstring key_str;
414 char buf[8];
416 if (!wcache->tdb) {
417 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
418 return NT_STATUS_UNSUCCESSFUL;
421 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
423 SIVAL(buf, 0, domain->sequence_number);
424 SIVAL(buf, 4, domain->last_seq_check);
425 data.dptr = buf;
426 data.dsize = 8;
428 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
429 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
430 return NT_STATUS_UNSUCCESSFUL;
433 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
434 domain->name, domain->sequence_number,
435 (uint32)domain->last_seq_check));
437 return NT_STATUS_OK;
441 refresh the domain sequence number. If force is True
442 then always refresh it, no matter how recently we fetched it
445 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
447 NTSTATUS status;
448 unsigned time_diff;
449 time_t t = time(NULL);
450 unsigned cache_time = lp_winbind_cache_time();
452 get_cache( domain );
454 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
455 /* trying to reconnect is expensive, don't do it too often */
456 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
457 cache_time *= 8;
459 #endif
461 time_diff = t - domain->last_seq_check;
463 /* see if we have to refetch the domain sequence number */
464 if (!force && (time_diff < cache_time)) {
465 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
466 goto done;
469 /* try to get the sequence number from the tdb cache first */
470 /* this will update the timestamp as well */
472 status = fetch_cache_seqnum( domain, t );
473 if ( NT_STATUS_IS_OK(status) )
474 goto done;
476 /* important! make sure that we know if this is a native
477 mode domain or not */
479 status = domain->backend->sequence_number(domain, &domain->sequence_number);
481 /* the above call could have set our domain->backend to NULL when
482 * coming from offline to online mode, make sure to reinitialize the
483 * backend - Guenther */
484 get_cache( domain );
486 if (!NT_STATUS_IS_OK(status)) {
487 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
488 domain->sequence_number = DOM_SEQUENCE_NONE;
491 domain->last_status = status;
492 domain->last_seq_check = time(NULL);
494 /* save the new sequence number ni the cache */
495 store_cache_seqnum( domain );
497 done:
498 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
499 domain->name, domain->sequence_number));
501 return;
505 decide if a cache entry has expired
507 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
509 /* If we've been told to be offline - stay in that state... */
510 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
511 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
512 keystr, domain->name ));
513 return False;
516 /* when the domain is offline return the cached entry.
517 * This deals with transient offline states... */
519 if (!domain->online) {
520 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
521 keystr, domain->name ));
522 return False;
525 /* if the server is OK and our cache entry came from when it was down then
526 the entry is invalid */
527 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
528 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
529 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
530 keystr, domain->name ));
531 return True;
534 /* if the server is down or the cache entry is not older than the
535 current sequence number then it is OK */
536 if (wcache_server_down(domain) ||
537 centry->sequence_number == domain->sequence_number) {
538 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
539 keystr, domain->name ));
540 return False;
543 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
544 keystr, domain->name ));
546 /* it's expired */
547 return True;
550 static struct cache_entry *wcache_fetch_raw(char *kstr)
552 TDB_DATA data;
553 struct cache_entry *centry;
554 TDB_DATA key;
556 key.dptr = kstr;
557 key.dsize = strlen(kstr);
558 data = tdb_fetch(wcache->tdb, key);
559 if (!data.dptr) {
560 /* a cache miss */
561 return NULL;
564 centry = SMB_XMALLOC_P(struct cache_entry);
565 centry->data = (unsigned char *)data.dptr;
566 centry->len = data.dsize;
567 centry->ofs = 0;
569 if (centry->len < 8) {
570 /* huh? corrupt cache? */
571 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
572 centry_free(centry);
573 return NULL;
576 centry->status = NT_STATUS(centry_uint32(centry));
577 centry->sequence_number = centry_uint32(centry);
579 return centry;
583 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
584 number and return status
586 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
587 struct winbindd_domain *domain,
588 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
589 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
590 struct winbindd_domain *domain,
591 const char *format, ...)
593 va_list ap;
594 char *kstr;
595 struct cache_entry *centry;
597 if (opt_nocache) {
598 return NULL;
601 refresh_sequence_number(domain, False);
603 va_start(ap, format);
604 smb_xvasprintf(&kstr, format, ap);
605 va_end(ap);
607 centry = wcache_fetch_raw(kstr);
608 if (centry == NULL) {
609 free(kstr);
610 return NULL;
613 if (centry_expired(domain, kstr, centry)) {
615 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
616 kstr, domain->name ));
618 centry_free(centry);
619 free(kstr);
620 return NULL;
623 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
624 kstr, domain->name ));
626 free(kstr);
627 return centry;
630 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
631 static void wcache_delete(const char *format, ...)
633 va_list ap;
634 char *kstr;
635 TDB_DATA key;
637 va_start(ap, format);
638 smb_xvasprintf(&kstr, format, ap);
639 va_end(ap);
641 key.dptr = kstr;
642 key.dsize = strlen(kstr);
644 tdb_delete(wcache->tdb, key);
645 free(kstr);
649 make sure we have at least len bytes available in a centry
651 static void centry_expand(struct cache_entry *centry, uint32 len)
653 if (centry->len - centry->ofs >= len)
654 return;
655 centry->len *= 2;
656 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
657 centry->len);
658 if (!centry->data) {
659 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
660 smb_panic("out of memory in centry_expand");
665 push a uint32 into a centry
667 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
669 centry_expand(centry, 4);
670 SIVAL(centry->data, centry->ofs, v);
671 centry->ofs += 4;
675 push a uint16 into a centry
677 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
679 centry_expand(centry, 2);
680 SIVAL(centry->data, centry->ofs, v);
681 centry->ofs += 2;
685 push a uint8 into a centry
687 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
689 centry_expand(centry, 1);
690 SCVAL(centry->data, centry->ofs, v);
691 centry->ofs += 1;
695 push a string into a centry
697 static void centry_put_string(struct cache_entry *centry, const char *s)
699 int len;
701 if (!s) {
702 /* null strings are marked as len 0xFFFF */
703 centry_put_uint8(centry, 0xFF);
704 return;
707 len = strlen(s);
708 /* can't handle more than 254 char strings. Truncating is probably best */
709 if (len > 254) {
710 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
711 len = 254;
713 centry_put_uint8(centry, len);
714 centry_expand(centry, len);
715 memcpy(centry->data + centry->ofs, s, len);
716 centry->ofs += len;
720 push a 16 byte hash into a centry - treat as 16 byte string.
722 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
724 centry_put_uint8(centry, 16);
725 centry_expand(centry, 16);
726 memcpy(centry->data + centry->ofs, val, 16);
727 centry->ofs += 16;
730 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
732 fstring sid_string;
733 centry_put_string(centry, sid_to_string(sid_string, sid));
737 push a NTTIME into a centry
739 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
741 centry_expand(centry, 8);
742 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
743 centry->ofs += 4;
744 SIVAL(centry->data, centry->ofs, nt >> 32);
745 centry->ofs += 4;
749 push a time_t into a centry - use a 64 bit size.
750 NTTIME here is being used as a convenient 64-bit size.
752 static void centry_put_time(struct cache_entry *centry, time_t t)
754 NTTIME nt = (NTTIME)t;
755 centry_put_nttime(centry, nt);
759 start a centry for output. When finished, call centry_end()
761 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
763 struct cache_entry *centry;
765 if (!wcache->tdb)
766 return NULL;
768 centry = SMB_XMALLOC_P(struct cache_entry);
770 centry->len = 8192; /* reasonable default */
771 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
772 centry->ofs = 0;
773 centry->sequence_number = domain->sequence_number;
774 centry_put_uint32(centry, NT_STATUS_V(status));
775 centry_put_uint32(centry, centry->sequence_number);
776 return centry;
780 finish a centry and write it to the tdb
782 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
783 static void centry_end(struct cache_entry *centry, const char *format, ...)
785 va_list ap;
786 char *kstr;
787 TDB_DATA key, data;
789 va_start(ap, format);
790 smb_xvasprintf(&kstr, format, ap);
791 va_end(ap);
793 key.dptr = kstr;
794 key.dsize = strlen(kstr);
795 data.dptr = (char *)centry->data;
796 data.dsize = centry->ofs;
798 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
799 free(kstr);
802 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
803 NTSTATUS status, const char *domain_name,
804 const char *name, const DOM_SID *sid,
805 enum lsa_SidType type)
807 struct cache_entry *centry;
808 fstring uname;
810 centry = centry_start(domain, status);
811 if (!centry)
812 return;
813 centry_put_uint32(centry, type);
814 centry_put_sid(centry, sid);
815 fstrcpy(uname, name);
816 strupper_m(uname);
817 centry_end(centry, "NS/%s/%s", domain_name, uname);
818 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
819 sid_string_static(sid)));
820 centry_free(centry);
823 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
824 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
826 struct cache_entry *centry;
827 fstring sid_string;
829 if (is_null_sid(sid)) {
830 return;
833 centry = centry_start(domain, status);
834 if (!centry)
835 return;
836 if (NT_STATUS_IS_OK(status)) {
837 centry_put_uint32(centry, type);
838 centry_put_string(centry, domain_name);
839 centry_put_string(centry, name);
841 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
842 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
843 centry_free(centry);
847 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
849 struct cache_entry *centry;
850 fstring sid_string;
852 if (is_null_sid(&info->user_sid)) {
853 return;
856 centry = centry_start(domain, status);
857 if (!centry)
858 return;
859 centry_put_string(centry, info->acct_name);
860 centry_put_string(centry, info->full_name);
861 centry_put_string(centry, info->homedir);
862 centry_put_string(centry, info->shell);
863 centry_put_uint32(centry, info->primary_gid);
864 centry_put_sid(centry, &info->user_sid);
865 centry_put_sid(centry, &info->group_sid);
866 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
867 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
868 centry_free(centry);
871 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
873 struct cache_entry *centry;
875 centry = centry_start(domain, status);
876 if (!centry)
877 return;
879 centry_put_nttime(centry, lockout_policy->duration);
880 centry_put_nttime(centry, lockout_policy->reset_count);
881 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
883 centry_end(centry, "LOC_POL/%s", domain->name);
885 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
887 centry_free(centry);
890 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
892 struct cache_entry *centry;
894 centry = centry_start(domain, status);
895 if (!centry)
896 return;
898 centry_put_uint16(centry, policy->min_length_password);
899 centry_put_uint16(centry, policy->password_history);
900 centry_put_uint32(centry, policy->password_properties);
901 centry_put_nttime(centry, policy->expire);
902 centry_put_nttime(centry, policy->min_passwordage);
904 centry_end(centry, "PWD_POL/%s", domain->name);
906 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
908 centry_free(centry);
911 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
913 struct winbind_cache *cache = get_cache(domain);
914 TDB_DATA data;
915 fstring key_str;
916 uint32 rid;
918 if (!cache->tdb) {
919 return NT_STATUS_INTERNAL_DB_ERROR;
922 if (is_null_sid(sid)) {
923 return NT_STATUS_INVALID_SID;
926 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
927 return NT_STATUS_INVALID_SID;
930 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
932 data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
933 if (!data.dptr) {
934 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
937 SAFE_FREE(data.dptr);
938 return NT_STATUS_OK;
941 /* Lookup creds for a SID - copes with old (unsalted) creds as well
942 as new salted ones. */
944 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
945 TALLOC_CTX *mem_ctx,
946 const DOM_SID *sid,
947 const uint8 **cached_nt_pass,
948 const uint8 **cached_salt)
950 struct winbind_cache *cache = get_cache(domain);
951 struct cache_entry *centry = NULL;
952 NTSTATUS status;
953 time_t t;
954 uint32 rid;
956 if (!cache->tdb) {
957 return NT_STATUS_INTERNAL_DB_ERROR;
960 if (is_null_sid(sid)) {
961 return NT_STATUS_INVALID_SID;
964 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
965 return NT_STATUS_INVALID_SID;
968 /* Try and get a salted cred first. If we can't
969 fall back to an unsalted cred. */
971 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
972 if (!centry) {
973 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
974 sid_string_static(sid)));
975 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
978 t = centry_time(centry);
980 /* In the salted case this isn't actually the nt_hash itself,
981 but the MD5 of the salt + nt_hash. Let the caller
982 sort this out. It can tell as we only return the cached_salt
983 if we are returning a salted cred. */
985 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
986 if (*cached_nt_pass == NULL) {
987 const char *sidstr = sid_string_static(sid);
989 /* Bad (old) cred cache. Delete and pretend we
990 don't have it. */
991 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
992 sidstr));
993 wcache_delete("CRED/%s", sidstr);
994 centry_free(centry);
995 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
998 /* We only have 17 bytes more data in the salted cred case. */
999 if (centry->len - centry->ofs == 17) {
1000 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1001 } else {
1002 *cached_salt = NULL;
1005 #if DEBUG_PASSWORD
1006 dump_data(100, (const char *)*cached_nt_pass, NT_HASH_LEN);
1007 if (*cached_salt) {
1008 dump_data(100, (const char *)*cached_salt, NT_HASH_LEN);
1010 #endif
1011 status = centry->status;
1013 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1014 sid_string_static(sid), nt_errstr(status) ));
1016 centry_free(centry);
1017 return status;
1020 /* Store creds for a SID - only writes out new salted ones. */
1022 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1023 TALLOC_CTX *mem_ctx,
1024 const DOM_SID *sid,
1025 const uint8 nt_pass[NT_HASH_LEN])
1027 struct cache_entry *centry;
1028 fstring sid_string;
1029 uint32 rid;
1030 uint8 cred_salt[NT_HASH_LEN];
1031 uint8 salted_hash[NT_HASH_LEN];
1033 if (is_null_sid(sid)) {
1034 return NT_STATUS_INVALID_SID;
1037 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1038 return NT_STATUS_INVALID_SID;
1041 centry = centry_start(domain, NT_STATUS_OK);
1042 if (!centry) {
1043 return NT_STATUS_INTERNAL_DB_ERROR;
1046 #if DEBUG_PASSWORD
1047 dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
1048 #endif
1050 centry_put_time(centry, time(NULL));
1052 /* Create a salt and then salt the hash. */
1053 generate_random_buffer(cred_salt, NT_HASH_LEN);
1054 E_md5hash(cred_salt, nt_pass, salted_hash);
1056 centry_put_hash16(centry, salted_hash);
1057 centry_put_hash16(centry, cred_salt);
1058 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1060 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1062 centry_free(centry);
1064 return NT_STATUS_OK;
1068 /* Query display info. This is the basic user list fn */
1069 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1070 TALLOC_CTX *mem_ctx,
1071 uint32 *num_entries,
1072 WINBIND_USERINFO **info)
1074 struct winbind_cache *cache = get_cache(domain);
1075 struct cache_entry *centry = NULL;
1076 NTSTATUS status;
1077 unsigned int i, retry;
1079 if (!cache->tdb)
1080 goto do_query;
1082 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1083 if (!centry)
1084 goto do_query;
1086 *num_entries = centry_uint32(centry);
1088 if (*num_entries == 0)
1089 goto do_cached;
1091 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1092 if (! (*info))
1093 smb_panic("query_user_list out of memory");
1094 for (i=0; i<(*num_entries); i++) {
1095 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1096 (*info)[i].full_name = centry_string(centry, mem_ctx);
1097 (*info)[i].homedir = centry_string(centry, mem_ctx);
1098 (*info)[i].shell = centry_string(centry, mem_ctx);
1099 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1100 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1103 do_cached:
1104 status = centry->status;
1106 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1107 domain->name, nt_errstr(status) ));
1109 centry_free(centry);
1110 return status;
1112 do_query:
1113 *num_entries = 0;
1114 *info = NULL;
1116 /* Return status value returned by seq number check */
1118 if (!NT_STATUS_IS_OK(domain->last_status))
1119 return domain->last_status;
1121 /* Put the query_user_list() in a retry loop. There appears to be
1122 * some bug either with Windows 2000 or Samba's handling of large
1123 * rpc replies. This manifests itself as sudden disconnection
1124 * at a random point in the enumeration of a large (60k) user list.
1125 * The retry loop simply tries the operation again. )-: It's not
1126 * pretty but an acceptable workaround until we work out what the
1127 * real problem is. */
1129 retry = 0;
1130 do {
1132 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1133 domain->name ));
1135 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1136 if (!NT_STATUS_IS_OK(status)) {
1137 DEBUG(3, ("query_user_list: returned 0x%08x, "
1138 "retrying\n", NT_STATUS_V(status)));
1140 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1141 DEBUG(3, ("query_user_list: flushing "
1142 "connection cache\n"));
1143 invalidate_cm_connection(&domain->conn);
1146 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1147 (retry++ < 5));
1149 /* and save it */
1150 refresh_sequence_number(domain, False);
1151 centry = centry_start(domain, status);
1152 if (!centry)
1153 goto skip_save;
1154 centry_put_uint32(centry, *num_entries);
1155 for (i=0; i<(*num_entries); i++) {
1156 centry_put_string(centry, (*info)[i].acct_name);
1157 centry_put_string(centry, (*info)[i].full_name);
1158 centry_put_string(centry, (*info)[i].homedir);
1159 centry_put_string(centry, (*info)[i].shell);
1160 centry_put_sid(centry, &(*info)[i].user_sid);
1161 centry_put_sid(centry, &(*info)[i].group_sid);
1162 if (domain->backend && domain->backend->consistent) {
1163 /* when the backend is consistent we can pre-prime some mappings */
1164 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1165 domain->name,
1166 (*info)[i].acct_name,
1167 &(*info)[i].user_sid,
1168 SID_NAME_USER);
1169 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1170 &(*info)[i].user_sid,
1171 domain->name,
1172 (*info)[i].acct_name,
1173 SID_NAME_USER);
1174 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1177 centry_end(centry, "UL/%s", domain->name);
1178 centry_free(centry);
1180 skip_save:
1181 return status;
1184 /* list all domain groups */
1185 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1186 TALLOC_CTX *mem_ctx,
1187 uint32 *num_entries,
1188 struct acct_info **info)
1190 struct winbind_cache *cache = get_cache(domain);
1191 struct cache_entry *centry = NULL;
1192 NTSTATUS status;
1193 unsigned int i;
1195 if (!cache->tdb)
1196 goto do_query;
1198 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1199 if (!centry)
1200 goto do_query;
1202 *num_entries = centry_uint32(centry);
1204 if (*num_entries == 0)
1205 goto do_cached;
1207 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1208 if (! (*info))
1209 smb_panic("enum_dom_groups out of memory");
1210 for (i=0; i<(*num_entries); i++) {
1211 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1212 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1213 (*info)[i].rid = centry_uint32(centry);
1216 do_cached:
1217 status = centry->status;
1219 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1220 domain->name, nt_errstr(status) ));
1222 centry_free(centry);
1223 return status;
1225 do_query:
1226 *num_entries = 0;
1227 *info = NULL;
1229 /* Return status value returned by seq number check */
1231 if (!NT_STATUS_IS_OK(domain->last_status))
1232 return domain->last_status;
1234 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1235 domain->name ));
1237 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1239 /* and save it */
1240 refresh_sequence_number(domain, False);
1241 centry = centry_start(domain, status);
1242 if (!centry)
1243 goto skip_save;
1244 centry_put_uint32(centry, *num_entries);
1245 for (i=0; i<(*num_entries); i++) {
1246 centry_put_string(centry, (*info)[i].acct_name);
1247 centry_put_string(centry, (*info)[i].acct_desc);
1248 centry_put_uint32(centry, (*info)[i].rid);
1250 centry_end(centry, "GL/%s/domain", domain->name);
1251 centry_free(centry);
1253 skip_save:
1254 return status;
1257 /* list all domain groups */
1258 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1259 TALLOC_CTX *mem_ctx,
1260 uint32 *num_entries,
1261 struct acct_info **info)
1263 struct winbind_cache *cache = get_cache(domain);
1264 struct cache_entry *centry = NULL;
1265 NTSTATUS status;
1266 unsigned int i;
1268 if (!cache->tdb)
1269 goto do_query;
1271 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1272 if (!centry)
1273 goto do_query;
1275 *num_entries = centry_uint32(centry);
1277 if (*num_entries == 0)
1278 goto do_cached;
1280 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1281 if (! (*info))
1282 smb_panic("enum_dom_groups out of memory");
1283 for (i=0; i<(*num_entries); i++) {
1284 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1285 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1286 (*info)[i].rid = centry_uint32(centry);
1289 do_cached:
1291 /* If we are returning cached data and the domain controller
1292 is down then we don't know whether the data is up to date
1293 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1294 indicate this. */
1296 if (wcache_server_down(domain)) {
1297 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1298 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1299 } else
1300 status = centry->status;
1302 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1303 domain->name, nt_errstr(status) ));
1305 centry_free(centry);
1306 return status;
1308 do_query:
1309 *num_entries = 0;
1310 *info = NULL;
1312 /* Return status value returned by seq number check */
1314 if (!NT_STATUS_IS_OK(domain->last_status))
1315 return domain->last_status;
1317 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1318 domain->name ));
1320 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1322 /* and save it */
1323 refresh_sequence_number(domain, False);
1324 centry = centry_start(domain, status);
1325 if (!centry)
1326 goto skip_save;
1327 centry_put_uint32(centry, *num_entries);
1328 for (i=0; i<(*num_entries); i++) {
1329 centry_put_string(centry, (*info)[i].acct_name);
1330 centry_put_string(centry, (*info)[i].acct_desc);
1331 centry_put_uint32(centry, (*info)[i].rid);
1333 centry_end(centry, "GL/%s/local", domain->name);
1334 centry_free(centry);
1336 skip_save:
1337 return status;
1340 /* convert a single name to a sid in a domain */
1341 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1342 TALLOC_CTX *mem_ctx,
1343 const char *domain_name,
1344 const char *name,
1345 DOM_SID *sid,
1346 enum lsa_SidType *type)
1348 struct winbind_cache *cache = get_cache(domain);
1349 struct cache_entry *centry = NULL;
1350 NTSTATUS status;
1351 fstring uname;
1353 if (!cache->tdb)
1354 goto do_query;
1356 fstrcpy(uname, name);
1357 strupper_m(uname);
1358 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1359 if (!centry)
1360 goto do_query;
1361 *type = (enum lsa_SidType)centry_uint32(centry);
1362 status = centry->status;
1363 if (NT_STATUS_IS_OK(status)) {
1364 centry_sid(centry, mem_ctx, sid);
1367 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1368 domain->name, nt_errstr(status) ));
1370 centry_free(centry);
1371 return status;
1373 do_query:
1374 ZERO_STRUCTP(sid);
1376 /* If the seq number check indicated that there is a problem
1377 * with this DC, then return that status... except for
1378 * access_denied. This is special because the dc may be in
1379 * "restrict anonymous = 1" mode, in which case it will deny
1380 * most unauthenticated operations, but *will* allow the LSA
1381 * name-to-sid that we try as a fallback. */
1383 if (!(NT_STATUS_IS_OK(domain->last_status)
1384 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1385 return domain->last_status;
1387 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1388 domain->name ));
1390 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1392 /* and save it */
1393 refresh_sequence_number(domain, False);
1395 if (domain->online && !is_null_sid(sid)) {
1396 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1399 if (NT_STATUS_IS_OK(status)) {
1400 strupper_m(CONST_DISCARD(char *,domain_name));
1401 strlower_m(CONST_DISCARD(char *,name));
1402 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1405 return status;
1408 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1409 given */
1410 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1411 TALLOC_CTX *mem_ctx,
1412 const DOM_SID *sid,
1413 char **domain_name,
1414 char **name,
1415 enum lsa_SidType *type)
1417 struct winbind_cache *cache = get_cache(domain);
1418 struct cache_entry *centry = NULL;
1419 NTSTATUS status;
1420 fstring sid_string;
1422 if (!cache->tdb)
1423 goto do_query;
1425 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1426 if (!centry)
1427 goto do_query;
1428 if (NT_STATUS_IS_OK(centry->status)) {
1429 *type = (enum lsa_SidType)centry_uint32(centry);
1430 *domain_name = centry_string(centry, mem_ctx);
1431 *name = centry_string(centry, mem_ctx);
1433 status = centry->status;
1435 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1436 domain->name, nt_errstr(status) ));
1438 centry_free(centry);
1439 return status;
1441 do_query:
1442 *name = NULL;
1443 *domain_name = NULL;
1445 /* If the seq number check indicated that there is a problem
1446 * with this DC, then return that status... except for
1447 * access_denied. This is special because the dc may be in
1448 * "restrict anonymous = 1" mode, in which case it will deny
1449 * most unauthenticated operations, but *will* allow the LSA
1450 * sid-to-name that we try as a fallback. */
1452 if (!(NT_STATUS_IS_OK(domain->last_status)
1453 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1454 return domain->last_status;
1456 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1457 domain->name ));
1459 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1461 /* and save it */
1462 refresh_sequence_number(domain, False);
1463 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1465 /* We can't save the name to sid mapping here, as with sid history a
1466 * later name2sid would give the wrong sid. */
1468 return status;
1471 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1472 TALLOC_CTX *mem_ctx,
1473 const DOM_SID *domain_sid,
1474 uint32 *rids,
1475 size_t num_rids,
1476 char **domain_name,
1477 char ***names,
1478 enum lsa_SidType **types)
1480 struct winbind_cache *cache = get_cache(domain);
1481 size_t i;
1482 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1483 BOOL have_mapped;
1484 BOOL have_unmapped;
1486 *domain_name = NULL;
1487 *names = NULL;
1488 *types = NULL;
1490 if (!cache->tdb) {
1491 goto do_query;
1494 if (num_rids == 0) {
1495 return NT_STATUS_OK;
1498 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1499 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1501 if ((*names == NULL) || (*types == NULL)) {
1502 result = NT_STATUS_NO_MEMORY;
1503 goto error;
1506 have_mapped = have_unmapped = False;
1508 for (i=0; i<num_rids; i++) {
1509 DOM_SID sid;
1510 struct cache_entry *centry;
1512 if (!sid_compose(&sid, domain_sid, rids[i])) {
1513 result = NT_STATUS_INTERNAL_ERROR;
1514 goto error;
1517 centry = wcache_fetch(cache, domain, "SN/%s",
1518 sid_string_static(&sid));
1519 if (!centry) {
1520 goto do_query;
1523 (*types)[i] = SID_NAME_UNKNOWN;
1524 (*names)[i] = talloc_strdup(*names, "");
1526 if (NT_STATUS_IS_OK(centry->status)) {
1527 char *dom;
1528 have_mapped = True;
1529 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1530 dom = centry_string(centry, mem_ctx);
1531 if (*domain_name == NULL) {
1532 *domain_name = dom;
1533 } else {
1534 talloc_free(dom);
1536 (*names)[i] = centry_string(centry, *names);
1537 } else {
1538 have_unmapped = True;
1541 centry_free(centry);
1544 if (!have_mapped) {
1545 return NT_STATUS_NONE_MAPPED;
1547 if (!have_unmapped) {
1548 return NT_STATUS_OK;
1550 return STATUS_SOME_UNMAPPED;
1552 do_query:
1554 TALLOC_FREE(*names);
1555 TALLOC_FREE(*types);
1557 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1558 rids, num_rids, domain_name,
1559 names, types);
1561 if (!NT_STATUS_IS_OK(result) &&
1562 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1563 return result;
1566 refresh_sequence_number(domain, False);
1568 for (i=0; i<num_rids; i++) {
1569 DOM_SID sid;
1570 NTSTATUS status;
1572 if (!sid_compose(&sid, domain_sid, rids[i])) {
1573 result = NT_STATUS_INTERNAL_ERROR;
1574 goto error;
1577 status = (*types)[i] == SID_NAME_UNKNOWN ?
1578 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1580 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1581 (*names)[i], (*types)[i]);
1584 return result;
1586 error:
1588 TALLOC_FREE(*names);
1589 TALLOC_FREE(*types);
1590 return result;
1593 /* Lookup user information from a rid */
1594 static NTSTATUS query_user(struct winbindd_domain *domain,
1595 TALLOC_CTX *mem_ctx,
1596 const DOM_SID *user_sid,
1597 WINBIND_USERINFO *info)
1599 struct winbind_cache *cache = get_cache(domain);
1600 struct cache_entry *centry = NULL;
1601 NTSTATUS status;
1603 if (!cache->tdb)
1604 goto do_query;
1606 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1608 /* If we have an access denied cache entry and a cached info3 in the
1609 samlogon cache then do a query. This will force the rpc back end
1610 to return the info3 data. */
1612 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1613 netsamlogon_cache_have(user_sid)) {
1614 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1615 domain->last_status = NT_STATUS_OK;
1616 centry_free(centry);
1617 goto do_query;
1620 if (!centry)
1621 goto do_query;
1623 info->acct_name = centry_string(centry, mem_ctx);
1624 info->full_name = centry_string(centry, mem_ctx);
1625 info->homedir = centry_string(centry, mem_ctx);
1626 info->shell = centry_string(centry, mem_ctx);
1627 info->primary_gid = centry_uint32(centry);
1628 centry_sid(centry, mem_ctx, &info->user_sid);
1629 centry_sid(centry, mem_ctx, &info->group_sid);
1630 status = centry->status;
1632 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1633 domain->name, nt_errstr(status) ));
1635 centry_free(centry);
1636 return status;
1638 do_query:
1639 ZERO_STRUCTP(info);
1641 /* Return status value returned by seq number check */
1643 if (!NT_STATUS_IS_OK(domain->last_status))
1644 return domain->last_status;
1646 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1647 domain->name ));
1649 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1651 /* and save it */
1652 refresh_sequence_number(domain, False);
1653 wcache_save_user(domain, status, info);
1655 return status;
1659 /* Lookup groups a user is a member of. */
1660 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1661 TALLOC_CTX *mem_ctx,
1662 const DOM_SID *user_sid,
1663 uint32 *num_groups, DOM_SID **user_gids)
1665 struct winbind_cache *cache = get_cache(domain);
1666 struct cache_entry *centry = NULL;
1667 NTSTATUS status;
1668 unsigned int i;
1669 fstring sid_string;
1671 if (!cache->tdb)
1672 goto do_query;
1674 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1676 /* If we have an access denied cache entry and a cached info3 in the
1677 samlogon cache then do a query. This will force the rpc back end
1678 to return the info3 data. */
1680 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1681 netsamlogon_cache_have(user_sid)) {
1682 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1683 domain->last_status = NT_STATUS_OK;
1684 centry_free(centry);
1685 goto do_query;
1688 if (!centry)
1689 goto do_query;
1691 *num_groups = centry_uint32(centry);
1693 if (*num_groups == 0)
1694 goto do_cached;
1696 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1697 if (! (*user_gids))
1698 smb_panic("lookup_usergroups out of memory");
1699 for (i=0; i<(*num_groups); i++) {
1700 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1703 do_cached:
1704 status = centry->status;
1706 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1707 domain->name, nt_errstr(status) ));
1709 centry_free(centry);
1710 return status;
1712 do_query:
1713 (*num_groups) = 0;
1714 (*user_gids) = NULL;
1716 /* Return status value returned by seq number check */
1718 if (!NT_STATUS_IS_OK(domain->last_status))
1719 return domain->last_status;
1721 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1722 domain->name ));
1724 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1726 /* and save it */
1727 refresh_sequence_number(domain, False);
1728 centry = centry_start(domain, status);
1729 if (!centry)
1730 goto skip_save;
1731 centry_put_uint32(centry, *num_groups);
1732 for (i=0; i<(*num_groups); i++) {
1733 centry_put_sid(centry, &(*user_gids)[i]);
1735 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1736 centry_free(centry);
1738 skip_save:
1739 return status;
1742 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1743 TALLOC_CTX *mem_ctx,
1744 uint32 num_sids, const DOM_SID *sids,
1745 uint32 *num_aliases, uint32 **alias_rids)
1747 struct winbind_cache *cache = get_cache(domain);
1748 struct cache_entry *centry = NULL;
1749 NTSTATUS status;
1750 char *sidlist = talloc_strdup(mem_ctx, "");
1751 int i;
1753 if (!cache->tdb)
1754 goto do_query;
1756 if (num_sids == 0) {
1757 *num_aliases = 0;
1758 *alias_rids = NULL;
1759 return NT_STATUS_OK;
1762 /* We need to cache indexed by the whole list of SIDs, the aliases
1763 * resulting might come from any of the SIDs. */
1765 for (i=0; i<num_sids; i++) {
1766 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1767 sid_string_static(&sids[i]));
1768 if (sidlist == NULL)
1769 return NT_STATUS_NO_MEMORY;
1772 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1774 if (!centry)
1775 goto do_query;
1777 *num_aliases = centry_uint32(centry);
1778 *alias_rids = NULL;
1780 if (*num_aliases) {
1781 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1783 if ((*alias_rids) == NULL) {
1784 centry_free(centry);
1785 return NT_STATUS_NO_MEMORY;
1787 } else {
1788 (*alias_rids) = NULL;
1791 for (i=0; i<(*num_aliases); i++)
1792 (*alias_rids)[i] = centry_uint32(centry);
1794 status = centry->status;
1796 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1797 "status %s\n", domain->name, nt_errstr(status)));
1799 centry_free(centry);
1800 return status;
1802 do_query:
1803 (*num_aliases) = 0;
1804 (*alias_rids) = NULL;
1806 if (!NT_STATUS_IS_OK(domain->last_status))
1807 return domain->last_status;
1809 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1810 "for domain %s\n", domain->name ));
1812 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1813 num_sids, sids,
1814 num_aliases, alias_rids);
1816 /* and save it */
1817 refresh_sequence_number(domain, False);
1818 centry = centry_start(domain, status);
1819 if (!centry)
1820 goto skip_save;
1821 centry_put_uint32(centry, *num_aliases);
1822 for (i=0; i<(*num_aliases); i++)
1823 centry_put_uint32(centry, (*alias_rids)[i]);
1824 centry_end(centry, "UA%s", sidlist);
1825 centry_free(centry);
1827 skip_save:
1828 return status;
1832 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1833 TALLOC_CTX *mem_ctx,
1834 const DOM_SID *group_sid, uint32 *num_names,
1835 DOM_SID **sid_mem, char ***names,
1836 uint32 **name_types)
1838 struct winbind_cache *cache = get_cache(domain);
1839 struct cache_entry *centry = NULL;
1840 NTSTATUS status;
1841 unsigned int i;
1842 fstring sid_string;
1844 if (!cache->tdb)
1845 goto do_query;
1847 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1848 if (!centry)
1849 goto do_query;
1851 *num_names = centry_uint32(centry);
1853 if (*num_names == 0)
1854 goto do_cached;
1856 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1857 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1858 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1860 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1861 smb_panic("lookup_groupmem out of memory");
1864 for (i=0; i<(*num_names); i++) {
1865 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1866 (*names)[i] = centry_string(centry, mem_ctx);
1867 (*name_types)[i] = centry_uint32(centry);
1870 do_cached:
1871 status = centry->status;
1873 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1874 domain->name, nt_errstr(status)));
1876 centry_free(centry);
1877 return status;
1879 do_query:
1880 (*num_names) = 0;
1881 (*sid_mem) = NULL;
1882 (*names) = NULL;
1883 (*name_types) = NULL;
1885 /* Return status value returned by seq number check */
1887 if (!NT_STATUS_IS_OK(domain->last_status))
1888 return domain->last_status;
1890 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1891 domain->name ));
1893 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1894 sid_mem, names, name_types);
1896 /* and save it */
1897 refresh_sequence_number(domain, False);
1898 centry = centry_start(domain, status);
1899 if (!centry)
1900 goto skip_save;
1901 centry_put_uint32(centry, *num_names);
1902 for (i=0; i<(*num_names); i++) {
1903 centry_put_sid(centry, &(*sid_mem)[i]);
1904 centry_put_string(centry, (*names)[i]);
1905 centry_put_uint32(centry, (*name_types)[i]);
1907 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1908 centry_free(centry);
1910 skip_save:
1911 return status;
1914 /* find the sequence number for a domain */
1915 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1917 refresh_sequence_number(domain, False);
1919 *seq = domain->sequence_number;
1921 return NT_STATUS_OK;
1924 /* enumerate trusted domains
1925 * (we need to have the list of trustdoms in the cache when we go offline) -
1926 * Guenther */
1927 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1928 TALLOC_CTX *mem_ctx,
1929 uint32 *num_domains,
1930 char ***names,
1931 char ***alt_names,
1932 DOM_SID **dom_sids)
1934 struct winbind_cache *cache = get_cache(domain);
1935 struct cache_entry *centry = NULL;
1936 NTSTATUS status;
1937 int i;
1939 if (!cache->tdb)
1940 goto do_query;
1942 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1944 if (!centry) {
1945 goto do_query;
1948 *num_domains = centry_uint32(centry);
1950 if (*num_domains) {
1951 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1952 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1953 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1955 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1956 smb_panic("trusted_domains out of memory");
1958 } else {
1959 (*names) = NULL;
1960 (*alt_names) = NULL;
1961 (*dom_sids) = NULL;
1964 for (i=0; i<(*num_domains); i++) {
1965 (*names)[i] = centry_string(centry, mem_ctx);
1966 (*alt_names)[i] = centry_string(centry, mem_ctx);
1967 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1970 status = centry->status;
1972 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1973 domain->name, *num_domains, nt_errstr(status) ));
1975 centry_free(centry);
1976 return status;
1978 do_query:
1979 (*num_domains) = 0;
1980 (*dom_sids) = NULL;
1981 (*names) = NULL;
1982 (*alt_names) = NULL;
1984 /* Return status value returned by seq number check */
1986 if (!NT_STATUS_IS_OK(domain->last_status))
1987 return domain->last_status;
1989 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1990 domain->name ));
1992 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1993 names, alt_names, dom_sids);
1995 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1996 * so that the generic centry handling still applies correctly -
1997 * Guenther*/
1999 if (!NT_STATUS_IS_ERR(status)) {
2000 status = NT_STATUS_OK;
2003 /* and save it */
2004 refresh_sequence_number(domain, False);
2006 centry = centry_start(domain, status);
2007 if (!centry)
2008 goto skip_save;
2010 centry_put_uint32(centry, *num_domains);
2012 for (i=0; i<(*num_domains); i++) {
2013 centry_put_string(centry, (*names)[i]);
2014 centry_put_string(centry, (*alt_names)[i]);
2015 centry_put_sid(centry, &(*dom_sids)[i]);
2018 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2020 centry_free(centry);
2022 skip_save:
2023 return status;
2026 /* get lockout policy */
2027 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2028 TALLOC_CTX *mem_ctx,
2029 SAM_UNK_INFO_12 *policy){
2030 struct winbind_cache *cache = get_cache(domain);
2031 struct cache_entry *centry = NULL;
2032 NTSTATUS status;
2034 if (!cache->tdb)
2035 goto do_query;
2037 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2039 if (!centry)
2040 goto do_query;
2042 policy->duration = centry_nttime(centry);
2043 policy->reset_count = centry_nttime(centry);
2044 policy->bad_attempt_lockout = centry_uint16(centry);
2046 status = centry->status;
2048 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2049 domain->name, nt_errstr(status) ));
2051 centry_free(centry);
2052 return status;
2054 do_query:
2055 ZERO_STRUCTP(policy);
2057 /* Return status value returned by seq number check */
2059 if (!NT_STATUS_IS_OK(domain->last_status))
2060 return domain->last_status;
2062 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2063 domain->name ));
2065 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2067 /* and save it */
2068 refresh_sequence_number(domain, False);
2069 wcache_save_lockout_policy(domain, status, policy);
2071 return status;
2074 /* get password policy */
2075 static NTSTATUS password_policy(struct winbindd_domain *domain,
2076 TALLOC_CTX *mem_ctx,
2077 SAM_UNK_INFO_1 *policy)
2079 struct winbind_cache *cache = get_cache(domain);
2080 struct cache_entry *centry = NULL;
2081 NTSTATUS status;
2083 if (!cache->tdb)
2084 goto do_query;
2086 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2088 if (!centry)
2089 goto do_query;
2091 policy->min_length_password = centry_uint16(centry);
2092 policy->password_history = centry_uint16(centry);
2093 policy->password_properties = centry_uint32(centry);
2094 policy->expire = centry_nttime(centry);
2095 policy->min_passwordage = centry_nttime(centry);
2097 status = centry->status;
2099 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2100 domain->name, nt_errstr(status) ));
2102 centry_free(centry);
2103 return status;
2105 do_query:
2106 ZERO_STRUCTP(policy);
2108 /* Return status value returned by seq number check */
2110 if (!NT_STATUS_IS_OK(domain->last_status))
2111 return domain->last_status;
2113 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2114 domain->name ));
2116 status = domain->backend->password_policy(domain, mem_ctx, policy);
2118 /* and save it */
2119 refresh_sequence_number(domain, False);
2120 wcache_save_password_policy(domain, status, policy);
2122 return status;
2126 /* Invalidate cached user and group lists coherently */
2128 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2129 void *state)
2131 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
2132 strncmp(kbuf.dptr, "GL/", 3) == 0)
2133 tdb_delete(the_tdb, kbuf);
2135 return 0;
2138 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2140 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2141 NET_USER_INFO_3 *info3)
2143 struct winbind_cache *cache;
2145 /* dont clear cached U/SID and UG/SID entries when we want to logon
2146 * offline - gd */
2148 if (lp_winbind_offline_logon()) {
2149 return;
2152 if (!domain)
2153 return;
2155 cache = get_cache(domain);
2156 netsamlogon_clear_cached_user(cache->tdb, info3);
2159 void wcache_invalidate_cache(void)
2161 struct winbindd_domain *domain;
2163 for (domain = domain_list(); domain; domain = domain->next) {
2164 struct winbind_cache *cache = get_cache(domain);
2166 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2167 "entries for %s\n", domain->name));
2168 if (cache)
2169 tdb_traverse(cache->tdb, traverse_fn, NULL);
2173 static BOOL init_wcache(void)
2175 if (wcache == NULL) {
2176 wcache = SMB_XMALLOC_P(struct winbind_cache);
2177 ZERO_STRUCTP(wcache);
2180 if (wcache->tdb != NULL)
2181 return True;
2183 /* when working offline we must not clear the cache on restart */
2184 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2185 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2186 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2187 O_RDWR|O_CREAT, 0600);
2189 if (wcache->tdb == NULL) {
2190 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2191 return False;
2194 return True;
2197 /************************************************************************
2198 This is called by the parent to initialize the cache file.
2199 We don't need sophisticated locking here as we know we're the
2200 only opener.
2201 ************************************************************************/
2203 BOOL initialize_winbindd_cache(void)
2205 BOOL cache_bad = True;
2206 uint32 vers;
2208 if (!init_wcache()) {
2209 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2210 return False;
2213 /* Check version number. */
2214 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2215 vers == WINBINDD_CACHE_VERSION) {
2216 cache_bad = False;
2219 if (cache_bad) {
2220 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2221 "and re-creating with version number %d\n",
2222 WINBINDD_CACHE_VERSION ));
2224 tdb_close(wcache->tdb);
2225 wcache->tdb = NULL;
2227 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2228 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2229 lock_path("winbindd_cache.tdb"),
2230 strerror(errno) ));
2231 return False;
2233 if (!init_wcache()) {
2234 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2235 "init_wcache failed.\n"));
2236 return False;
2239 /* Write the version. */
2240 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2241 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2242 tdb_errorstr(wcache->tdb) ));
2243 return False;
2247 tdb_close(wcache->tdb);
2248 wcache->tdb = NULL;
2249 return True;
2252 void cache_store_response(pid_t pid, struct winbindd_response *response)
2254 fstring key_str;
2256 if (!init_wcache())
2257 return;
2259 DEBUG(10, ("Storing response for pid %d, len %d\n",
2260 pid, response->length));
2262 fstr_sprintf(key_str, "DR/%d", pid);
2263 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2264 make_tdb_data((const char *)response, sizeof(*response)),
2265 TDB_REPLACE) == -1)
2266 return;
2268 if (response->length == sizeof(*response))
2269 return;
2271 /* There's extra data */
2273 DEBUG(10, ("Storing extra data: len=%d\n",
2274 (int)(response->length - sizeof(*response))));
2276 fstr_sprintf(key_str, "DE/%d", pid);
2277 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2278 make_tdb_data((const char *)response->extra_data.data,
2279 response->length - sizeof(*response)),
2280 TDB_REPLACE) == 0)
2281 return;
2283 /* We could not store the extra data, make sure the tdb does not
2284 * contain a main record with wrong dangling extra data */
2286 fstr_sprintf(key_str, "DR/%d", pid);
2287 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2289 return;
2292 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2294 TDB_DATA data;
2295 fstring key_str;
2297 if (!init_wcache())
2298 return False;
2300 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2302 fstr_sprintf(key_str, "DR/%d", pid);
2303 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2305 if (data.dptr == NULL)
2306 return False;
2308 if (data.dsize != sizeof(*response))
2309 return False;
2311 memcpy(response, data.dptr, data.dsize);
2312 SAFE_FREE(data.dptr);
2314 if (response->length == sizeof(*response)) {
2315 response->extra_data.data = NULL;
2316 return True;
2319 /* There's extra data */
2321 DEBUG(10, ("Retrieving extra data length=%d\n",
2322 (int)(response->length - sizeof(*response))));
2324 fstr_sprintf(key_str, "DE/%d", pid);
2325 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2327 if (data.dptr == NULL) {
2328 DEBUG(0, ("Did not find extra data\n"));
2329 return False;
2332 if (data.dsize != (response->length - sizeof(*response))) {
2333 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2334 SAFE_FREE(data.dptr);
2335 return False;
2338 dump_data(11, data.dptr, data.dsize);
2340 response->extra_data.data = data.dptr;
2341 return True;
2344 void cache_cleanup_response(pid_t pid)
2346 fstring key_str;
2348 if (!init_wcache())
2349 return;
2351 fstr_sprintf(key_str, "DR/%d", pid);
2352 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2354 fstr_sprintf(key_str, "DE/%d", pid);
2355 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2357 return;
2361 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2362 const char **domain_name, const char **name,
2363 enum lsa_SidType *type)
2365 struct winbindd_domain *domain;
2366 struct winbind_cache *cache;
2367 struct cache_entry *centry = NULL;
2368 NTSTATUS status;
2370 domain = find_lookup_domain_from_sid(sid);
2371 if (domain == NULL) {
2372 return False;
2375 cache = get_cache(domain);
2377 if (cache->tdb == NULL) {
2378 return False;
2381 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2382 if (centry == NULL) {
2383 return False;
2386 if (NT_STATUS_IS_OK(centry->status)) {
2387 *type = (enum lsa_SidType)centry_uint32(centry);
2388 *domain_name = centry_string(centry, mem_ctx);
2389 *name = centry_string(centry, mem_ctx);
2392 status = centry->status;
2393 centry_free(centry);
2394 return NT_STATUS_IS_OK(status);
2397 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2398 const char *domain_name,
2399 const char *name,
2400 DOM_SID *sid,
2401 enum lsa_SidType *type)
2403 struct winbindd_domain *domain;
2404 struct winbind_cache *cache;
2405 struct cache_entry *centry = NULL;
2406 NTSTATUS status;
2407 fstring uname;
2409 domain = find_lookup_domain_from_name(domain_name);
2410 if (domain == NULL) {
2411 return False;
2414 cache = get_cache(domain);
2416 if (cache->tdb == NULL) {
2417 return False;
2420 fstrcpy(uname, name);
2421 strupper_m(uname);
2423 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2424 if (centry == NULL) {
2425 return False;
2428 if (NT_STATUS_IS_OK(centry->status)) {
2429 *type = (enum lsa_SidType)centry_uint32(centry);
2430 centry_sid(centry, mem_ctx, sid);
2433 status = centry->status;
2434 centry_free(centry);
2436 return NT_STATUS_IS_OK(status);
2439 void cache_name2sid(struct winbindd_domain *domain,
2440 const char *domain_name, const char *name,
2441 enum lsa_SidType type, const DOM_SID *sid)
2443 refresh_sequence_number(domain, False);
2444 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2445 sid, type);
2448 /* delete all centries that don't have NT_STATUS_OK set */
2450 * The original idea that this cache only contains centries has
2451 * been blurred - now other stuff gets put in here. Ensure we
2452 * ignore these things on cleanup.
2455 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2456 TDB_DATA dbuf, void *state)
2458 struct cache_entry *centry;
2460 if (is_non_centry_key(kbuf)) {
2461 return 0;
2464 centry = wcache_fetch_raw(kbuf.dptr);
2465 if (!centry) {
2466 return 0;
2469 if (!NT_STATUS_IS_OK(centry->status)) {
2470 DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2471 tdb_delete(the_tdb, kbuf);
2474 centry_free(centry);
2475 return 0;
2478 /* flush the cache */
2479 void wcache_flush_cache(void)
2481 if (!wcache)
2482 return;
2483 if (wcache->tdb) {
2484 tdb_close(wcache->tdb);
2485 wcache->tdb = NULL;
2487 if (opt_nocache)
2488 return;
2490 /* when working offline we must not clear the cache on restart */
2491 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2492 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2493 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2494 O_RDWR|O_CREAT, 0600);
2496 if (!wcache->tdb) {
2497 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2498 return;
2501 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2503 DEBUG(10,("wcache_flush_cache success\n"));
2506 /* Count cached creds */
2508 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2509 void *state)
2511 int *cred_count = (int*)state;
2513 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2514 (*cred_count)++;
2516 return 0;
2519 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2521 struct winbind_cache *cache = get_cache(domain);
2523 *count = 0;
2525 if (!cache->tdb) {
2526 return NT_STATUS_INTERNAL_DB_ERROR;
2529 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2531 return NT_STATUS_OK;
2534 struct cred_list {
2535 struct cred_list *prev, *next;
2536 TDB_DATA key;
2537 fstring name;
2538 time_t created;
2540 static struct cred_list *wcache_cred_list;
2542 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2543 void *state)
2545 struct cred_list *cred;
2547 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2549 cred = SMB_MALLOC_P(struct cred_list);
2550 if (cred == NULL) {
2551 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2552 return -1;
2555 ZERO_STRUCTP(cred);
2557 /* save a copy of the key */
2559 fstrcpy(cred->name, kbuf.dptr);
2560 DLIST_ADD(wcache_cred_list, cred);
2563 return 0;
2566 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2568 struct winbind_cache *cache = get_cache(domain);
2569 NTSTATUS status;
2570 int ret;
2571 struct cred_list *cred, *oldest = NULL;
2573 if (!cache->tdb) {
2574 return NT_STATUS_INTERNAL_DB_ERROR;
2577 /* we possibly already have an entry */
2578 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2580 fstring key_str;
2582 DEBUG(11,("we already have an entry, deleting that\n"));
2584 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2586 tdb_delete(cache->tdb, string_tdb_data(key_str));
2588 return NT_STATUS_OK;
2591 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2592 if (ret == 0) {
2593 return NT_STATUS_OK;
2594 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2595 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2598 ZERO_STRUCTP(oldest);
2600 for (cred = wcache_cred_list; cred; cred = cred->next) {
2602 TDB_DATA data;
2603 time_t t;
2605 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2606 if (!data.dptr) {
2607 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2608 cred->name));
2609 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2610 goto done;
2613 t = IVAL(data.dptr, 0);
2614 SAFE_FREE(data.dptr);
2616 if (!oldest) {
2617 oldest = SMB_MALLOC_P(struct cred_list);
2618 if (oldest == NULL) {
2619 status = NT_STATUS_NO_MEMORY;
2620 goto done;
2623 fstrcpy(oldest->name, cred->name);
2624 oldest->created = t;
2625 continue;
2628 if (t < oldest->created) {
2629 fstrcpy(oldest->name, cred->name);
2630 oldest->created = t;
2634 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2635 status = NT_STATUS_OK;
2636 } else {
2637 status = NT_STATUS_UNSUCCESSFUL;
2639 done:
2640 SAFE_FREE(wcache_cred_list);
2641 SAFE_FREE(oldest);
2643 return status;
2646 /* Change the global online/offline state. */
2647 BOOL set_global_winbindd_state_offline(void)
2649 TDB_DATA data;
2651 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2653 /* Only go offline if someone has created
2654 the key "WINBINDD_OFFLINE" in the cache tdb. */
2656 if (wcache == NULL || wcache->tdb == NULL) {
2657 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2658 return False;
2661 if (!lp_winbind_offline_logon()) {
2662 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2663 return False;
2666 if (global_winbindd_offline_state) {
2667 /* Already offline. */
2668 return True;
2671 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2673 if (!data.dptr || data.dsize != 4) {
2674 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2675 SAFE_FREE(data.dptr);
2676 return False;
2677 } else {
2678 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2679 global_winbindd_offline_state = True;
2680 SAFE_FREE(data.dptr);
2681 return True;
2685 void set_global_winbindd_state_online(void)
2687 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2689 if (!lp_winbind_offline_logon()) {
2690 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2691 return;
2694 if (!global_winbindd_offline_state) {
2695 /* Already online. */
2696 return;
2698 global_winbindd_offline_state = False;
2700 if (!wcache->tdb) {
2701 return;
2704 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2705 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2708 BOOL get_global_winbindd_state_offline(void)
2710 return global_winbindd_offline_state;
2713 /* the cache backend methods are exposed via this structure */
2714 struct winbindd_methods cache_methods = {
2715 True,
2716 query_user_list,
2717 enum_dom_groups,
2718 enum_local_groups,
2719 name_to_sid,
2720 sid_to_name,
2721 rids_to_names,
2722 query_user,
2723 lookup_usergroups,
2724 lookup_useraliases,
2725 lookup_groupmem,
2726 sequence_number,
2727 lockout_policy,
2728 password_policy,
2729 trusted_domains