r19158: Remove root and nobody users from ldif, from Björn Jacke
[Samba/nascimento.git] / source3 / nsswitch / winbindd_cache.c
blob745a9e262a31733082e35836921a0fd8d1ba5d6b
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 /* Global online/offline state - False when online. winbindd starts up online
33 and sets this to true if the first query fails and there's an entry in
34 the cache tdb telling us to stay offline. */
36 static BOOL global_winbindd_offline_state;
38 struct winbind_cache {
39 TDB_CONTEXT *tdb;
42 struct cache_entry {
43 NTSTATUS status;
44 uint32 sequence_number;
45 uint8 *data;
46 uint32 len, ofs;
49 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
51 static struct winbind_cache *wcache;
53 void winbindd_check_cache_size(time_t t)
55 static time_t last_check_time;
56 struct stat st;
58 if (last_check_time == (time_t)0)
59 last_check_time = t;
61 if (t - last_check_time < 60 && t - last_check_time > 0)
62 return;
64 if (wcache == NULL || wcache->tdb == NULL) {
65 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
66 return;
69 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
70 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
71 return;
74 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
75 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
76 (unsigned long)st.st_size,
77 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
78 wcache_flush_cache();
82 /* get the winbind_cache structure */
83 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
85 struct winbind_cache *ret = wcache;
86 #ifdef HAVE_ADS
87 struct winbindd_domain *our_domain = domain;
88 #endif
90 /* we have to know what type of domain we are dealing with first */
92 if ( !domain->initialized )
93 init_dc_connection( domain );
95 /*
96 OK. listen up becasue I'm only going to say this once.
97 We have the following scenarios to consider
98 (a) trusted AD domains on a Samba DC,
99 (b) trusted AD domains and we are joined to a non-kerberos domain
100 (c) trusted AD domains and we are joined to a kerberos (AD) domain
102 For (a) we can always contact the trusted domain using krb5
103 since we have the domain trust account password
105 For (b) we can only use RPC since we have no way of
106 getting a krb5 ticket in our own domain
108 For (c) we can always use krb5 since we have a kerberos trust
110 --jerry
113 if (!domain->backend) {
114 extern struct winbindd_methods reconnect_methods;
115 #ifdef HAVE_ADS
116 extern struct winbindd_methods ads_methods;
118 /* find our domain first so we can figure out if we
119 are joined to a kerberized domain */
121 if ( !domain->primary )
122 our_domain = find_our_domain();
124 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
125 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
126 domain->backend = &ads_methods;
127 } else {
128 #endif /* HAVE_ADS */
129 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
130 domain->backend = &reconnect_methods;
131 #ifdef HAVE_ADS
133 #endif /* HAVE_ADS */
136 if (ret)
137 return ret;
139 ret = SMB_XMALLOC_P(struct winbind_cache);
140 ZERO_STRUCTP(ret);
142 wcache = ret;
143 wcache_flush_cache();
145 return ret;
149 free a centry structure
151 static void centry_free(struct cache_entry *centry)
153 if (!centry)
154 return;
155 SAFE_FREE(centry->data);
156 free(centry);
160 pull a uint32 from a cache entry
162 static uint32 centry_uint32(struct cache_entry *centry)
164 uint32 ret;
165 if (centry->len - centry->ofs < 4) {
166 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
167 centry->len - centry->ofs));
168 smb_panic("centry_uint32");
170 ret = IVAL(centry->data, centry->ofs);
171 centry->ofs += 4;
172 return ret;
176 pull a uint16 from a cache entry
178 static uint16 centry_uint16(struct cache_entry *centry)
180 uint16 ret;
181 if (centry->len - centry->ofs < 2) {
182 DEBUG(0,("centry corruption? needed 2 bytes, have %d\n",
183 centry->len - centry->ofs));
184 smb_panic("centry_uint16");
186 ret = CVAL(centry->data, centry->ofs);
187 centry->ofs += 2;
188 return ret;
192 pull a uint8 from a cache entry
194 static uint8 centry_uint8(struct cache_entry *centry)
196 uint8 ret;
197 if (centry->len - centry->ofs < 1) {
198 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
199 centry->len - centry->ofs));
200 smb_panic("centry_uint32");
202 ret = CVAL(centry->data, centry->ofs);
203 centry->ofs += 1;
204 return ret;
208 pull a NTTIME from a cache entry
210 static NTTIME centry_nttime(struct cache_entry *centry)
212 NTTIME ret;
213 if (centry->len - centry->ofs < 8) {
214 DEBUG(0,("centry corruption? needed 8 bytes, have %d\n",
215 centry->len - centry->ofs));
216 smb_panic("centry_nttime");
218 ret = IVAL(centry->data, centry->ofs);
219 centry->ofs += 4;
220 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
221 centry->ofs += 4;
222 return ret;
226 pull a time_t from a cache entry
228 static time_t centry_time(struct cache_entry *centry)
230 time_t ret;
231 if (centry->len - centry->ofs < sizeof(time_t)) {
232 DEBUG(0,("centry corruption? needed %u bytes, have %u\n",
233 (unsigned int)sizeof(time_t), (unsigned int)(centry->len - centry->ofs)));
234 smb_panic("centry_time");
236 ret = IVAL(centry->data, centry->ofs); /* FIXME: correct ? */
237 centry->ofs += sizeof(time_t);
238 return ret;
241 /* pull a string from a cache entry, using the supplied
242 talloc context
244 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
246 uint32 len;
247 char *ret;
249 len = centry_uint8(centry);
251 if (len == 0xFF) {
252 /* a deliberate NULL string */
253 return NULL;
256 if (centry->len - centry->ofs < len) {
257 DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
258 len, centry->len - centry->ofs));
259 smb_panic("centry_string");
262 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
263 if (!ret) {
264 smb_panic("centry_string out of memory\n");
266 memcpy(ret,centry->data + centry->ofs, len);
267 ret[len] = 0;
268 centry->ofs += len;
269 return ret;
272 /* pull a hash16 from a cache entry, using the supplied
273 talloc context
275 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
277 uint32 len;
278 char *ret;
280 len = centry_uint8(centry);
282 if (len != 16) {
283 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
284 len ));
285 return NULL;
288 if (centry->len - centry->ofs < 16) {
289 DEBUG(0,("centry corruption? needed 16 bytes, have %d\n",
290 centry->len - centry->ofs));
291 return NULL;
294 ret = TALLOC_ARRAY(mem_ctx, char, 16);
295 if (!ret) {
296 smb_panic("centry_hash out of memory\n");
298 memcpy(ret,centry->data + centry->ofs, 16);
299 centry->ofs += 16;
300 return ret;
303 /* pull a sid from a cache entry, using the supplied
304 talloc context
306 static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
308 char *sid_string;
309 sid_string = centry_string(centry, mem_ctx);
310 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
311 return False;
313 return True;
316 /* the server is considered down if it can't give us a sequence number */
317 static BOOL wcache_server_down(struct winbindd_domain *domain)
319 BOOL ret;
321 if (!wcache->tdb)
322 return False;
324 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
326 if (ret)
327 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
328 domain->name ));
329 return ret;
332 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
334 TDB_DATA data;
335 fstring key;
336 uint32 time_diff;
338 if (!wcache->tdb) {
339 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
340 return NT_STATUS_UNSUCCESSFUL;
343 fstr_sprintf( key, "SEQNUM/%s", domain->name );
345 data = tdb_fetch_bystring( wcache->tdb, key );
346 if ( !data.dptr || data.dsize!=8 ) {
347 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
348 return NT_STATUS_UNSUCCESSFUL;
351 domain->sequence_number = IVAL(data.dptr, 0);
352 domain->last_seq_check = IVAL(data.dptr, 4);
354 SAFE_FREE(data.dptr);
356 /* have we expired? */
358 time_diff = now - domain->last_seq_check;
359 if ( time_diff > lp_winbind_cache_time() ) {
360 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
361 domain->name, domain->sequence_number,
362 (uint32)domain->last_seq_check));
363 return NT_STATUS_UNSUCCESSFUL;
366 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
367 domain->name, domain->sequence_number,
368 (uint32)domain->last_seq_check));
370 return NT_STATUS_OK;
373 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
375 TDB_DATA data, key;
376 fstring key_str;
377 char buf[8];
379 if (!wcache->tdb) {
380 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
381 return NT_STATUS_UNSUCCESSFUL;
384 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
385 key.dptr = key_str;
386 key.dsize = strlen(key_str)+1;
388 SIVAL(buf, 0, domain->sequence_number);
389 SIVAL(buf, 4, domain->last_seq_check);
390 data.dptr = buf;
391 data.dsize = 8;
393 if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
394 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
395 return NT_STATUS_UNSUCCESSFUL;
398 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
399 domain->name, domain->sequence_number,
400 (uint32)domain->last_seq_check));
402 return NT_STATUS_OK;
406 refresh the domain sequence number. If force is True
407 then always refresh it, no matter how recently we fetched it
410 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
412 NTSTATUS status;
413 unsigned time_diff;
414 time_t t = time(NULL);
415 unsigned cache_time = lp_winbind_cache_time();
417 get_cache( domain );
419 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
420 /* trying to reconnect is expensive, don't do it too often */
421 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
422 cache_time *= 8;
424 #endif
426 time_diff = t - domain->last_seq_check;
428 /* see if we have to refetch the domain sequence number */
429 if (!force && (time_diff < cache_time)) {
430 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
431 goto done;
434 /* try to get the sequence number from the tdb cache first */
435 /* this will update the timestamp as well */
437 status = fetch_cache_seqnum( domain, t );
438 if ( NT_STATUS_IS_OK(status) )
439 goto done;
441 /* important! make sure that we know if this is a native
442 mode domain or not */
444 status = domain->backend->sequence_number(domain, &domain->sequence_number);
446 if (!NT_STATUS_IS_OK(status)) {
447 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
448 domain->sequence_number = DOM_SEQUENCE_NONE;
451 domain->last_status = status;
452 domain->last_seq_check = time(NULL);
454 /* save the new sequence number ni the cache */
455 store_cache_seqnum( domain );
457 done:
458 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
459 domain->name, domain->sequence_number));
461 return;
465 decide if a cache entry has expired
467 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
469 /* If we've been told to be offline - stay in that state... */
470 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
471 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
472 keystr, domain->name ));
473 return False;
476 /* when the domain is offline return the cached entry.
477 * This deals with transient offline states... */
479 if (!domain->online) {
480 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
481 keystr, domain->name ));
482 return False;
485 /* if the server is OK and our cache entry came from when it was down then
486 the entry is invalid */
487 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
488 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
489 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
490 keystr, domain->name ));
491 return True;
494 /* if the server is down or the cache entry is not older than the
495 current sequence number then it is OK */
496 if (wcache_server_down(domain) ||
497 centry->sequence_number == domain->sequence_number) {
498 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
499 keystr, domain->name ));
500 return False;
503 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
504 keystr, domain->name ));
506 /* it's expired */
507 return True;
510 static struct cache_entry *wcache_fetch_raw(char *kstr)
512 TDB_DATA data;
513 struct cache_entry *centry;
514 TDB_DATA key;
516 key.dptr = kstr;
517 key.dsize = strlen(kstr);
518 data = tdb_fetch(wcache->tdb, key);
519 if (!data.dptr) {
520 /* a cache miss */
521 return NULL;
524 centry = SMB_XMALLOC_P(struct cache_entry);
525 centry->data = (unsigned char *)data.dptr;
526 centry->len = data.dsize;
527 centry->ofs = 0;
529 if (centry->len < 8) {
530 /* huh? corrupt cache? */
531 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
532 centry_free(centry);
533 return NULL;
536 centry->status = NT_STATUS(centry_uint32(centry));
537 centry->sequence_number = centry_uint32(centry);
539 return centry;
543 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
544 number and return status
546 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
547 struct winbindd_domain *domain,
548 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
549 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
550 struct winbindd_domain *domain,
551 const char *format, ...)
553 va_list ap;
554 char *kstr;
555 struct cache_entry *centry;
557 extern BOOL opt_nocache;
559 if (opt_nocache) {
560 return NULL;
563 refresh_sequence_number(domain, False);
565 va_start(ap, format);
566 smb_xvasprintf(&kstr, format, ap);
567 va_end(ap);
569 centry = wcache_fetch_raw(kstr);
570 if (centry == NULL) {
571 free(kstr);
572 return NULL;
575 if (centry_expired(domain, kstr, centry)) {
577 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
578 kstr, domain->name ));
580 centry_free(centry);
581 free(kstr);
582 return NULL;
585 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
586 kstr, domain->name ));
588 free(kstr);
589 return centry;
592 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
593 static void wcache_delete(const char *format, ...)
595 va_list ap;
596 char *kstr;
597 TDB_DATA key;
599 va_start(ap, format);
600 smb_xvasprintf(&kstr, format, ap);
601 va_end(ap);
603 key.dptr = kstr;
604 key.dsize = strlen(kstr);
606 tdb_delete(wcache->tdb, key);
607 free(kstr);
611 make sure we have at least len bytes available in a centry
613 static void centry_expand(struct cache_entry *centry, uint32 len)
615 if (centry->len - centry->ofs >= len)
616 return;
617 centry->len *= 2;
618 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
619 centry->len);
620 if (!centry->data) {
621 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
622 smb_panic("out of memory in centry_expand");
627 push a uint32 into a centry
629 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
631 centry_expand(centry, 4);
632 SIVAL(centry->data, centry->ofs, v);
633 centry->ofs += 4;
637 push a uint16 into a centry
639 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
641 centry_expand(centry, 2);
642 SIVAL(centry->data, centry->ofs, v);
643 centry->ofs += 2;
647 push a uint8 into a centry
649 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
651 centry_expand(centry, 1);
652 SCVAL(centry->data, centry->ofs, v);
653 centry->ofs += 1;
657 push a string into a centry
659 static void centry_put_string(struct cache_entry *centry, const char *s)
661 int len;
663 if (!s) {
664 /* null strings are marked as len 0xFFFF */
665 centry_put_uint8(centry, 0xFF);
666 return;
669 len = strlen(s);
670 /* can't handle more than 254 char strings. Truncating is probably best */
671 if (len > 254) {
672 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
673 len = 254;
675 centry_put_uint8(centry, len);
676 centry_expand(centry, len);
677 memcpy(centry->data + centry->ofs, s, len);
678 centry->ofs += len;
682 push a 16 byte hash into a centry - treat as 16 byte string.
684 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
686 centry_put_uint8(centry, 16);
687 centry_expand(centry, 16);
688 memcpy(centry->data + centry->ofs, val, 16);
689 centry->ofs += 16;
692 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
694 fstring sid_string;
695 centry_put_string(centry, sid_to_string(sid_string, sid));
699 push a NTTIME into a centry
701 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
703 centry_expand(centry, 8);
704 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
705 centry->ofs += 4;
706 SIVAL(centry->data, centry->ofs, nt >> 32);
707 centry->ofs += 4;
711 push a time_t into a centry
713 static void centry_put_time(struct cache_entry *centry, time_t t)
715 centry_expand(centry, sizeof(time_t));
716 SIVAL(centry->data, centry->ofs, t); /* FIXME: is this correct ?? */
717 centry->ofs += sizeof(time_t);
721 start a centry for output. When finished, call centry_end()
723 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
725 struct cache_entry *centry;
727 if (!wcache->tdb)
728 return NULL;
730 centry = SMB_XMALLOC_P(struct cache_entry);
732 centry->len = 8192; /* reasonable default */
733 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
734 centry->ofs = 0;
735 centry->sequence_number = domain->sequence_number;
736 centry_put_uint32(centry, NT_STATUS_V(status));
737 centry_put_uint32(centry, centry->sequence_number);
738 return centry;
742 finish a centry and write it to the tdb
744 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
745 static void centry_end(struct cache_entry *centry, const char *format, ...)
747 va_list ap;
748 char *kstr;
749 TDB_DATA key, data;
751 va_start(ap, format);
752 smb_xvasprintf(&kstr, format, ap);
753 va_end(ap);
755 key.dptr = kstr;
756 key.dsize = strlen(kstr);
757 data.dptr = (char *)centry->data;
758 data.dsize = centry->ofs;
760 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
761 free(kstr);
764 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
765 NTSTATUS status, const char *domain_name,
766 const char *name, const DOM_SID *sid,
767 enum lsa_SidType type)
769 struct cache_entry *centry;
770 fstring uname;
772 centry = centry_start(domain, status);
773 if (!centry)
774 return;
775 centry_put_uint32(centry, type);
776 centry_put_sid(centry, sid);
777 fstrcpy(uname, name);
778 strupper_m(uname);
779 centry_end(centry, "NS/%s/%s", domain_name, uname);
780 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
781 sid_string_static(sid)));
782 centry_free(centry);
785 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
786 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
788 struct cache_entry *centry;
789 fstring sid_string;
791 if (is_null_sid(sid)) {
792 return;
795 centry = centry_start(domain, status);
796 if (!centry)
797 return;
798 if (NT_STATUS_IS_OK(status)) {
799 centry_put_uint32(centry, type);
800 centry_put_string(centry, domain_name);
801 centry_put_string(centry, name);
803 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
804 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
805 centry_free(centry);
809 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
811 struct cache_entry *centry;
812 fstring sid_string;
814 if (is_null_sid(&info->user_sid)) {
815 return;
818 centry = centry_start(domain, status);
819 if (!centry)
820 return;
821 centry_put_string(centry, info->acct_name);
822 centry_put_string(centry, info->full_name);
823 centry_put_string(centry, info->homedir);
824 centry_put_string(centry, info->shell);
825 centry_put_sid(centry, &info->user_sid);
826 centry_put_sid(centry, &info->group_sid);
827 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
828 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
829 centry_free(centry);
832 static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
834 struct cache_entry *centry;
836 centry = centry_start(domain, status);
837 if (!centry)
838 return;
840 centry_put_nttime(centry, lockout_policy->duration);
841 centry_put_nttime(centry, lockout_policy->reset_count);
842 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
844 centry_end(centry, "LOC_POL/%s", domain->name);
846 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
848 centry_free(centry);
851 static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
853 struct cache_entry *centry;
855 centry = centry_start(domain, status);
856 if (!centry)
857 return;
859 centry_put_uint16(centry, policy->min_length_password);
860 centry_put_uint16(centry, policy->password_history);
861 centry_put_uint32(centry, policy->password_properties);
862 centry_put_nttime(centry, policy->expire);
863 centry_put_nttime(centry, policy->min_passwordage);
865 centry_end(centry, "PWD_POL/%s", domain->name);
867 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
869 centry_free(centry);
872 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
874 struct winbind_cache *cache = get_cache(domain);
875 TDB_DATA data;
876 fstring key_str;
877 uint32 rid;
879 if (!cache->tdb) {
880 return NT_STATUS_INTERNAL_DB_ERROR;
883 if (is_null_sid(sid)) {
884 return NT_STATUS_INVALID_SID;
887 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
888 return NT_STATUS_INVALID_SID;
891 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
893 data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
894 if (!data.dptr) {
895 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
898 SAFE_FREE(data.dptr);
899 return NT_STATUS_OK;
902 /* Lookup creds for a SID - copes with old (unsalted) creds as well
903 as new salted ones. */
905 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
906 TALLOC_CTX *mem_ctx,
907 const DOM_SID *sid,
908 const uint8 **cached_nt_pass,
909 const uint8 **cached_salt)
911 struct winbind_cache *cache = get_cache(domain);
912 struct cache_entry *centry = NULL;
913 NTSTATUS status;
914 time_t t;
915 uint32 rid;
917 if (!cache->tdb) {
918 return NT_STATUS_INTERNAL_DB_ERROR;
921 if (is_null_sid(sid)) {
922 return NT_STATUS_INVALID_SID;
925 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
926 return NT_STATUS_INVALID_SID;
929 /* Try and get a salted cred first. If we can't
930 fall back to an unsalted cred. */
932 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
933 if (!centry) {
934 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
935 sid_string_static(sid)));
936 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
939 t = centry_time(centry);
941 /* In the salted case this isn't actually the nt_hash itself,
942 but the MD5 of the salt + nt_hash. Let the caller
943 sort this out. It can tell as we only return the cached_salt
944 if we are returning a salted cred. */
946 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
947 if (*cached_nt_pass == NULL) {
948 const char *sidstr = sid_string_static(sid);
950 /* Bad (old) cred cache. Delete and pretend we
951 don't have it. */
952 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
953 sidstr));
954 wcache_delete("CRED/%s", sidstr);
955 centry_free(centry);
956 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
959 /* We only have 17 bytes more data in the salted cred case. */
960 if (centry->len - centry->ofs == 17) {
961 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
962 } else {
963 *cached_salt = NULL;
966 #if DEBUG_PASSWORD
967 dump_data(100, (const char *)*cached_nt_pass, NT_HASH_LEN);
968 if (*cached_salt) {
969 dump_data(100, (const char *)*cached_salt, NT_HASH_LEN);
971 #endif
972 status = centry->status;
974 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
975 sid_string_static(sid), nt_errstr(status) ));
977 centry_free(centry);
978 return status;
981 /* Store creds for a SID - only writes out new salted ones. */
983 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
984 TALLOC_CTX *mem_ctx,
985 const DOM_SID *sid,
986 const uint8 nt_pass[NT_HASH_LEN])
988 struct cache_entry *centry;
989 fstring sid_string;
990 uint32 rid;
991 uint8 cred_salt[NT_HASH_LEN];
992 uint8 salted_hash[NT_HASH_LEN];
994 if (is_null_sid(sid)) {
995 return NT_STATUS_INVALID_SID;
998 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
999 return NT_STATUS_INVALID_SID;
1002 centry = centry_start(domain, NT_STATUS_OK);
1003 if (!centry) {
1004 return NT_STATUS_INTERNAL_DB_ERROR;
1007 #if DEBUG_PASSWORD
1008 dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
1009 #endif
1011 centry_put_time(centry, time(NULL));
1013 /* Create a salt and then salt the hash. */
1014 generate_random_buffer(cred_salt, NT_HASH_LEN);
1015 E_md5hash(cred_salt, nt_pass, salted_hash);
1017 centry_put_hash16(centry, salted_hash);
1018 centry_put_hash16(centry, cred_salt);
1019 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1021 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1023 centry_free(centry);
1025 return NT_STATUS_OK;
1029 /* Query display info. This is the basic user list fn */
1030 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1031 TALLOC_CTX *mem_ctx,
1032 uint32 *num_entries,
1033 WINBIND_USERINFO **info)
1035 struct winbind_cache *cache = get_cache(domain);
1036 struct cache_entry *centry = NULL;
1037 NTSTATUS status;
1038 unsigned int i, retry;
1040 if (!cache->tdb)
1041 goto do_query;
1043 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1044 if (!centry)
1045 goto do_query;
1047 *num_entries = centry_uint32(centry);
1049 if (*num_entries == 0)
1050 goto do_cached;
1052 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1053 if (! (*info))
1054 smb_panic("query_user_list out of memory");
1055 for (i=0; i<(*num_entries); i++) {
1056 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1057 (*info)[i].full_name = centry_string(centry, mem_ctx);
1058 (*info)[i].homedir = centry_string(centry, mem_ctx);
1059 (*info)[i].shell = centry_string(centry, mem_ctx);
1060 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1061 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1064 do_cached:
1065 status = centry->status;
1067 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1068 domain->name, nt_errstr(status) ));
1070 centry_free(centry);
1071 return status;
1073 do_query:
1074 *num_entries = 0;
1075 *info = NULL;
1077 /* Return status value returned by seq number check */
1079 if (!NT_STATUS_IS_OK(domain->last_status))
1080 return domain->last_status;
1082 /* Put the query_user_list() in a retry loop. There appears to be
1083 * some bug either with Windows 2000 or Samba's handling of large
1084 * rpc replies. This manifests itself as sudden disconnection
1085 * at a random point in the enumeration of a large (60k) user list.
1086 * The retry loop simply tries the operation again. )-: It's not
1087 * pretty but an acceptable workaround until we work out what the
1088 * real problem is. */
1090 retry = 0;
1091 do {
1093 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1094 domain->name ));
1096 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1097 if (!NT_STATUS_IS_OK(status))
1098 DEBUG(3, ("query_user_list: returned 0x%08x, "
1099 "retrying\n", NT_STATUS_V(status)));
1100 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1101 DEBUG(3, ("query_user_list: flushing "
1102 "connection cache\n"));
1103 invalidate_cm_connection(&domain->conn);
1106 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1107 (retry++ < 5));
1109 /* and save it */
1110 refresh_sequence_number(domain, False);
1111 centry = centry_start(domain, status);
1112 if (!centry)
1113 goto skip_save;
1114 centry_put_uint32(centry, *num_entries);
1115 for (i=0; i<(*num_entries); i++) {
1116 centry_put_string(centry, (*info)[i].acct_name);
1117 centry_put_string(centry, (*info)[i].full_name);
1118 centry_put_string(centry, (*info)[i].homedir);
1119 centry_put_string(centry, (*info)[i].shell);
1120 centry_put_sid(centry, &(*info)[i].user_sid);
1121 centry_put_sid(centry, &(*info)[i].group_sid);
1122 if (domain->backend->consistent) {
1123 /* when the backend is consistent we can pre-prime some mappings */
1124 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1125 domain->name,
1126 (*info)[i].acct_name,
1127 &(*info)[i].user_sid,
1128 SID_NAME_USER);
1129 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1130 &(*info)[i].user_sid,
1131 domain->name,
1132 (*info)[i].acct_name,
1133 SID_NAME_USER);
1134 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1137 centry_end(centry, "UL/%s", domain->name);
1138 centry_free(centry);
1140 skip_save:
1141 return status;
1144 /* list all domain groups */
1145 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1146 TALLOC_CTX *mem_ctx,
1147 uint32 *num_entries,
1148 struct acct_info **info)
1150 struct winbind_cache *cache = get_cache(domain);
1151 struct cache_entry *centry = NULL;
1152 NTSTATUS status;
1153 unsigned int i;
1155 if (!cache->tdb)
1156 goto do_query;
1158 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1159 if (!centry)
1160 goto do_query;
1162 *num_entries = centry_uint32(centry);
1164 if (*num_entries == 0)
1165 goto do_cached;
1167 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1168 if (! (*info))
1169 smb_panic("enum_dom_groups out of memory");
1170 for (i=0; i<(*num_entries); i++) {
1171 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1172 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1173 (*info)[i].rid = centry_uint32(centry);
1176 do_cached:
1177 status = centry->status;
1179 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1180 domain->name, nt_errstr(status) ));
1182 centry_free(centry);
1183 return status;
1185 do_query:
1186 *num_entries = 0;
1187 *info = NULL;
1189 /* Return status value returned by seq number check */
1191 if (!NT_STATUS_IS_OK(domain->last_status))
1192 return domain->last_status;
1194 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1195 domain->name ));
1197 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1199 /* and save it */
1200 refresh_sequence_number(domain, False);
1201 centry = centry_start(domain, status);
1202 if (!centry)
1203 goto skip_save;
1204 centry_put_uint32(centry, *num_entries);
1205 for (i=0; i<(*num_entries); i++) {
1206 centry_put_string(centry, (*info)[i].acct_name);
1207 centry_put_string(centry, (*info)[i].acct_desc);
1208 centry_put_uint32(centry, (*info)[i].rid);
1210 centry_end(centry, "GL/%s/domain", domain->name);
1211 centry_free(centry);
1213 skip_save:
1214 return status;
1217 /* list all domain groups */
1218 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1219 TALLOC_CTX *mem_ctx,
1220 uint32 *num_entries,
1221 struct acct_info **info)
1223 struct winbind_cache *cache = get_cache(domain);
1224 struct cache_entry *centry = NULL;
1225 NTSTATUS status;
1226 unsigned int i;
1228 if (!cache->tdb)
1229 goto do_query;
1231 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1232 if (!centry)
1233 goto do_query;
1235 *num_entries = centry_uint32(centry);
1237 if (*num_entries == 0)
1238 goto do_cached;
1240 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1241 if (! (*info))
1242 smb_panic("enum_dom_groups out of memory");
1243 for (i=0; i<(*num_entries); i++) {
1244 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1245 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1246 (*info)[i].rid = centry_uint32(centry);
1249 do_cached:
1251 /* If we are returning cached data and the domain controller
1252 is down then we don't know whether the data is up to date
1253 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1254 indicate this. */
1256 if (wcache_server_down(domain)) {
1257 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1258 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1259 } else
1260 status = centry->status;
1262 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1263 domain->name, nt_errstr(status) ));
1265 centry_free(centry);
1266 return status;
1268 do_query:
1269 *num_entries = 0;
1270 *info = NULL;
1272 /* Return status value returned by seq number check */
1274 if (!NT_STATUS_IS_OK(domain->last_status))
1275 return domain->last_status;
1277 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1278 domain->name ));
1280 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1282 /* and save it */
1283 refresh_sequence_number(domain, False);
1284 centry = centry_start(domain, status);
1285 if (!centry)
1286 goto skip_save;
1287 centry_put_uint32(centry, *num_entries);
1288 for (i=0; i<(*num_entries); i++) {
1289 centry_put_string(centry, (*info)[i].acct_name);
1290 centry_put_string(centry, (*info)[i].acct_desc);
1291 centry_put_uint32(centry, (*info)[i].rid);
1293 centry_end(centry, "GL/%s/local", domain->name);
1294 centry_free(centry);
1296 skip_save:
1297 return status;
1300 /* convert a single name to a sid in a domain */
1301 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1302 TALLOC_CTX *mem_ctx,
1303 const char *domain_name,
1304 const char *name,
1305 DOM_SID *sid,
1306 enum lsa_SidType *type)
1308 struct winbind_cache *cache = get_cache(domain);
1309 struct cache_entry *centry = NULL;
1310 NTSTATUS status;
1311 fstring uname;
1313 if (!cache->tdb)
1314 goto do_query;
1316 fstrcpy(uname, name);
1317 strupper_m(uname);
1318 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1319 if (!centry)
1320 goto do_query;
1321 *type = (enum lsa_SidType)centry_uint32(centry);
1322 status = centry->status;
1323 if (NT_STATUS_IS_OK(status)) {
1324 centry_sid(centry, mem_ctx, sid);
1327 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1328 domain->name, nt_errstr(status) ));
1330 centry_free(centry);
1331 return status;
1333 do_query:
1334 ZERO_STRUCTP(sid);
1336 /* If the seq number check indicated that there is a problem
1337 * with this DC, then return that status... except for
1338 * access_denied. This is special because the dc may be in
1339 * "restrict anonymous = 1" mode, in which case it will deny
1340 * most unauthenticated operations, but *will* allow the LSA
1341 * name-to-sid that we try as a fallback. */
1343 if (!(NT_STATUS_IS_OK(domain->last_status)
1344 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1345 return domain->last_status;
1347 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1348 domain->name ));
1350 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1352 /* and save it */
1353 if (domain->online && !is_null_sid(sid)) {
1354 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1357 if (NT_STATUS_IS_OK(status)) {
1358 strupper_m(CONST_DISCARD(char *,domain_name));
1359 strlower_m(CONST_DISCARD(char *,name));
1360 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1363 return status;
1366 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1367 given */
1368 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1369 TALLOC_CTX *mem_ctx,
1370 const DOM_SID *sid,
1371 char **domain_name,
1372 char **name,
1373 enum lsa_SidType *type)
1375 struct winbind_cache *cache = get_cache(domain);
1376 struct cache_entry *centry = NULL;
1377 NTSTATUS status;
1378 fstring sid_string;
1380 if (!cache->tdb)
1381 goto do_query;
1383 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1384 if (!centry)
1385 goto do_query;
1386 if (NT_STATUS_IS_OK(centry->status)) {
1387 *type = (enum lsa_SidType)centry_uint32(centry);
1388 *domain_name = centry_string(centry, mem_ctx);
1389 *name = centry_string(centry, mem_ctx);
1391 status = centry->status;
1393 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1394 domain->name, nt_errstr(status) ));
1396 centry_free(centry);
1397 return status;
1399 do_query:
1400 *name = NULL;
1401 *domain_name = NULL;
1403 /* If the seq number check indicated that there is a problem
1404 * with this DC, then return that status... except for
1405 * access_denied. This is special because the dc may be in
1406 * "restrict anonymous = 1" mode, in which case it will deny
1407 * most unauthenticated operations, but *will* allow the LSA
1408 * sid-to-name that we try as a fallback. */
1410 if (!(NT_STATUS_IS_OK(domain->last_status)
1411 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1412 return domain->last_status;
1414 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1415 domain->name ));
1417 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1419 /* and save it */
1420 refresh_sequence_number(domain, False);
1421 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1423 /* We can't save the name to sid mapping here, as with sid history a
1424 * later name2sid would give the wrong sid. */
1426 return status;
1429 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1430 TALLOC_CTX *mem_ctx,
1431 const DOM_SID *domain_sid,
1432 uint32 *rids,
1433 size_t num_rids,
1434 char **domain_name,
1435 char ***names,
1436 enum lsa_SidType **types)
1438 struct winbind_cache *cache = get_cache(domain);
1439 size_t i;
1440 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1441 BOOL have_mapped;
1442 BOOL have_unmapped;
1444 *domain_name = NULL;
1445 *names = NULL;
1446 *types = NULL;
1448 if (!cache->tdb) {
1449 goto do_query;
1452 if (num_rids == 0) {
1453 return NT_STATUS_OK;
1456 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1457 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1459 if ((*names == NULL) || (*types == NULL)) {
1460 result = NT_STATUS_NO_MEMORY;
1461 goto error;
1464 have_mapped = have_unmapped = False;
1466 for (i=0; i<num_rids; i++) {
1467 DOM_SID sid;
1468 struct cache_entry *centry;
1470 if (!sid_compose(&sid, domain_sid, rids[i])) {
1471 result = NT_STATUS_INTERNAL_ERROR;
1472 goto error;
1475 centry = wcache_fetch(cache, domain, "SN/%s",
1476 sid_string_static(&sid));
1477 if (!centry) {
1478 goto do_query;
1481 (*types)[i] = SID_NAME_UNKNOWN;
1482 (*names)[i] = talloc_strdup(*names, "");
1484 if (NT_STATUS_IS_OK(centry->status)) {
1485 char *dom;
1486 have_mapped = True;
1487 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1488 dom = centry_string(centry, mem_ctx);
1489 if (*domain_name == NULL) {
1490 *domain_name = dom;
1491 } else {
1492 talloc_free(dom);
1494 (*names)[i] = centry_string(centry, *names);
1495 } else {
1496 have_unmapped = True;
1499 centry_free(centry);
1502 if (!have_mapped) {
1503 return NT_STATUS_NONE_MAPPED;
1505 if (!have_unmapped) {
1506 return NT_STATUS_OK;
1508 return STATUS_SOME_UNMAPPED;
1510 do_query:
1512 TALLOC_FREE(*names);
1513 TALLOC_FREE(*types);
1515 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1516 rids, num_rids, domain_name,
1517 names, types);
1519 if (!NT_STATUS_IS_OK(result) &&
1520 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1521 return result;
1524 refresh_sequence_number(domain, False);
1526 for (i=0; i<num_rids; i++) {
1527 DOM_SID sid;
1528 NTSTATUS status;
1530 if (!sid_compose(&sid, domain_sid, rids[i])) {
1531 result = NT_STATUS_INTERNAL_ERROR;
1532 goto error;
1535 status = (*types)[i] == SID_NAME_UNKNOWN ?
1536 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1538 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1539 (*names)[i], (*types)[i]);
1542 return result;
1544 error:
1546 TALLOC_FREE(*names);
1547 TALLOC_FREE(*types);
1548 return result;
1551 /* Lookup user information from a rid */
1552 static NTSTATUS query_user(struct winbindd_domain *domain,
1553 TALLOC_CTX *mem_ctx,
1554 const DOM_SID *user_sid,
1555 WINBIND_USERINFO *info)
1557 struct winbind_cache *cache = get_cache(domain);
1558 struct cache_entry *centry = NULL;
1559 NTSTATUS status;
1561 if (!cache->tdb)
1562 goto do_query;
1564 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1566 /* If we have an access denied cache entry and a cached info3 in the
1567 samlogon cache then do a query. This will force the rpc back end
1568 to return the info3 data. */
1570 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1571 netsamlogon_cache_have(user_sid)) {
1572 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1573 domain->last_status = NT_STATUS_OK;
1574 centry_free(centry);
1575 goto do_query;
1578 if (!centry)
1579 goto do_query;
1581 info->acct_name = centry_string(centry, mem_ctx);
1582 info->full_name = centry_string(centry, mem_ctx);
1583 info->homedir = centry_string(centry, mem_ctx);
1584 info->shell = centry_string(centry, mem_ctx);
1585 centry_sid(centry, mem_ctx, &info->user_sid);
1586 centry_sid(centry, mem_ctx, &info->group_sid);
1587 status = centry->status;
1589 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1590 domain->name, nt_errstr(status) ));
1592 centry_free(centry);
1593 return status;
1595 do_query:
1596 ZERO_STRUCTP(info);
1598 /* Return status value returned by seq number check */
1600 if (!NT_STATUS_IS_OK(domain->last_status))
1601 return domain->last_status;
1603 DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1604 domain->name ));
1606 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1608 /* and save it */
1609 refresh_sequence_number(domain, False);
1610 wcache_save_user(domain, status, info);
1612 return status;
1616 /* Lookup groups a user is a member of. */
1617 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1618 TALLOC_CTX *mem_ctx,
1619 const DOM_SID *user_sid,
1620 uint32 *num_groups, DOM_SID **user_gids)
1622 struct winbind_cache *cache = get_cache(domain);
1623 struct cache_entry *centry = NULL;
1624 NTSTATUS status;
1625 unsigned int i;
1626 fstring sid_string;
1628 if (!cache->tdb)
1629 goto do_query;
1631 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1633 /* If we have an access denied cache entry and a cached info3 in the
1634 samlogon cache then do a query. This will force the rpc back end
1635 to return the info3 data. */
1637 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1638 netsamlogon_cache_have(user_sid)) {
1639 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1640 domain->last_status = NT_STATUS_OK;
1641 centry_free(centry);
1642 goto do_query;
1645 if (!centry)
1646 goto do_query;
1648 *num_groups = centry_uint32(centry);
1650 if (*num_groups == 0)
1651 goto do_cached;
1653 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1654 if (! (*user_gids))
1655 smb_panic("lookup_usergroups out of memory");
1656 for (i=0; i<(*num_groups); i++) {
1657 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1660 do_cached:
1661 status = centry->status;
1663 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1664 domain->name, nt_errstr(status) ));
1666 centry_free(centry);
1667 return status;
1669 do_query:
1670 (*num_groups) = 0;
1671 (*user_gids) = NULL;
1673 /* Return status value returned by seq number check */
1675 if (!NT_STATUS_IS_OK(domain->last_status))
1676 return domain->last_status;
1678 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1679 domain->name ));
1681 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1683 /* and save it */
1684 refresh_sequence_number(domain, False);
1685 centry = centry_start(domain, status);
1686 if (!centry)
1687 goto skip_save;
1688 centry_put_uint32(centry, *num_groups);
1689 for (i=0; i<(*num_groups); i++) {
1690 centry_put_sid(centry, &(*user_gids)[i]);
1692 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1693 centry_free(centry);
1695 skip_save:
1696 return status;
1699 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1700 TALLOC_CTX *mem_ctx,
1701 uint32 num_sids, const DOM_SID *sids,
1702 uint32 *num_aliases, uint32 **alias_rids)
1704 struct winbind_cache *cache = get_cache(domain);
1705 struct cache_entry *centry = NULL;
1706 NTSTATUS status;
1707 char *sidlist = talloc_strdup(mem_ctx, "");
1708 int i;
1710 if (!cache->tdb)
1711 goto do_query;
1713 if (num_sids == 0) {
1714 *num_aliases = 0;
1715 *alias_rids = NULL;
1716 return NT_STATUS_OK;
1719 /* We need to cache indexed by the whole list of SIDs, the aliases
1720 * resulting might come from any of the SIDs. */
1722 for (i=0; i<num_sids; i++) {
1723 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1724 sid_string_static(&sids[i]));
1725 if (sidlist == NULL)
1726 return NT_STATUS_NO_MEMORY;
1729 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1731 if (!centry)
1732 goto do_query;
1734 *num_aliases = centry_uint32(centry);
1735 *alias_rids = NULL;
1737 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1739 if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1740 centry_free(centry);
1741 return NT_STATUS_NO_MEMORY;
1744 for (i=0; i<(*num_aliases); i++)
1745 (*alias_rids)[i] = centry_uint32(centry);
1747 status = centry->status;
1749 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1750 "status %s\n", domain->name, nt_errstr(status)));
1752 centry_free(centry);
1753 return status;
1755 do_query:
1756 (*num_aliases) = 0;
1757 (*alias_rids) = NULL;
1759 if (!NT_STATUS_IS_OK(domain->last_status))
1760 return domain->last_status;
1762 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1763 "for domain %s\n", domain->name ));
1765 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1766 num_sids, sids,
1767 num_aliases, alias_rids);
1769 /* and save it */
1770 refresh_sequence_number(domain, False);
1771 centry = centry_start(domain, status);
1772 if (!centry)
1773 goto skip_save;
1774 centry_put_uint32(centry, *num_aliases);
1775 for (i=0; i<(*num_aliases); i++)
1776 centry_put_uint32(centry, (*alias_rids)[i]);
1777 centry_end(centry, "UA%s", sidlist);
1778 centry_free(centry);
1780 skip_save:
1781 return status;
1785 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1786 TALLOC_CTX *mem_ctx,
1787 const DOM_SID *group_sid, uint32 *num_names,
1788 DOM_SID **sid_mem, char ***names,
1789 uint32 **name_types)
1791 struct winbind_cache *cache = get_cache(domain);
1792 struct cache_entry *centry = NULL;
1793 NTSTATUS status;
1794 unsigned int i;
1795 fstring sid_string;
1797 if (!cache->tdb)
1798 goto do_query;
1800 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1801 if (!centry)
1802 goto do_query;
1804 *num_names = centry_uint32(centry);
1806 if (*num_names == 0)
1807 goto do_cached;
1809 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1810 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1811 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1813 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1814 smb_panic("lookup_groupmem out of memory");
1817 for (i=0; i<(*num_names); i++) {
1818 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1819 (*names)[i] = centry_string(centry, mem_ctx);
1820 (*name_types)[i] = centry_uint32(centry);
1823 do_cached:
1824 status = centry->status;
1826 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1827 domain->name, nt_errstr(status)));
1829 centry_free(centry);
1830 return status;
1832 do_query:
1833 (*num_names) = 0;
1834 (*sid_mem) = NULL;
1835 (*names) = NULL;
1836 (*name_types) = NULL;
1838 /* Return status value returned by seq number check */
1840 if (!NT_STATUS_IS_OK(domain->last_status))
1841 return domain->last_status;
1843 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1844 domain->name ));
1846 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1847 sid_mem, names, name_types);
1849 /* and save it */
1850 refresh_sequence_number(domain, False);
1851 centry = centry_start(domain, status);
1852 if (!centry)
1853 goto skip_save;
1854 centry_put_uint32(centry, *num_names);
1855 for (i=0; i<(*num_names); i++) {
1856 centry_put_sid(centry, &(*sid_mem)[i]);
1857 centry_put_string(centry, (*names)[i]);
1858 centry_put_uint32(centry, (*name_types)[i]);
1860 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1861 centry_free(centry);
1863 skip_save:
1864 return status;
1867 /* find the sequence number for a domain */
1868 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1870 refresh_sequence_number(domain, False);
1872 *seq = domain->sequence_number;
1874 return NT_STATUS_OK;
1877 /* enumerate trusted domains
1878 * (we need to have the list of trustdoms in the cache when we go offline) -
1879 * Guenther */
1880 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1881 TALLOC_CTX *mem_ctx,
1882 uint32 *num_domains,
1883 char ***names,
1884 char ***alt_names,
1885 DOM_SID **dom_sids)
1887 struct winbind_cache *cache = get_cache(domain);
1888 struct cache_entry *centry = NULL;
1889 NTSTATUS status;
1890 int i;
1892 if (!cache->tdb)
1893 goto do_query;
1895 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1897 if (!centry) {
1898 goto do_query;
1901 *num_domains = centry_uint32(centry);
1903 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1904 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1905 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1907 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1908 smb_panic("trusted_domains out of memory");
1911 for (i=0; i<(*num_domains); i++) {
1912 (*names)[i] = centry_string(centry, mem_ctx);
1913 (*alt_names)[i] = centry_string(centry, mem_ctx);
1914 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1917 status = centry->status;
1919 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1920 domain->name, *num_domains, nt_errstr(status) ));
1922 centry_free(centry);
1923 return status;
1925 do_query:
1926 (*num_domains) = 0;
1927 (*dom_sids) = NULL;
1928 (*names) = NULL;
1929 (*alt_names) = NULL;
1931 /* Return status value returned by seq number check */
1933 if (!NT_STATUS_IS_OK(domain->last_status))
1934 return domain->last_status;
1936 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1937 domain->name ));
1939 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1940 names, alt_names, dom_sids);
1942 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1943 * so that the generic centry handling still applies correctly -
1944 * Guenther*/
1946 if (!NT_STATUS_IS_ERR(status)) {
1947 status = NT_STATUS_OK;
1950 /* and save it */
1951 refresh_sequence_number(domain, False);
1953 centry = centry_start(domain, status);
1954 if (!centry)
1955 goto skip_save;
1957 centry_put_uint32(centry, *num_domains);
1959 for (i=0; i<(*num_domains); i++) {
1960 centry_put_string(centry, (*names)[i]);
1961 centry_put_string(centry, (*alt_names)[i]);
1962 centry_put_sid(centry, &(*dom_sids)[i]);
1965 centry_end(centry, "TRUSTDOMS/%s", domain->name);
1967 centry_free(centry);
1969 skip_save:
1970 return status;
1973 /* get lockout policy */
1974 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1975 TALLOC_CTX *mem_ctx,
1976 SAM_UNK_INFO_12 *policy){
1977 struct winbind_cache *cache = get_cache(domain);
1978 struct cache_entry *centry = NULL;
1979 NTSTATUS status;
1981 if (!cache->tdb)
1982 goto do_query;
1984 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
1986 if (!centry)
1987 goto do_query;
1989 policy->duration = centry_nttime(centry);
1990 policy->reset_count = centry_nttime(centry);
1991 policy->bad_attempt_lockout = centry_uint16(centry);
1993 status = centry->status;
1995 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
1996 domain->name, nt_errstr(status) ));
1998 centry_free(centry);
1999 return status;
2001 do_query:
2002 ZERO_STRUCTP(policy);
2004 /* Return status value returned by seq number check */
2006 if (!NT_STATUS_IS_OK(domain->last_status))
2007 return domain->last_status;
2009 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2010 domain->name ));
2012 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2014 /* and save it */
2015 refresh_sequence_number(domain, False);
2016 wcache_save_lockout_policy(domain, status, policy);
2018 return status;
2021 /* get password policy */
2022 static NTSTATUS password_policy(struct winbindd_domain *domain,
2023 TALLOC_CTX *mem_ctx,
2024 SAM_UNK_INFO_1 *policy)
2026 struct winbind_cache *cache = get_cache(domain);
2027 struct cache_entry *centry = NULL;
2028 NTSTATUS status;
2030 if (!cache->tdb)
2031 goto do_query;
2033 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2035 if (!centry)
2036 goto do_query;
2038 policy->min_length_password = centry_uint16(centry);
2039 policy->password_history = centry_uint16(centry);
2040 policy->password_properties = centry_uint32(centry);
2041 policy->expire = centry_nttime(centry);
2042 policy->min_passwordage = centry_nttime(centry);
2044 status = centry->status;
2046 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2047 domain->name, nt_errstr(status) ));
2049 centry_free(centry);
2050 return status;
2052 do_query:
2053 ZERO_STRUCTP(policy);
2055 /* Return status value returned by seq number check */
2057 if (!NT_STATUS_IS_OK(domain->last_status))
2058 return domain->last_status;
2060 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2061 domain->name ));
2063 status = domain->backend->password_policy(domain, mem_ctx, policy);
2065 /* and save it */
2066 refresh_sequence_number(domain, False);
2067 wcache_save_password_policy(domain, status, policy);
2069 return status;
2073 /* Invalidate cached user and group lists coherently */
2075 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2076 void *state)
2078 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
2079 strncmp(kbuf.dptr, "GL/", 3) == 0)
2080 tdb_delete(the_tdb, kbuf);
2082 return 0;
2085 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2087 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2088 NET_USER_INFO_3 *info3)
2090 struct winbind_cache *cache;
2092 if (!domain)
2093 return;
2095 cache = get_cache(domain);
2096 netsamlogon_clear_cached_user(cache->tdb, info3);
2099 void wcache_invalidate_cache(void)
2101 struct winbindd_domain *domain;
2103 for (domain = domain_list(); domain; domain = domain->next) {
2104 struct winbind_cache *cache = get_cache(domain);
2106 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2107 "entries for %s\n", domain->name));
2108 if (cache)
2109 tdb_traverse(cache->tdb, traverse_fn, NULL);
2113 static BOOL init_wcache(void)
2115 if (wcache == NULL) {
2116 wcache = SMB_XMALLOC_P(struct winbind_cache);
2117 ZERO_STRUCTP(wcache);
2120 if (wcache->tdb != NULL)
2121 return True;
2123 /* when working offline we must not clear the cache on restart */
2124 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2125 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2126 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2127 O_RDWR|O_CREAT, 0600);
2129 if (wcache->tdb == NULL) {
2130 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2131 return False;
2134 return True;
2137 void cache_store_response(pid_t pid, struct winbindd_response *response)
2139 fstring key_str;
2141 if (!init_wcache())
2142 return;
2144 DEBUG(10, ("Storing response for pid %d, len %d\n",
2145 pid, response->length));
2147 fstr_sprintf(key_str, "DR/%d", pid);
2148 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2149 make_tdb_data((const char *)response, sizeof(*response)),
2150 TDB_REPLACE) == -1)
2151 return;
2153 if (response->length == sizeof(*response))
2154 return;
2156 /* There's extra data */
2158 DEBUG(10, ("Storing extra data: len=%d\n",
2159 (int)(response->length - sizeof(*response))));
2161 fstr_sprintf(key_str, "DE/%d", pid);
2162 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2163 make_tdb_data((const char *)response->extra_data.data,
2164 response->length - sizeof(*response)),
2165 TDB_REPLACE) == 0)
2166 return;
2168 /* We could not store the extra data, make sure the tdb does not
2169 * contain a main record with wrong dangling extra data */
2171 fstr_sprintf(key_str, "DR/%d", pid);
2172 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2174 return;
2177 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2179 TDB_DATA data;
2180 fstring key_str;
2182 if (!init_wcache())
2183 return False;
2185 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2187 fstr_sprintf(key_str, "DR/%d", pid);
2188 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2190 if (data.dptr == NULL)
2191 return False;
2193 if (data.dsize != sizeof(*response))
2194 return False;
2196 memcpy(response, data.dptr, data.dsize);
2197 SAFE_FREE(data.dptr);
2199 if (response->length == sizeof(*response)) {
2200 response->extra_data.data = NULL;
2201 return True;
2204 /* There's extra data */
2206 DEBUG(10, ("Retrieving extra data length=%d\n",
2207 (int)(response->length - sizeof(*response))));
2209 fstr_sprintf(key_str, "DE/%d", pid);
2210 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2212 if (data.dptr == NULL) {
2213 DEBUG(0, ("Did not find extra data\n"));
2214 return False;
2217 if (data.dsize != (response->length - sizeof(*response))) {
2218 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2219 SAFE_FREE(data.dptr);
2220 return False;
2223 dump_data(11, data.dptr, data.dsize);
2225 response->extra_data.data = data.dptr;
2226 return True;
2229 void cache_cleanup_response(pid_t pid)
2231 fstring key_str;
2233 if (!init_wcache())
2234 return;
2236 fstr_sprintf(key_str, "DR/%d", pid);
2237 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2239 fstr_sprintf(key_str, "DE/%d", pid);
2240 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2242 return;
2246 BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2247 const char **domain_name, const char **name,
2248 enum lsa_SidType *type)
2250 struct winbindd_domain *domain;
2251 struct winbind_cache *cache;
2252 struct cache_entry *centry = NULL;
2253 NTSTATUS status;
2255 domain = find_lookup_domain_from_sid(sid);
2256 if (domain == NULL) {
2257 return False;
2260 cache = get_cache(domain);
2262 if (cache->tdb == NULL) {
2263 return False;
2266 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2267 if (centry == NULL) {
2268 return False;
2271 if (NT_STATUS_IS_OK(centry->status)) {
2272 *type = (enum lsa_SidType)centry_uint32(centry);
2273 *domain_name = centry_string(centry, mem_ctx);
2274 *name = centry_string(centry, mem_ctx);
2277 status = centry->status;
2278 centry_free(centry);
2279 return NT_STATUS_IS_OK(status);
2282 BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2283 const char *domain_name,
2284 const char *name,
2285 DOM_SID *sid,
2286 enum lsa_SidType *type)
2288 struct winbindd_domain *domain;
2289 struct winbind_cache *cache;
2290 struct cache_entry *centry = NULL;
2291 NTSTATUS status;
2292 fstring uname;
2294 domain = find_lookup_domain_from_name(domain_name);
2295 if (domain == NULL) {
2296 return False;
2299 cache = get_cache(domain);
2301 if (cache->tdb == NULL) {
2302 return False;
2305 fstrcpy(uname, name);
2306 strupper_m(uname);
2308 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2309 if (centry == NULL) {
2310 return False;
2313 if (NT_STATUS_IS_OK(centry->status)) {
2314 *type = (enum lsa_SidType)centry_uint32(centry);
2315 centry_sid(centry, mem_ctx, sid);
2318 status = centry->status;
2319 centry_free(centry);
2321 return NT_STATUS_IS_OK(status);
2324 void cache_name2sid(struct winbindd_domain *domain,
2325 const char *domain_name, const char *name,
2326 enum lsa_SidType type, const DOM_SID *sid)
2328 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2329 sid, type);
2332 /* delete all centries that don't have NT_STATUS_OK set */
2333 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2334 TDB_DATA dbuf, void *state)
2336 struct cache_entry *centry;
2338 centry = wcache_fetch_raw(kbuf.dptr);
2339 if (!centry) {
2340 return 0;
2343 if (!NT_STATUS_IS_OK(centry->status)) {
2344 DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2345 tdb_delete(the_tdb, kbuf);
2348 centry_free(centry);
2349 return 0;
2352 /* flush the cache */
2353 void wcache_flush_cache(void)
2355 extern BOOL opt_nocache;
2357 if (!wcache)
2358 return;
2359 if (wcache->tdb) {
2360 tdb_close(wcache->tdb);
2361 wcache->tdb = NULL;
2363 if (opt_nocache)
2364 return;
2366 /* when working offline we must not clear the cache on restart */
2367 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2368 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2369 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2370 O_RDWR|O_CREAT, 0600);
2372 if (!wcache->tdb) {
2373 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2374 return;
2377 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2379 DEBUG(10,("wcache_flush_cache success\n"));
2382 /* Count cached creds */
2384 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2385 void *state)
2387 int *cred_count = (int*)state;
2389 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2390 (*cred_count)++;
2392 return 0;
2395 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2397 struct winbind_cache *cache = get_cache(domain);
2399 *count = 0;
2401 if (!cache->tdb) {
2402 return NT_STATUS_INTERNAL_DB_ERROR;
2405 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2407 return NT_STATUS_OK;
2410 struct cred_list {
2411 struct cred_list *prev, *next;
2412 TDB_DATA key;
2413 fstring name;
2414 time_t created;
2416 static struct cred_list *wcache_cred_list;
2418 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2419 void *state)
2421 struct cred_list *cred;
2423 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2425 cred = SMB_MALLOC_P(struct cred_list);
2426 if (cred == NULL) {
2427 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2428 return -1;
2431 ZERO_STRUCTP(cred);
2433 /* save a copy of the key */
2435 fstrcpy(cred->name, kbuf.dptr);
2436 DLIST_ADD(wcache_cred_list, cred);
2439 return 0;
2442 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2444 struct winbind_cache *cache = get_cache(domain);
2445 NTSTATUS status;
2446 int ret;
2447 struct cred_list *cred, *oldest = NULL;
2449 if (!cache->tdb) {
2450 return NT_STATUS_INTERNAL_DB_ERROR;
2453 /* we possibly already have an entry */
2454 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2456 fstring key_str;
2458 DEBUG(11,("we already have an entry, deleting that\n"));
2460 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2462 tdb_delete(cache->tdb, string_tdb_data(key_str));
2464 return NT_STATUS_OK;
2467 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2468 if (ret == 0) {
2469 return NT_STATUS_OK;
2470 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2471 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2474 ZERO_STRUCTP(oldest);
2476 for (cred = wcache_cred_list; cred; cred = cred->next) {
2478 TDB_DATA data;
2479 time_t t;
2481 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2482 if (!data.dptr) {
2483 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2484 cred->name));
2485 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2486 goto done;
2489 t = IVAL(data.dptr, 0);
2490 SAFE_FREE(data.dptr);
2492 if (!oldest) {
2493 oldest = SMB_MALLOC_P(struct cred_list);
2494 if (oldest == NULL) {
2495 status = NT_STATUS_NO_MEMORY;
2496 goto done;
2499 fstrcpy(oldest->name, cred->name);
2500 oldest->created = t;
2501 continue;
2504 if (t < oldest->created) {
2505 fstrcpy(oldest->name, cred->name);
2506 oldest->created = t;
2510 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2511 status = NT_STATUS_OK;
2512 } else {
2513 status = NT_STATUS_UNSUCCESSFUL;
2515 done:
2516 SAFE_FREE(wcache_cred_list);
2517 SAFE_FREE(oldest);
2519 return status;
2522 /* Change the global online/offline state. */
2523 BOOL set_global_winbindd_state_offline(void)
2525 TDB_DATA data;
2526 int err;
2528 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2530 /* Only go offline if someone has created
2531 the key "WINBINDD_OFFLINE" in the cache tdb. */
2533 if (wcache == NULL || wcache->tdb == NULL) {
2534 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2535 return False;
2538 if (!lp_winbind_offline_logon()) {
2539 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2540 return False;
2543 if (global_winbindd_offline_state) {
2544 /* Already offline. */
2545 return True;
2548 /* wcache->tdb->ecode = 0; */
2550 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2552 /* As this is a key with no data we don't need to free, we
2553 check for existence by looking at tdb_err. */
2555 err = tdb_error(wcache->tdb);
2557 if (err == TDB_ERR_NOEXIST) {
2558 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2559 return False;
2560 } else {
2561 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2562 global_winbindd_offline_state = True;
2563 return True;
2567 void set_global_winbindd_state_online(void)
2569 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2571 if (!lp_winbind_offline_logon()) {
2572 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2573 return;
2576 if (!global_winbindd_offline_state) {
2577 /* Already online. */
2578 return;
2580 global_winbindd_offline_state = False;
2582 if (!wcache->tdb) {
2583 return;
2586 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2587 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2590 BOOL get_global_winbindd_state_offline(void)
2592 return global_winbindd_offline_state;
2595 /* the cache backend methods are exposed via this structure */
2596 struct winbindd_methods cache_methods = {
2597 True,
2598 query_user_list,
2599 enum_dom_groups,
2600 enum_local_groups,
2601 name_to_sid,
2602 sid_to_name,
2603 rids_to_names,
2604 query_user,
2605 lookup_usergroups,
2606 lookup_useraliases,
2607 lookup_groupmem,
2608 sequence_number,
2609 lockout_policy,
2610 password_policy,
2611 trusted_domains