s3-libnet-samsync: call init and close ops function where appropriate.
[Samba.git] / source / winbindd / winbindd_cache.c
blob360e915bc47542e9ac52e3df6618ef3e0de1297f
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "winbindd.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods;
36 extern bool opt_nocache;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40 extern struct winbindd_methods builtin_passdb_methods;
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys[] = {
49 "SEQNUM/",
50 "DR/",
51 "DE/",
52 "WINBINDD_OFFLINE",
53 WINBINDD_CACHE_VERSION_KEYSTR,
54 NULL
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static bool is_non_centry_key(TDB_DATA kbuf)
63 int i;
65 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66 return false;
68 for (i = 0; non_centry_keys[i] != NULL; i++) {
69 size_t namelen = strlen(non_centry_keys[i]);
70 if (kbuf.dsize < namelen) {
71 continue;
73 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74 return true;
77 return false;
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static bool global_winbindd_offline_state;
86 struct winbind_cache {
87 TDB_CONTEXT *tdb;
90 struct cache_entry {
91 NTSTATUS status;
92 uint32 sequence_number;
93 uint8 *data;
94 uint32 len, ofs;
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache *wcache;
103 void winbindd_check_cache_size(time_t t)
105 static time_t last_check_time;
106 struct stat st;
108 if (last_check_time == (time_t)0)
109 last_check_time = t;
111 if (t - last_check_time < 60 && t - last_check_time > 0)
112 return;
114 if (wcache == NULL || wcache->tdb == NULL) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116 return;
119 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121 return;
124 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st.st_size,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
135 struct winbind_cache *ret = wcache;
137 /* We have to know what type of domain we are dealing with first. */
139 if (domain->internal) {
140 domain->backend = &builtin_passdb_methods;
141 domain->initialized = True;
143 if ( !domain->initialized ) {
144 init_dc_connection( domain );
148 OK. listen up becasue I'm only going to say this once.
149 We have the following scenarios to consider
150 (a) trusted AD domains on a Samba DC,
151 (b) trusted AD domains and we are joined to a non-kerberos domain
152 (c) trusted AD domains and we are joined to a kerberos (AD) domain
154 For (a) we can always contact the trusted domain using krb5
155 since we have the domain trust account password
157 For (b) we can only use RPC since we have no way of
158 getting a krb5 ticket in our own domain
160 For (c) we can always use krb5 since we have a kerberos trust
162 --jerry
165 if (!domain->backend) {
166 #ifdef HAVE_ADS
167 struct winbindd_domain *our_domain = domain;
169 /* find our domain first so we can figure out if we
170 are joined to a kerberized domain */
172 if ( !domain->primary )
173 our_domain = find_our_domain();
175 if ((our_domain->active_directory || IS_DC)
176 && domain->active_directory
177 && !lp_winbind_rpc_only()) {
178 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
179 domain->backend = &ads_methods;
180 } else {
181 #endif /* HAVE_ADS */
182 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
183 domain->backend = &reconnect_methods;
184 #ifdef HAVE_ADS
186 #endif /* HAVE_ADS */
189 if (ret)
190 return ret;
192 ret = SMB_XMALLOC_P(struct winbind_cache);
193 ZERO_STRUCTP(ret);
195 wcache = ret;
196 wcache_flush_cache();
198 return ret;
202 free a centry structure
204 static void centry_free(struct cache_entry *centry)
206 if (!centry)
207 return;
208 SAFE_FREE(centry->data);
209 free(centry);
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
214 if (centry->len - centry->ofs < nbytes) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes,
217 centry->len - centry->ofs));
218 return false;
220 return true;
224 pull a uint32 from a cache entry
226 static uint32 centry_uint32(struct cache_entry *centry)
228 uint32 ret;
230 if (!centry_check_bytes(centry, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret = IVAL(centry->data, centry->ofs);
234 centry->ofs += 4;
235 return ret;
239 pull a uint16 from a cache entry
241 static uint16 centry_uint16(struct cache_entry *centry)
243 uint16 ret;
244 if (!centry_check_bytes(centry, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 2;
249 return ret;
253 pull a uint8 from a cache entry
255 static uint8 centry_uint8(struct cache_entry *centry)
257 uint8 ret;
258 if (!centry_check_bytes(centry, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret = CVAL(centry->data, centry->ofs);
262 centry->ofs += 1;
263 return ret;
267 pull a NTTIME from a cache entry
269 static NTTIME centry_nttime(struct cache_entry *centry)
271 NTTIME ret;
272 if (!centry_check_bytes(centry, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret = IVAL(centry->data, centry->ofs);
276 centry->ofs += 4;
277 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278 centry->ofs += 4;
279 return ret;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry *centry)
287 return (time_t)centry_nttime(centry);
290 /* pull a string from a cache entry, using the supplied
291 talloc context
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 uint32 len;
296 char *ret;
298 len = centry_uint8(centry);
300 if (len == 0xFF) {
301 /* a deliberate NULL string */
302 return NULL;
305 if (!centry_check_bytes(centry, (size_t)len)) {
306 smb_panic_fn("centry_string");
309 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310 if (!ret) {
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (!centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret,centry->data + centry->ofs, 16);
344 centry->ofs += 16;
345 return ret;
348 /* pull a sid from a cache entry, using the supplied
349 talloc context
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
353 char *sid_string;
354 sid_string = centry_string(centry, mem_ctx);
355 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356 return false;
358 return true;
363 pull a NTSTATUS from a cache entry
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
367 NTSTATUS status;
369 status = NT_STATUS(centry_uint32(centry));
370 return status;
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain *domain)
377 bool ret;
379 if (!wcache->tdb)
380 return false;
382 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
384 if (ret)
385 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
386 domain->name ));
387 return ret;
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
392 TDB_DATA data;
393 fstring key;
394 uint32 time_diff;
396 if (!wcache->tdb) {
397 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398 return NT_STATUS_UNSUCCESSFUL;
401 fstr_sprintf( key, "SEQNUM/%s", domain->name );
403 data = tdb_fetch_bystring( wcache->tdb, key );
404 if ( !data.dptr || data.dsize!=8 ) {
405 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
406 return NT_STATUS_UNSUCCESSFUL;
409 domain->sequence_number = IVAL(data.dptr, 0);
410 domain->last_seq_check = IVAL(data.dptr, 4);
412 SAFE_FREE(data.dptr);
414 /* have we expired? */
416 time_diff = now - domain->last_seq_check;
417 if ( time_diff > lp_winbind_cache_time() ) {
418 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419 domain->name, domain->sequence_number,
420 (uint32)domain->last_seq_check));
421 return NT_STATUS_UNSUCCESSFUL;
424 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
425 domain->name, domain->sequence_number,
426 (uint32)domain->last_seq_check));
428 return NT_STATUS_OK;
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
433 TDB_DATA data;
434 fstring key_str;
435 uint8 buf[8];
437 if (!wcache->tdb) {
438 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439 return NT_STATUS_UNSUCCESSFUL;
442 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
444 SIVAL(buf, 0, domain->sequence_number);
445 SIVAL(buf, 4, domain->last_seq_check);
446 data.dptr = buf;
447 data.dsize = 8;
449 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
450 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
458 return NT_STATUS_OK;
462 refresh the domain sequence number. If force is true
463 then always refresh it, no matter how recently we fetched it
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
468 NTSTATUS status;
469 unsigned time_diff;
470 time_t t = time(NULL);
471 unsigned cache_time = lp_winbind_cache_time();
473 if ( IS_DOMAIN_OFFLINE(domain) ) {
474 return;
477 get_cache( domain );
479 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
480 /* trying to reconnect is expensive, don't do it too often */
481 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
482 cache_time *= 8;
484 #endif
486 time_diff = t - domain->last_seq_check;
488 /* see if we have to refetch the domain sequence number */
489 if (!force && (time_diff < cache_time)) {
490 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
491 goto done;
494 /* try to get the sequence number from the tdb cache first */
495 /* this will update the timestamp as well */
497 status = fetch_cache_seqnum( domain, t );
498 if ( NT_STATUS_IS_OK(status) )
499 goto done;
501 /* important! make sure that we know if this is a native
502 mode domain or not. And that we can contact it. */
504 if ( winbindd_can_contact_domain( domain ) ) {
505 status = domain->backend->sequence_number(domain,
506 &domain->sequence_number);
507 } else {
508 /* just use the current time */
509 status = NT_STATUS_OK;
510 domain->sequence_number = time(NULL);
514 /* the above call could have set our domain->backend to NULL when
515 * coming from offline to online mode, make sure to reinitialize the
516 * backend - Guenther */
517 get_cache( domain );
519 if (!NT_STATUS_IS_OK(status)) {
520 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
521 domain->sequence_number = DOM_SEQUENCE_NONE;
524 domain->last_status = status;
525 domain->last_seq_check = time(NULL);
527 /* save the new sequence number in the cache */
528 store_cache_seqnum( domain );
530 done:
531 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
532 domain->name, domain->sequence_number));
534 return;
538 decide if a cache entry has expired
540 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
542 /* If we've been told to be offline - stay in that state... */
543 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
544 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
545 keystr, domain->name ));
546 return false;
549 /* when the domain is offline return the cached entry.
550 * This deals with transient offline states... */
552 if (!domain->online) {
553 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
554 keystr, domain->name ));
555 return false;
558 /* if the server is OK and our cache entry came from when it was down then
559 the entry is invalid */
560 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
561 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
562 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
563 keystr, domain->name ));
564 return true;
567 /* if the server is down or the cache entry is not older than the
568 current sequence number then it is OK */
569 if (wcache_server_down(domain) ||
570 centry->sequence_number == domain->sequence_number) {
571 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
572 keystr, domain->name ));
573 return false;
576 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
577 keystr, domain->name ));
579 /* it's expired */
580 return true;
583 static struct cache_entry *wcache_fetch_raw(char *kstr)
585 TDB_DATA data;
586 struct cache_entry *centry;
587 TDB_DATA key;
589 key = string_tdb_data(kstr);
590 data = tdb_fetch(wcache->tdb, key);
591 if (!data.dptr) {
592 /* a cache miss */
593 return NULL;
596 centry = SMB_XMALLOC_P(struct cache_entry);
597 centry->data = (unsigned char *)data.dptr;
598 centry->len = data.dsize;
599 centry->ofs = 0;
601 if (centry->len < 8) {
602 /* huh? corrupt cache? */
603 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
604 centry_free(centry);
605 return NULL;
608 centry->status = centry_ntstatus(centry);
609 centry->sequence_number = centry_uint32(centry);
611 return centry;
615 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
616 number and return status
618 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
619 struct winbindd_domain *domain,
620 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
621 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
622 struct winbindd_domain *domain,
623 const char *format, ...)
625 va_list ap;
626 char *kstr;
627 struct cache_entry *centry;
629 if (opt_nocache) {
630 return NULL;
633 refresh_sequence_number(domain, false);
635 va_start(ap, format);
636 smb_xvasprintf(&kstr, format, ap);
637 va_end(ap);
639 centry = wcache_fetch_raw(kstr);
640 if (centry == NULL) {
641 free(kstr);
642 return NULL;
645 if (centry_expired(domain, kstr, centry)) {
647 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
648 kstr, domain->name ));
650 centry_free(centry);
651 free(kstr);
652 return NULL;
655 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
656 kstr, domain->name ));
658 free(kstr);
659 return centry;
662 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
663 static void wcache_delete(const char *format, ...)
665 va_list ap;
666 char *kstr;
667 TDB_DATA key;
669 va_start(ap, format);
670 smb_xvasprintf(&kstr, format, ap);
671 va_end(ap);
673 key = string_tdb_data(kstr);
675 tdb_delete(wcache->tdb, key);
676 free(kstr);
680 make sure we have at least len bytes available in a centry
682 static void centry_expand(struct cache_entry *centry, uint32 len)
684 if (centry->len - centry->ofs >= len)
685 return;
686 centry->len *= 2;
687 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
688 centry->len);
689 if (!centry->data) {
690 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
691 smb_panic_fn("out of memory in centry_expand");
696 push a uint32 into a centry
698 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
700 centry_expand(centry, 4);
701 SIVAL(centry->data, centry->ofs, v);
702 centry->ofs += 4;
706 push a uint16 into a centry
708 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
710 centry_expand(centry, 2);
711 SIVAL(centry->data, centry->ofs, v);
712 centry->ofs += 2;
716 push a uint8 into a centry
718 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
720 centry_expand(centry, 1);
721 SCVAL(centry->data, centry->ofs, v);
722 centry->ofs += 1;
726 push a string into a centry
728 static void centry_put_string(struct cache_entry *centry, const char *s)
730 int len;
732 if (!s) {
733 /* null strings are marked as len 0xFFFF */
734 centry_put_uint8(centry, 0xFF);
735 return;
738 len = strlen(s);
739 /* can't handle more than 254 char strings. Truncating is probably best */
740 if (len > 254) {
741 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
742 len = 254;
744 centry_put_uint8(centry, len);
745 centry_expand(centry, len);
746 memcpy(centry->data + centry->ofs, s, len);
747 centry->ofs += len;
751 push a 16 byte hash into a centry - treat as 16 byte string.
753 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
755 centry_put_uint8(centry, 16);
756 centry_expand(centry, 16);
757 memcpy(centry->data + centry->ofs, val, 16);
758 centry->ofs += 16;
761 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
763 fstring sid_string;
764 centry_put_string(centry, sid_to_fstring(sid_string, sid));
769 put NTSTATUS into a centry
771 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
773 uint32 status_value = NT_STATUS_V(status);
774 centry_put_uint32(centry, status_value);
779 push a NTTIME into a centry
781 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
783 centry_expand(centry, 8);
784 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
785 centry->ofs += 4;
786 SIVAL(centry->data, centry->ofs, nt >> 32);
787 centry->ofs += 4;
791 push a time_t into a centry - use a 64 bit size.
792 NTTIME here is being used as a convenient 64-bit size.
794 static void centry_put_time(struct cache_entry *centry, time_t t)
796 NTTIME nt = (NTTIME)t;
797 centry_put_nttime(centry, nt);
801 start a centry for output. When finished, call centry_end()
803 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
805 struct cache_entry *centry;
807 if (!wcache->tdb)
808 return NULL;
810 centry = SMB_XMALLOC_P(struct cache_entry);
812 centry->len = 8192; /* reasonable default */
813 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
814 centry->ofs = 0;
815 centry->sequence_number = domain->sequence_number;
816 centry_put_ntstatus(centry, status);
817 centry_put_uint32(centry, centry->sequence_number);
818 return centry;
822 finish a centry and write it to the tdb
824 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
825 static void centry_end(struct cache_entry *centry, const char *format, ...)
827 va_list ap;
828 char *kstr;
829 TDB_DATA key, data;
831 if (opt_nocache) {
832 return;
835 va_start(ap, format);
836 smb_xvasprintf(&kstr, format, ap);
837 va_end(ap);
839 key = string_tdb_data(kstr);
840 data.dptr = centry->data;
841 data.dsize = centry->ofs;
843 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
844 free(kstr);
847 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
848 NTSTATUS status, const char *domain_name,
849 const char *name, const DOM_SID *sid,
850 enum lsa_SidType type)
852 struct cache_entry *centry;
853 fstring uname;
855 centry = centry_start(domain, status);
856 if (!centry)
857 return;
858 centry_put_uint32(centry, type);
859 centry_put_sid(centry, sid);
860 fstrcpy(uname, name);
861 strupper_m(uname);
862 centry_end(centry, "NS/%s/%s", domain_name, uname);
863 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
864 uname, sid_string_dbg(sid), nt_errstr(status)));
865 centry_free(centry);
868 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
869 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
871 struct cache_entry *centry;
872 fstring sid_string;
874 centry = centry_start(domain, status);
875 if (!centry)
876 return;
878 if (NT_STATUS_IS_OK(status)) {
879 centry_put_uint32(centry, type);
880 centry_put_string(centry, domain_name);
881 centry_put_string(centry, name);
884 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
885 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
886 name, nt_errstr(status)));
887 centry_free(centry);
891 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
893 struct cache_entry *centry;
894 fstring sid_string;
896 if (is_null_sid(&info->user_sid)) {
897 return;
900 centry = centry_start(domain, status);
901 if (!centry)
902 return;
903 centry_put_string(centry, info->acct_name);
904 centry_put_string(centry, info->full_name);
905 centry_put_string(centry, info->homedir);
906 centry_put_string(centry, info->shell);
907 centry_put_uint32(centry, info->primary_gid);
908 centry_put_sid(centry, &info->user_sid);
909 centry_put_sid(centry, &info->group_sid);
910 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
911 &info->user_sid));
912 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
913 centry_free(centry);
916 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
917 NTSTATUS status,
918 struct samr_DomInfo12 *lockout_policy)
920 struct cache_entry *centry;
922 centry = centry_start(domain, status);
923 if (!centry)
924 return;
926 centry_put_nttime(centry, lockout_policy->lockout_duration);
927 centry_put_nttime(centry, lockout_policy->lockout_window);
928 centry_put_uint16(centry, lockout_policy->lockout_threshold);
930 centry_end(centry, "LOC_POL/%s", domain->name);
932 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
934 centry_free(centry);
939 static void wcache_save_password_policy(struct winbindd_domain *domain,
940 NTSTATUS status,
941 struct samr_DomInfo1 *policy)
943 struct cache_entry *centry;
945 centry = centry_start(domain, status);
946 if (!centry)
947 return;
949 centry_put_uint16(centry, policy->min_password_length);
950 centry_put_uint16(centry, policy->password_history_length);
951 centry_put_uint32(centry, policy->password_properties);
952 centry_put_nttime(centry, policy->max_password_age);
953 centry_put_nttime(centry, policy->min_password_age);
955 centry_end(centry, "PWD_POL/%s", domain->name);
957 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
959 centry_free(centry);
962 /***************************************************************************
963 ***************************************************************************/
965 static void wcache_save_username_alias(struct winbindd_domain *domain,
966 NTSTATUS status,
967 const char *name, const char *alias)
969 struct cache_entry *centry;
970 fstring uname;
972 if ( (centry = centry_start(domain, status)) == NULL )
973 return;
975 centry_put_string( centry, alias );
977 fstrcpy(uname, name);
978 strupper_m(uname);
979 centry_end(centry, "NSS/NA/%s", uname);
981 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
983 centry_free(centry);
986 static void wcache_save_alias_username(struct winbindd_domain *domain,
987 NTSTATUS status,
988 const char *alias, const char *name)
990 struct cache_entry *centry;
991 fstring uname;
993 if ( (centry = centry_start(domain, status)) == NULL )
994 return;
996 centry_put_string( centry, name );
998 fstrcpy(uname, alias);
999 strupper_m(uname);
1000 centry_end(centry, "NSS/AN/%s", uname);
1002 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1004 centry_free(centry);
1007 /***************************************************************************
1008 ***************************************************************************/
1010 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1011 struct winbindd_domain *domain,
1012 const char *name, char **alias )
1014 struct winbind_cache *cache = get_cache(domain);
1015 struct cache_entry *centry = NULL;
1016 NTSTATUS status;
1017 char *upper_name;
1019 if ( domain->internal )
1020 return NT_STATUS_NOT_SUPPORTED;
1022 if (!cache->tdb)
1023 goto do_query;
1025 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1026 return NT_STATUS_NO_MEMORY;
1027 strupper_m(upper_name);
1029 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1031 SAFE_FREE( upper_name );
1033 if (!centry)
1034 goto do_query;
1036 status = centry->status;
1038 if (!NT_STATUS_IS_OK(status)) {
1039 centry_free(centry);
1040 return status;
1043 *alias = centry_string( centry, mem_ctx );
1045 centry_free(centry);
1047 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1048 name, *alias ? *alias : "(none)"));
1050 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1052 do_query:
1054 /* If its not in cache and we are offline, then fail */
1056 if ( get_global_winbindd_state_offline() || !domain->online ) {
1057 DEBUG(8,("resolve_username_to_alias: rejecting query "
1058 "in offline mode\n"));
1059 return NT_STATUS_NOT_FOUND;
1062 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1064 if ( NT_STATUS_IS_OK( status ) ) {
1065 wcache_save_username_alias(domain, status, name, *alias);
1068 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1069 wcache_save_username_alias(domain, status, name, "(NULL)");
1072 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1073 nt_errstr(status)));
1075 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1076 set_domain_offline( domain );
1079 return status;
1082 /***************************************************************************
1083 ***************************************************************************/
1085 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1086 struct winbindd_domain *domain,
1087 const char *alias, char **name )
1089 struct winbind_cache *cache = get_cache(domain);
1090 struct cache_entry *centry = NULL;
1091 NTSTATUS status;
1092 char *upper_name;
1094 if ( domain->internal )
1095 return NT_STATUS_NOT_SUPPORTED;
1097 if (!cache->tdb)
1098 goto do_query;
1100 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1101 return NT_STATUS_NO_MEMORY;
1102 strupper_m(upper_name);
1104 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1106 SAFE_FREE( upper_name );
1108 if (!centry)
1109 goto do_query;
1111 status = centry->status;
1113 if (!NT_STATUS_IS_OK(status)) {
1114 centry_free(centry);
1115 return status;
1118 *name = centry_string( centry, mem_ctx );
1120 centry_free(centry);
1122 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1123 alias, *name ? *name : "(none)"));
1125 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1127 do_query:
1129 /* If its not in cache and we are offline, then fail */
1131 if ( get_global_winbindd_state_offline() || !domain->online ) {
1132 DEBUG(8,("resolve_alias_to_username: rejecting query "
1133 "in offline mode\n"));
1134 return NT_STATUS_NOT_FOUND;
1137 /* an alias cannot contain a domain prefix or '@' */
1139 if (strchr(alias, '\\') || strchr(alias, '@')) {
1140 DEBUG(10,("resolve_alias_to_username: skipping fully "
1141 "qualified name %s\n", alias));
1142 return NT_STATUS_OBJECT_NAME_INVALID;
1145 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1147 if ( NT_STATUS_IS_OK( status ) ) {
1148 wcache_save_alias_username( domain, status, alias, *name );
1151 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1152 wcache_save_alias_username(domain, status, alias, "(NULL)");
1155 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1156 nt_errstr(status)));
1158 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1159 set_domain_offline( domain );
1162 return status;
1165 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1167 struct winbind_cache *cache = get_cache(domain);
1168 TDB_DATA data;
1169 fstring key_str, tmp;
1170 uint32 rid;
1172 if (!cache->tdb) {
1173 return NT_STATUS_INTERNAL_DB_ERROR;
1176 if (is_null_sid(sid)) {
1177 return NT_STATUS_INVALID_SID;
1180 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1181 return NT_STATUS_INVALID_SID;
1184 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1186 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1187 if (!data.dptr) {
1188 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1191 SAFE_FREE(data.dptr);
1192 return NT_STATUS_OK;
1195 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1196 as new salted ones. */
1198 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1199 TALLOC_CTX *mem_ctx,
1200 const DOM_SID *sid,
1201 const uint8 **cached_nt_pass,
1202 const uint8 **cached_salt)
1204 struct winbind_cache *cache = get_cache(domain);
1205 struct cache_entry *centry = NULL;
1206 NTSTATUS status;
1207 time_t t;
1208 uint32 rid;
1209 fstring tmp;
1211 if (!cache->tdb) {
1212 return NT_STATUS_INTERNAL_DB_ERROR;
1215 if (is_null_sid(sid)) {
1216 return NT_STATUS_INVALID_SID;
1219 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1220 return NT_STATUS_INVALID_SID;
1223 /* Try and get a salted cred first. If we can't
1224 fall back to an unsalted cred. */
1226 centry = wcache_fetch(cache, domain, "CRED/%s",
1227 sid_to_fstring(tmp, sid));
1228 if (!centry) {
1229 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1230 sid_string_dbg(sid)));
1231 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1234 t = centry_time(centry);
1236 /* In the salted case this isn't actually the nt_hash itself,
1237 but the MD5 of the salt + nt_hash. Let the caller
1238 sort this out. It can tell as we only return the cached_salt
1239 if we are returning a salted cred. */
1241 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1242 if (*cached_nt_pass == NULL) {
1243 fstring sidstr;
1245 sid_to_fstring(sidstr, sid);
1247 /* Bad (old) cred cache. Delete and pretend we
1248 don't have it. */
1249 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1250 sidstr));
1251 wcache_delete("CRED/%s", sidstr);
1252 centry_free(centry);
1253 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1256 /* We only have 17 bytes more data in the salted cred case. */
1257 if (centry->len - centry->ofs == 17) {
1258 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1259 } else {
1260 *cached_salt = NULL;
1263 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1264 if (*cached_salt) {
1265 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1268 status = centry->status;
1270 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1271 sid_string_dbg(sid), nt_errstr(status) ));
1273 centry_free(centry);
1274 return status;
1277 /* Store creds for a SID - only writes out new salted ones. */
1279 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1280 TALLOC_CTX *mem_ctx,
1281 const DOM_SID *sid,
1282 const uint8 nt_pass[NT_HASH_LEN])
1284 struct cache_entry *centry;
1285 fstring sid_string;
1286 uint32 rid;
1287 uint8 cred_salt[NT_HASH_LEN];
1288 uint8 salted_hash[NT_HASH_LEN];
1290 if (is_null_sid(sid)) {
1291 return NT_STATUS_INVALID_SID;
1294 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1295 return NT_STATUS_INVALID_SID;
1298 centry = centry_start(domain, NT_STATUS_OK);
1299 if (!centry) {
1300 return NT_STATUS_INTERNAL_DB_ERROR;
1303 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1305 centry_put_time(centry, time(NULL));
1307 /* Create a salt and then salt the hash. */
1308 generate_random_buffer(cred_salt, NT_HASH_LEN);
1309 E_md5hash(cred_salt, nt_pass, salted_hash);
1311 centry_put_hash16(centry, salted_hash);
1312 centry_put_hash16(centry, cred_salt);
1313 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1315 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1317 centry_free(centry);
1319 return NT_STATUS_OK;
1323 /* Query display info. This is the basic user list fn */
1324 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1325 TALLOC_CTX *mem_ctx,
1326 uint32 *num_entries,
1327 WINBIND_USERINFO **info)
1329 struct winbind_cache *cache = get_cache(domain);
1330 struct cache_entry *centry = NULL;
1331 NTSTATUS status;
1332 unsigned int i, retry;
1334 if (!cache->tdb)
1335 goto do_query;
1337 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1338 if (!centry)
1339 goto do_query;
1341 *num_entries = centry_uint32(centry);
1343 if (*num_entries == 0)
1344 goto do_cached;
1346 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1347 if (! (*info)) {
1348 smb_panic_fn("query_user_list out of memory");
1350 for (i=0; i<(*num_entries); i++) {
1351 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1352 (*info)[i].full_name = centry_string(centry, mem_ctx);
1353 (*info)[i].homedir = centry_string(centry, mem_ctx);
1354 (*info)[i].shell = centry_string(centry, mem_ctx);
1355 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1356 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1359 do_cached:
1360 status = centry->status;
1362 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1363 domain->name, nt_errstr(status) ));
1365 centry_free(centry);
1366 return status;
1368 do_query:
1369 *num_entries = 0;
1370 *info = NULL;
1372 /* Return status value returned by seq number check */
1374 if (!NT_STATUS_IS_OK(domain->last_status))
1375 return domain->last_status;
1377 /* Put the query_user_list() in a retry loop. There appears to be
1378 * some bug either with Windows 2000 or Samba's handling of large
1379 * rpc replies. This manifests itself as sudden disconnection
1380 * at a random point in the enumeration of a large (60k) user list.
1381 * The retry loop simply tries the operation again. )-: It's not
1382 * pretty but an acceptable workaround until we work out what the
1383 * real problem is. */
1385 retry = 0;
1386 do {
1388 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1389 domain->name ));
1391 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1392 if (!NT_STATUS_IS_OK(status)) {
1393 DEBUG(3, ("query_user_list: returned 0x%08x, "
1394 "retrying\n", NT_STATUS_V(status)));
1396 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1397 DEBUG(3, ("query_user_list: flushing "
1398 "connection cache\n"));
1399 invalidate_cm_connection(&domain->conn);
1402 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1403 (retry++ < 5));
1405 /* and save it */
1406 refresh_sequence_number(domain, false);
1407 centry = centry_start(domain, status);
1408 if (!centry)
1409 goto skip_save;
1410 centry_put_uint32(centry, *num_entries);
1411 for (i=0; i<(*num_entries); i++) {
1412 centry_put_string(centry, (*info)[i].acct_name);
1413 centry_put_string(centry, (*info)[i].full_name);
1414 centry_put_string(centry, (*info)[i].homedir);
1415 centry_put_string(centry, (*info)[i].shell);
1416 centry_put_sid(centry, &(*info)[i].user_sid);
1417 centry_put_sid(centry, &(*info)[i].group_sid);
1418 if (domain->backend && domain->backend->consistent) {
1419 /* when the backend is consistent we can pre-prime some mappings */
1420 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1421 domain->name,
1422 (*info)[i].acct_name,
1423 &(*info)[i].user_sid,
1424 SID_NAME_USER);
1425 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1426 &(*info)[i].user_sid,
1427 domain->name,
1428 (*info)[i].acct_name,
1429 SID_NAME_USER);
1430 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1433 centry_end(centry, "UL/%s", domain->name);
1434 centry_free(centry);
1436 skip_save:
1437 return status;
1440 /* list all domain groups */
1441 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1442 TALLOC_CTX *mem_ctx,
1443 uint32 *num_entries,
1444 struct acct_info **info)
1446 struct winbind_cache *cache = get_cache(domain);
1447 struct cache_entry *centry = NULL;
1448 NTSTATUS status;
1449 unsigned int i;
1451 if (!cache->tdb)
1452 goto do_query;
1454 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1455 if (!centry)
1456 goto do_query;
1458 *num_entries = centry_uint32(centry);
1460 if (*num_entries == 0)
1461 goto do_cached;
1463 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1464 if (! (*info)) {
1465 smb_panic_fn("enum_dom_groups out of memory");
1467 for (i=0; i<(*num_entries); i++) {
1468 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1469 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1470 (*info)[i].rid = centry_uint32(centry);
1473 do_cached:
1474 status = centry->status;
1476 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1477 domain->name, nt_errstr(status) ));
1479 centry_free(centry);
1480 return status;
1482 do_query:
1483 *num_entries = 0;
1484 *info = NULL;
1486 /* Return status value returned by seq number check */
1488 if (!NT_STATUS_IS_OK(domain->last_status))
1489 return domain->last_status;
1491 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1492 domain->name ));
1494 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1496 /* and save it */
1497 refresh_sequence_number(domain, false);
1498 centry = centry_start(domain, status);
1499 if (!centry)
1500 goto skip_save;
1501 centry_put_uint32(centry, *num_entries);
1502 for (i=0; i<(*num_entries); i++) {
1503 centry_put_string(centry, (*info)[i].acct_name);
1504 centry_put_string(centry, (*info)[i].acct_desc);
1505 centry_put_uint32(centry, (*info)[i].rid);
1507 centry_end(centry, "GL/%s/domain", domain->name);
1508 centry_free(centry);
1510 skip_save:
1511 return status;
1514 /* list all domain groups */
1515 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1516 TALLOC_CTX *mem_ctx,
1517 uint32 *num_entries,
1518 struct acct_info **info)
1520 struct winbind_cache *cache = get_cache(domain);
1521 struct cache_entry *centry = NULL;
1522 NTSTATUS status;
1523 unsigned int i;
1525 if (!cache->tdb)
1526 goto do_query;
1528 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1529 if (!centry)
1530 goto do_query;
1532 *num_entries = centry_uint32(centry);
1534 if (*num_entries == 0)
1535 goto do_cached;
1537 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1538 if (! (*info)) {
1539 smb_panic_fn("enum_dom_groups out of memory");
1541 for (i=0; i<(*num_entries); i++) {
1542 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1543 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1544 (*info)[i].rid = centry_uint32(centry);
1547 do_cached:
1549 /* If we are returning cached data and the domain controller
1550 is down then we don't know whether the data is up to date
1551 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1552 indicate this. */
1554 if (wcache_server_down(domain)) {
1555 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1556 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1557 } else
1558 status = centry->status;
1560 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1561 domain->name, nt_errstr(status) ));
1563 centry_free(centry);
1564 return status;
1566 do_query:
1567 *num_entries = 0;
1568 *info = NULL;
1570 /* Return status value returned by seq number check */
1572 if (!NT_STATUS_IS_OK(domain->last_status))
1573 return domain->last_status;
1575 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1576 domain->name ));
1578 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1580 /* and save it */
1581 refresh_sequence_number(domain, false);
1582 centry = centry_start(domain, status);
1583 if (!centry)
1584 goto skip_save;
1585 centry_put_uint32(centry, *num_entries);
1586 for (i=0; i<(*num_entries); i++) {
1587 centry_put_string(centry, (*info)[i].acct_name);
1588 centry_put_string(centry, (*info)[i].acct_desc);
1589 centry_put_uint32(centry, (*info)[i].rid);
1591 centry_end(centry, "GL/%s/local", domain->name);
1592 centry_free(centry);
1594 skip_save:
1595 return status;
1598 /* convert a single name to a sid in a domain */
1599 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1600 TALLOC_CTX *mem_ctx,
1601 enum winbindd_cmd orig_cmd,
1602 const char *domain_name,
1603 const char *name,
1604 DOM_SID *sid,
1605 enum lsa_SidType *type)
1607 struct winbind_cache *cache = get_cache(domain);
1608 struct cache_entry *centry = NULL;
1609 NTSTATUS status;
1610 fstring uname;
1612 if (!cache->tdb)
1613 goto do_query;
1615 fstrcpy(uname, name);
1616 strupper_m(uname);
1617 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1618 if (!centry)
1619 goto do_query;
1621 status = centry->status;
1622 if (NT_STATUS_IS_OK(status)) {
1623 *type = (enum lsa_SidType)centry_uint32(centry);
1624 centry_sid(centry, mem_ctx, sid);
1627 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1628 domain->name, nt_errstr(status) ));
1630 centry_free(centry);
1631 return status;
1633 do_query:
1634 ZERO_STRUCTP(sid);
1636 /* If the seq number check indicated that there is a problem
1637 * with this DC, then return that status... except for
1638 * access_denied. This is special because the dc may be in
1639 * "restrict anonymous = 1" mode, in which case it will deny
1640 * most unauthenticated operations, but *will* allow the LSA
1641 * name-to-sid that we try as a fallback. */
1643 if (!(NT_STATUS_IS_OK(domain->last_status)
1644 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1645 return domain->last_status;
1647 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1648 domain->name ));
1650 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1651 domain_name, name, sid, type);
1653 /* and save it */
1654 refresh_sequence_number(domain, false);
1656 if (domain->online &&
1657 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1658 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1660 /* Only save the reverse mapping if this was not a UPN */
1661 if (!strchr(name, '@')) {
1662 strupper_m(CONST_DISCARD(char *,domain_name));
1663 strlower_m(CONST_DISCARD(char *,name));
1664 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1668 return status;
1671 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1672 given */
1673 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1674 TALLOC_CTX *mem_ctx,
1675 const DOM_SID *sid,
1676 char **domain_name,
1677 char **name,
1678 enum lsa_SidType *type)
1680 struct winbind_cache *cache = get_cache(domain);
1681 struct cache_entry *centry = NULL;
1682 NTSTATUS status;
1683 fstring sid_string;
1685 if (!cache->tdb)
1686 goto do_query;
1688 centry = wcache_fetch(cache, domain, "SN/%s",
1689 sid_to_fstring(sid_string, sid));
1690 if (!centry)
1691 goto do_query;
1693 status = centry->status;
1694 if (NT_STATUS_IS_OK(status)) {
1695 *type = (enum lsa_SidType)centry_uint32(centry);
1696 *domain_name = centry_string(centry, mem_ctx);
1697 *name = centry_string(centry, mem_ctx);
1700 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1701 domain->name, nt_errstr(status) ));
1703 centry_free(centry);
1704 return status;
1706 do_query:
1707 *name = NULL;
1708 *domain_name = NULL;
1710 /* If the seq number check indicated that there is a problem
1711 * with this DC, then return that status... except for
1712 * access_denied. This is special because the dc may be in
1713 * "restrict anonymous = 1" mode, in which case it will deny
1714 * most unauthenticated operations, but *will* allow the LSA
1715 * sid-to-name that we try as a fallback. */
1717 if (!(NT_STATUS_IS_OK(domain->last_status)
1718 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1719 return domain->last_status;
1721 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1722 domain->name ));
1724 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1726 /* and save it */
1727 refresh_sequence_number(domain, false);
1728 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1730 /* We can't save the name to sid mapping here, as with sid history a
1731 * later name2sid would give the wrong sid. */
1733 return status;
1736 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1737 TALLOC_CTX *mem_ctx,
1738 const DOM_SID *domain_sid,
1739 uint32 *rids,
1740 size_t num_rids,
1741 char **domain_name,
1742 char ***names,
1743 enum lsa_SidType **types)
1745 struct winbind_cache *cache = get_cache(domain);
1746 size_t i;
1747 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1748 bool have_mapped;
1749 bool have_unmapped;
1751 *domain_name = NULL;
1752 *names = NULL;
1753 *types = NULL;
1755 if (!cache->tdb) {
1756 goto do_query;
1759 if (num_rids == 0) {
1760 return NT_STATUS_OK;
1763 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1764 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1766 if ((*names == NULL) || (*types == NULL)) {
1767 result = NT_STATUS_NO_MEMORY;
1768 goto error;
1771 have_mapped = have_unmapped = false;
1773 for (i=0; i<num_rids; i++) {
1774 DOM_SID sid;
1775 struct cache_entry *centry;
1776 fstring tmp;
1778 if (!sid_compose(&sid, domain_sid, rids[i])) {
1779 result = NT_STATUS_INTERNAL_ERROR;
1780 goto error;
1783 centry = wcache_fetch(cache, domain, "SN/%s",
1784 sid_to_fstring(tmp, &sid));
1785 if (!centry) {
1786 goto do_query;
1789 (*types)[i] = SID_NAME_UNKNOWN;
1790 (*names)[i] = talloc_strdup(*names, "");
1792 if (NT_STATUS_IS_OK(centry->status)) {
1793 char *dom;
1794 have_mapped = true;
1795 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1797 dom = centry_string(centry, mem_ctx);
1798 if (*domain_name == NULL) {
1799 *domain_name = dom;
1800 } else {
1801 talloc_free(dom);
1804 (*names)[i] = centry_string(centry, *names);
1806 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1807 have_unmapped = true;
1809 } else {
1810 /* something's definitely wrong */
1811 result = centry->status;
1812 goto error;
1815 centry_free(centry);
1818 if (!have_mapped) {
1819 return NT_STATUS_NONE_MAPPED;
1821 if (!have_unmapped) {
1822 return NT_STATUS_OK;
1824 return STATUS_SOME_UNMAPPED;
1826 do_query:
1828 TALLOC_FREE(*names);
1829 TALLOC_FREE(*types);
1831 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1832 rids, num_rids, domain_name,
1833 names, types);
1836 None of the queried rids has been found so save all negative entries
1838 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1839 for (i = 0; i < num_rids; i++) {
1840 DOM_SID sid;
1841 const char *name = "";
1842 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1843 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1845 if (!sid_compose(&sid, domain_sid, rids[i])) {
1846 return NT_STATUS_INTERNAL_ERROR;
1849 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1850 name, type);
1853 return result;
1857 Some or all of the queried rids have been found.
1859 if (!NT_STATUS_IS_OK(result) &&
1860 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1861 return result;
1864 refresh_sequence_number(domain, false);
1866 for (i=0; i<num_rids; i++) {
1867 DOM_SID sid;
1868 NTSTATUS status;
1870 if (!sid_compose(&sid, domain_sid, rids[i])) {
1871 result = NT_STATUS_INTERNAL_ERROR;
1872 goto error;
1875 status = (*types)[i] == SID_NAME_UNKNOWN ?
1876 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1878 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1879 (*names)[i], (*types)[i]);
1882 return result;
1884 error:
1886 TALLOC_FREE(*names);
1887 TALLOC_FREE(*types);
1888 return result;
1891 /* Lookup user information from a rid */
1892 static NTSTATUS query_user(struct winbindd_domain *domain,
1893 TALLOC_CTX *mem_ctx,
1894 const DOM_SID *user_sid,
1895 WINBIND_USERINFO *info)
1897 struct winbind_cache *cache = get_cache(domain);
1898 struct cache_entry *centry = NULL;
1899 NTSTATUS status;
1900 fstring tmp;
1902 if (!cache->tdb)
1903 goto do_query;
1905 centry = wcache_fetch(cache, domain, "U/%s",
1906 sid_to_fstring(tmp, user_sid));
1908 /* If we have an access denied cache entry and a cached info3 in the
1909 samlogon cache then do a query. This will force the rpc back end
1910 to return the info3 data. */
1912 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1913 netsamlogon_cache_have(user_sid)) {
1914 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1915 domain->last_status = NT_STATUS_OK;
1916 centry_free(centry);
1917 goto do_query;
1920 if (!centry)
1921 goto do_query;
1923 /* if status is not ok then this is a negative hit
1924 and the rest of the data doesn't matter */
1925 status = centry->status;
1926 if (NT_STATUS_IS_OK(status)) {
1927 info->acct_name = centry_string(centry, mem_ctx);
1928 info->full_name = centry_string(centry, mem_ctx);
1929 info->homedir = centry_string(centry, mem_ctx);
1930 info->shell = centry_string(centry, mem_ctx);
1931 info->primary_gid = centry_uint32(centry);
1932 centry_sid(centry, mem_ctx, &info->user_sid);
1933 centry_sid(centry, mem_ctx, &info->group_sid);
1936 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1937 domain->name, nt_errstr(status) ));
1939 centry_free(centry);
1940 return status;
1942 do_query:
1943 ZERO_STRUCTP(info);
1945 /* Return status value returned by seq number check */
1947 if (!NT_STATUS_IS_OK(domain->last_status))
1948 return domain->last_status;
1950 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1951 domain->name ));
1953 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1955 /* and save it */
1956 refresh_sequence_number(domain, false);
1957 wcache_save_user(domain, status, info);
1959 return status;
1963 /* Lookup groups a user is a member of. */
1964 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1965 TALLOC_CTX *mem_ctx,
1966 const DOM_SID *user_sid,
1967 uint32 *num_groups, DOM_SID **user_gids)
1969 struct winbind_cache *cache = get_cache(domain);
1970 struct cache_entry *centry = NULL;
1971 NTSTATUS status;
1972 unsigned int i;
1973 fstring sid_string;
1975 if (!cache->tdb)
1976 goto do_query;
1978 centry = wcache_fetch(cache, domain, "UG/%s",
1979 sid_to_fstring(sid_string, user_sid));
1981 /* If we have an access denied cache entry and a cached info3 in the
1982 samlogon cache then do a query. This will force the rpc back end
1983 to return the info3 data. */
1985 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1986 netsamlogon_cache_have(user_sid)) {
1987 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1988 domain->last_status = NT_STATUS_OK;
1989 centry_free(centry);
1990 goto do_query;
1993 if (!centry)
1994 goto do_query;
1996 *num_groups = centry_uint32(centry);
1998 if (*num_groups == 0)
1999 goto do_cached;
2001 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2002 if (! (*user_gids)) {
2003 smb_panic_fn("lookup_usergroups out of memory");
2005 for (i=0; i<(*num_groups); i++) {
2006 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
2009 do_cached:
2010 status = centry->status;
2012 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2013 domain->name, nt_errstr(status) ));
2015 centry_free(centry);
2016 return status;
2018 do_query:
2019 (*num_groups) = 0;
2020 (*user_gids) = NULL;
2022 /* Return status value returned by seq number check */
2024 if (!NT_STATUS_IS_OK(domain->last_status))
2025 return domain->last_status;
2027 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2028 domain->name ));
2030 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2032 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2033 goto skip_save;
2035 /* and save it */
2036 refresh_sequence_number(domain, false);
2037 centry = centry_start(domain, status);
2038 if (!centry)
2039 goto skip_save;
2041 centry_put_uint32(centry, *num_groups);
2042 for (i=0; i<(*num_groups); i++) {
2043 centry_put_sid(centry, &(*user_gids)[i]);
2046 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2047 centry_free(centry);
2049 skip_save:
2050 return status;
2053 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2054 TALLOC_CTX *mem_ctx,
2055 uint32 num_sids, const DOM_SID *sids,
2056 uint32 *num_aliases, uint32 **alias_rids)
2058 struct winbind_cache *cache = get_cache(domain);
2059 struct cache_entry *centry = NULL;
2060 NTSTATUS status;
2061 char *sidlist = talloc_strdup(mem_ctx, "");
2062 int i;
2064 if (!cache->tdb)
2065 goto do_query;
2067 if (num_sids == 0) {
2068 *num_aliases = 0;
2069 *alias_rids = NULL;
2070 return NT_STATUS_OK;
2073 /* We need to cache indexed by the whole list of SIDs, the aliases
2074 * resulting might come from any of the SIDs. */
2076 for (i=0; i<num_sids; i++) {
2077 fstring tmp;
2078 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2079 sid_to_fstring(tmp, &sids[i]));
2080 if (sidlist == NULL)
2081 return NT_STATUS_NO_MEMORY;
2084 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2086 if (!centry)
2087 goto do_query;
2089 *num_aliases = centry_uint32(centry);
2090 *alias_rids = NULL;
2092 if (*num_aliases) {
2093 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2095 if ((*alias_rids) == NULL) {
2096 centry_free(centry);
2097 return NT_STATUS_NO_MEMORY;
2099 } else {
2100 (*alias_rids) = NULL;
2103 for (i=0; i<(*num_aliases); i++)
2104 (*alias_rids)[i] = centry_uint32(centry);
2106 status = centry->status;
2108 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2109 "status %s\n", domain->name, nt_errstr(status)));
2111 centry_free(centry);
2112 return status;
2114 do_query:
2115 (*num_aliases) = 0;
2116 (*alias_rids) = NULL;
2118 if (!NT_STATUS_IS_OK(domain->last_status))
2119 return domain->last_status;
2121 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2122 "for domain %s\n", domain->name ));
2124 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2125 num_sids, sids,
2126 num_aliases, alias_rids);
2128 /* and save it */
2129 refresh_sequence_number(domain, false);
2130 centry = centry_start(domain, status);
2131 if (!centry)
2132 goto skip_save;
2133 centry_put_uint32(centry, *num_aliases);
2134 for (i=0; i<(*num_aliases); i++)
2135 centry_put_uint32(centry, (*alias_rids)[i]);
2136 centry_end(centry, "UA%s", sidlist);
2137 centry_free(centry);
2139 skip_save:
2140 return status;
2144 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2145 TALLOC_CTX *mem_ctx,
2146 const DOM_SID *group_sid, uint32 *num_names,
2147 DOM_SID **sid_mem, char ***names,
2148 uint32 **name_types)
2150 struct winbind_cache *cache = get_cache(domain);
2151 struct cache_entry *centry = NULL;
2152 NTSTATUS status;
2153 unsigned int i;
2154 fstring sid_string;
2156 if (!cache->tdb)
2157 goto do_query;
2159 centry = wcache_fetch(cache, domain, "GM/%s",
2160 sid_to_fstring(sid_string, group_sid));
2161 if (!centry)
2162 goto do_query;
2164 *num_names = centry_uint32(centry);
2166 if (*num_names == 0)
2167 goto do_cached;
2169 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2170 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2171 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2173 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2174 smb_panic_fn("lookup_groupmem out of memory");
2177 for (i=0; i<(*num_names); i++) {
2178 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
2179 (*names)[i] = centry_string(centry, mem_ctx);
2180 (*name_types)[i] = centry_uint32(centry);
2183 do_cached:
2184 status = centry->status;
2186 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2187 domain->name, nt_errstr(status)));
2189 centry_free(centry);
2190 return status;
2192 do_query:
2193 (*num_names) = 0;
2194 (*sid_mem) = NULL;
2195 (*names) = NULL;
2196 (*name_types) = NULL;
2198 /* Return status value returned by seq number check */
2200 if (!NT_STATUS_IS_OK(domain->last_status))
2201 return domain->last_status;
2203 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2204 domain->name ));
2206 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2207 sid_mem, names, name_types);
2209 /* and save it */
2210 refresh_sequence_number(domain, false);
2211 centry = centry_start(domain, status);
2212 if (!centry)
2213 goto skip_save;
2214 centry_put_uint32(centry, *num_names);
2215 for (i=0; i<(*num_names); i++) {
2216 centry_put_sid(centry, &(*sid_mem)[i]);
2217 centry_put_string(centry, (*names)[i]);
2218 centry_put_uint32(centry, (*name_types)[i]);
2220 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2221 centry_free(centry);
2223 skip_save:
2224 return status;
2227 /* find the sequence number for a domain */
2228 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2230 refresh_sequence_number(domain, false);
2232 *seq = domain->sequence_number;
2234 return NT_STATUS_OK;
2237 /* enumerate trusted domains
2238 * (we need to have the list of trustdoms in the cache when we go offline) -
2239 * Guenther */
2240 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2241 TALLOC_CTX *mem_ctx,
2242 uint32 *num_domains,
2243 char ***names,
2244 char ***alt_names,
2245 DOM_SID **dom_sids)
2247 struct winbind_cache *cache = get_cache(domain);
2248 struct cache_entry *centry = NULL;
2249 NTSTATUS status;
2250 int i;
2252 if (!cache->tdb)
2253 goto do_query;
2255 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2257 if (!centry) {
2258 goto do_query;
2261 *num_domains = centry_uint32(centry);
2263 if (*num_domains) {
2264 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2265 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2266 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2268 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2269 smb_panic_fn("trusted_domains out of memory");
2271 } else {
2272 (*names) = NULL;
2273 (*alt_names) = NULL;
2274 (*dom_sids) = NULL;
2277 for (i=0; i<(*num_domains); i++) {
2278 (*names)[i] = centry_string(centry, mem_ctx);
2279 (*alt_names)[i] = centry_string(centry, mem_ctx);
2280 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2281 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2285 status = centry->status;
2287 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2288 domain->name, *num_domains, nt_errstr(status) ));
2290 centry_free(centry);
2291 return status;
2293 do_query:
2294 (*num_domains) = 0;
2295 (*dom_sids) = NULL;
2296 (*names) = NULL;
2297 (*alt_names) = NULL;
2299 /* Return status value returned by seq number check */
2301 if (!NT_STATUS_IS_OK(domain->last_status))
2302 return domain->last_status;
2304 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2305 domain->name ));
2307 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2308 names, alt_names, dom_sids);
2310 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2311 * so that the generic centry handling still applies correctly -
2312 * Guenther*/
2314 if (!NT_STATUS_IS_ERR(status)) {
2315 status = NT_STATUS_OK;
2319 #if 0 /* Disabled as we want the trust dom list to be managed by
2320 the main parent and always to make the query. --jerry */
2322 /* and save it */
2323 refresh_sequence_number(domain, false);
2325 centry = centry_start(domain, status);
2326 if (!centry)
2327 goto skip_save;
2329 centry_put_uint32(centry, *num_domains);
2331 for (i=0; i<(*num_domains); i++) {
2332 centry_put_string(centry, (*names)[i]);
2333 centry_put_string(centry, (*alt_names)[i]);
2334 centry_put_sid(centry, &(*dom_sids)[i]);
2337 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2339 centry_free(centry);
2341 skip_save:
2342 #endif
2344 return status;
2347 /* get lockout policy */
2348 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2349 TALLOC_CTX *mem_ctx,
2350 struct samr_DomInfo12 *policy)
2352 struct winbind_cache *cache = get_cache(domain);
2353 struct cache_entry *centry = NULL;
2354 NTSTATUS status;
2356 if (!cache->tdb)
2357 goto do_query;
2359 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2361 if (!centry)
2362 goto do_query;
2364 policy->lockout_duration = centry_nttime(centry);
2365 policy->lockout_window = centry_nttime(centry);
2366 policy->lockout_threshold = centry_uint16(centry);
2368 status = centry->status;
2370 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2371 domain->name, nt_errstr(status) ));
2373 centry_free(centry);
2374 return status;
2376 do_query:
2377 ZERO_STRUCTP(policy);
2379 /* Return status value returned by seq number check */
2381 if (!NT_STATUS_IS_OK(domain->last_status))
2382 return domain->last_status;
2384 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2385 domain->name ));
2387 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2389 /* and save it */
2390 refresh_sequence_number(domain, false);
2391 wcache_save_lockout_policy(domain, status, policy);
2393 return status;
2396 /* get password policy */
2397 static NTSTATUS password_policy(struct winbindd_domain *domain,
2398 TALLOC_CTX *mem_ctx,
2399 struct samr_DomInfo1 *policy)
2401 struct winbind_cache *cache = get_cache(domain);
2402 struct cache_entry *centry = NULL;
2403 NTSTATUS status;
2405 if (!cache->tdb)
2406 goto do_query;
2408 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2410 if (!centry)
2411 goto do_query;
2413 policy->min_password_length = centry_uint16(centry);
2414 policy->password_history_length = centry_uint16(centry);
2415 policy->password_properties = centry_uint32(centry);
2416 policy->max_password_age = centry_nttime(centry);
2417 policy->min_password_age = centry_nttime(centry);
2419 status = centry->status;
2421 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2422 domain->name, nt_errstr(status) ));
2424 centry_free(centry);
2425 return status;
2427 do_query:
2428 ZERO_STRUCTP(policy);
2430 /* Return status value returned by seq number check */
2432 if (!NT_STATUS_IS_OK(domain->last_status))
2433 return domain->last_status;
2435 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2436 domain->name ));
2438 status = domain->backend->password_policy(domain, mem_ctx, policy);
2440 /* and save it */
2441 refresh_sequence_number(domain, false);
2442 if (NT_STATUS_IS_OK(status)) {
2443 wcache_save_password_policy(domain, status, policy);
2446 return status;
2450 /* Invalidate cached user and group lists coherently */
2452 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2453 void *state)
2455 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2456 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2457 tdb_delete(the_tdb, kbuf);
2459 return 0;
2462 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2464 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2465 struct netr_SamInfo3 *info3)
2467 DOM_SID sid;
2468 fstring key_str, sid_string;
2469 struct winbind_cache *cache;
2471 /* dont clear cached U/SID and UG/SID entries when we want to logon
2472 * offline - gd */
2474 if (lp_winbind_offline_logon()) {
2475 return;
2478 if (!domain)
2479 return;
2481 cache = get_cache(domain);
2483 if (!cache->tdb) {
2484 return;
2487 sid_copy(&sid, info3->base.domain_sid);
2488 sid_append_rid(&sid, info3->base.rid);
2490 /* Clear U/SID cache entry */
2491 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2492 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2493 tdb_delete(cache->tdb, string_tdb_data(key_str));
2495 /* Clear UG/SID cache entry */
2496 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2497 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2498 tdb_delete(cache->tdb, string_tdb_data(key_str));
2500 /* Samba/winbindd never needs this. */
2501 netsamlogon_clear_cached_user(info3);
2504 bool wcache_invalidate_cache(void)
2506 struct winbindd_domain *domain;
2508 for (domain = domain_list(); domain; domain = domain->next) {
2509 struct winbind_cache *cache = get_cache(domain);
2511 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2512 "entries for %s\n", domain->name));
2513 if (cache) {
2514 if (cache->tdb) {
2515 tdb_traverse(cache->tdb, traverse_fn, NULL);
2516 } else {
2517 return false;
2521 return true;
2524 bool init_wcache(void)
2526 if (wcache == NULL) {
2527 wcache = SMB_XMALLOC_P(struct winbind_cache);
2528 ZERO_STRUCTP(wcache);
2531 if (wcache->tdb != NULL)
2532 return true;
2534 /* when working offline we must not clear the cache on restart */
2535 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2536 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2537 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2538 O_RDWR|O_CREAT, 0600);
2540 if (wcache->tdb == NULL) {
2541 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2542 return false;
2545 return true;
2548 /************************************************************************
2549 This is called by the parent to initialize the cache file.
2550 We don't need sophisticated locking here as we know we're the
2551 only opener.
2552 ************************************************************************/
2554 bool initialize_winbindd_cache(void)
2556 bool cache_bad = true;
2557 uint32 vers;
2559 if (!init_wcache()) {
2560 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2561 return false;
2564 /* Check version number. */
2565 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2566 vers == WINBINDD_CACHE_VERSION) {
2567 cache_bad = false;
2570 if (cache_bad) {
2571 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2572 "and re-creating with version number %d\n",
2573 WINBINDD_CACHE_VERSION ));
2575 tdb_close(wcache->tdb);
2576 wcache->tdb = NULL;
2578 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2579 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2580 lock_path("winbindd_cache.tdb"),
2581 strerror(errno) ));
2582 return false;
2584 if (!init_wcache()) {
2585 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2586 "init_wcache failed.\n"));
2587 return false;
2590 /* Write the version. */
2591 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2592 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2593 tdb_errorstr(wcache->tdb) ));
2594 return false;
2598 tdb_close(wcache->tdb);
2599 wcache->tdb = NULL;
2600 return true;
2603 void close_winbindd_cache(void)
2605 if (!wcache) {
2606 return;
2608 if (wcache->tdb) {
2609 tdb_close(wcache->tdb);
2610 wcache->tdb = NULL;
2614 void cache_store_response(pid_t pid, struct winbindd_response *response)
2616 fstring key_str;
2618 if (!init_wcache())
2619 return;
2621 DEBUG(10, ("Storing response for pid %d, len %d\n",
2622 pid, response->length));
2624 fstr_sprintf(key_str, "DR/%d", pid);
2625 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2626 make_tdb_data((uint8 *)response, sizeof(*response)),
2627 TDB_REPLACE) == -1)
2628 return;
2630 if (response->length == sizeof(*response))
2631 return;
2633 /* There's extra data */
2635 DEBUG(10, ("Storing extra data: len=%d\n",
2636 (int)(response->length - sizeof(*response))));
2638 fstr_sprintf(key_str, "DE/%d", pid);
2639 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2640 make_tdb_data((uint8 *)response->extra_data.data,
2641 response->length - sizeof(*response)),
2642 TDB_REPLACE) == 0)
2643 return;
2645 /* We could not store the extra data, make sure the tdb does not
2646 * contain a main record with wrong dangling extra data */
2648 fstr_sprintf(key_str, "DR/%d", pid);
2649 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2651 return;
2654 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2656 TDB_DATA data;
2657 fstring key_str;
2659 if (!init_wcache())
2660 return false;
2662 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2664 fstr_sprintf(key_str, "DR/%d", pid);
2665 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2667 if (data.dptr == NULL)
2668 return false;
2670 if (data.dsize != sizeof(*response))
2671 return false;
2673 memcpy(response, data.dptr, data.dsize);
2674 SAFE_FREE(data.dptr);
2676 if (response->length == sizeof(*response)) {
2677 response->extra_data.data = NULL;
2678 return true;
2681 /* There's extra data */
2683 DEBUG(10, ("Retrieving extra data length=%d\n",
2684 (int)(response->length - sizeof(*response))));
2686 fstr_sprintf(key_str, "DE/%d", pid);
2687 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2689 if (data.dptr == NULL) {
2690 DEBUG(0, ("Did not find extra data\n"));
2691 return false;
2694 if (data.dsize != (response->length - sizeof(*response))) {
2695 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2696 SAFE_FREE(data.dptr);
2697 return false;
2700 dump_data(11, (uint8 *)data.dptr, data.dsize);
2702 response->extra_data.data = data.dptr;
2703 return true;
2706 void cache_cleanup_response(pid_t pid)
2708 fstring key_str;
2710 if (!init_wcache())
2711 return;
2713 fstr_sprintf(key_str, "DR/%d", pid);
2714 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2716 fstr_sprintf(key_str, "DE/%d", pid);
2717 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2719 return;
2723 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2724 char **domain_name, char **name,
2725 enum lsa_SidType *type)
2727 struct winbindd_domain *domain;
2728 struct winbind_cache *cache;
2729 struct cache_entry *centry = NULL;
2730 NTSTATUS status;
2731 fstring tmp;
2733 domain = find_lookup_domain_from_sid(sid);
2734 if (domain == NULL) {
2735 return false;
2738 cache = get_cache(domain);
2740 if (cache->tdb == NULL) {
2741 return false;
2744 centry = wcache_fetch(cache, domain, "SN/%s",
2745 sid_to_fstring(tmp, sid));
2746 if (centry == NULL) {
2747 return false;
2750 if (NT_STATUS_IS_OK(centry->status)) {
2751 *type = (enum lsa_SidType)centry_uint32(centry);
2752 *domain_name = centry_string(centry, mem_ctx);
2753 *name = centry_string(centry, mem_ctx);
2756 status = centry->status;
2757 centry_free(centry);
2758 return NT_STATUS_IS_OK(status);
2761 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2762 const char *domain_name,
2763 const char *name,
2764 DOM_SID *sid,
2765 enum lsa_SidType *type)
2767 struct winbindd_domain *domain;
2768 struct winbind_cache *cache;
2769 struct cache_entry *centry = NULL;
2770 NTSTATUS status;
2771 fstring uname;
2772 bool original_online_state;
2774 domain = find_lookup_domain_from_name(domain_name);
2775 if (domain == NULL) {
2776 return false;
2779 cache = get_cache(domain);
2781 if (cache->tdb == NULL) {
2782 return false;
2785 fstrcpy(uname, name);
2786 strupper_m(uname);
2788 /* If we are doing a cached logon, temporarily set the domain
2789 offline so the cache won't expire the entry */
2791 original_online_state = domain->online;
2792 domain->online = false;
2793 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2794 domain->online = original_online_state;
2796 if (centry == NULL) {
2797 return false;
2800 if (NT_STATUS_IS_OK(centry->status)) {
2801 *type = (enum lsa_SidType)centry_uint32(centry);
2802 centry_sid(centry, mem_ctx, sid);
2805 status = centry->status;
2806 centry_free(centry);
2808 return NT_STATUS_IS_OK(status);
2811 void cache_name2sid(struct winbindd_domain *domain,
2812 const char *domain_name, const char *name,
2813 enum lsa_SidType type, const DOM_SID *sid)
2815 refresh_sequence_number(domain, false);
2816 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2817 sid, type);
2821 * The original idea that this cache only contains centries has
2822 * been blurred - now other stuff gets put in here. Ensure we
2823 * ignore these things on cleanup.
2826 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2827 TDB_DATA dbuf, void *state)
2829 struct cache_entry *centry;
2831 if (is_non_centry_key(kbuf)) {
2832 return 0;
2835 centry = wcache_fetch_raw((char *)kbuf.dptr);
2836 if (!centry) {
2837 return 0;
2840 if (!NT_STATUS_IS_OK(centry->status)) {
2841 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2842 tdb_delete(the_tdb, kbuf);
2845 centry_free(centry);
2846 return 0;
2849 /* flush the cache */
2850 void wcache_flush_cache(void)
2852 if (!wcache)
2853 return;
2854 if (wcache->tdb) {
2855 tdb_close(wcache->tdb);
2856 wcache->tdb = NULL;
2858 if (opt_nocache)
2859 return;
2861 /* when working offline we must not clear the cache on restart */
2862 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2863 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2864 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2865 O_RDWR|O_CREAT, 0600);
2867 if (!wcache->tdb) {
2868 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2869 return;
2872 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2874 DEBUG(10,("wcache_flush_cache success\n"));
2877 /* Count cached creds */
2879 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2880 void *state)
2882 int *cred_count = (int*)state;
2884 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2885 (*cred_count)++;
2887 return 0;
2890 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2892 struct winbind_cache *cache = get_cache(domain);
2894 *count = 0;
2896 if (!cache->tdb) {
2897 return NT_STATUS_INTERNAL_DB_ERROR;
2900 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2902 return NT_STATUS_OK;
2905 struct cred_list {
2906 struct cred_list *prev, *next;
2907 TDB_DATA key;
2908 fstring name;
2909 time_t created;
2911 static struct cred_list *wcache_cred_list;
2913 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2914 void *state)
2916 struct cred_list *cred;
2918 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2920 cred = SMB_MALLOC_P(struct cred_list);
2921 if (cred == NULL) {
2922 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2923 return -1;
2926 ZERO_STRUCTP(cred);
2928 /* save a copy of the key */
2930 fstrcpy(cred->name, (const char *)kbuf.dptr);
2931 DLIST_ADD(wcache_cred_list, cred);
2934 return 0;
2937 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2939 struct winbind_cache *cache = get_cache(domain);
2940 NTSTATUS status;
2941 int ret;
2942 struct cred_list *cred, *oldest = NULL;
2944 if (!cache->tdb) {
2945 return NT_STATUS_INTERNAL_DB_ERROR;
2948 /* we possibly already have an entry */
2949 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2951 fstring key_str, tmp;
2953 DEBUG(11,("we already have an entry, deleting that\n"));
2955 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2957 tdb_delete(cache->tdb, string_tdb_data(key_str));
2959 return NT_STATUS_OK;
2962 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2963 if (ret == 0) {
2964 return NT_STATUS_OK;
2965 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2966 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2969 ZERO_STRUCTP(oldest);
2971 for (cred = wcache_cred_list; cred; cred = cred->next) {
2973 TDB_DATA data;
2974 time_t t;
2976 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2977 if (!data.dptr) {
2978 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2979 cred->name));
2980 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2981 goto done;
2984 t = IVAL(data.dptr, 0);
2985 SAFE_FREE(data.dptr);
2987 if (!oldest) {
2988 oldest = SMB_MALLOC_P(struct cred_list);
2989 if (oldest == NULL) {
2990 status = NT_STATUS_NO_MEMORY;
2991 goto done;
2994 fstrcpy(oldest->name, cred->name);
2995 oldest->created = t;
2996 continue;
2999 if (t < oldest->created) {
3000 fstrcpy(oldest->name, cred->name);
3001 oldest->created = t;
3005 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3006 status = NT_STATUS_OK;
3007 } else {
3008 status = NT_STATUS_UNSUCCESSFUL;
3010 done:
3011 SAFE_FREE(wcache_cred_list);
3012 SAFE_FREE(oldest);
3014 return status;
3017 /* Change the global online/offline state. */
3018 bool set_global_winbindd_state_offline(void)
3020 TDB_DATA data;
3022 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3024 /* Only go offline if someone has created
3025 the key "WINBINDD_OFFLINE" in the cache tdb. */
3027 if (wcache == NULL || wcache->tdb == NULL) {
3028 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3029 return false;
3032 if (!lp_winbind_offline_logon()) {
3033 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3034 return false;
3037 if (global_winbindd_offline_state) {
3038 /* Already offline. */
3039 return true;
3042 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3044 if (!data.dptr || data.dsize != 4) {
3045 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3046 SAFE_FREE(data.dptr);
3047 return false;
3048 } else {
3049 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3050 global_winbindd_offline_state = true;
3051 SAFE_FREE(data.dptr);
3052 return true;
3056 void set_global_winbindd_state_online(void)
3058 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3060 if (!lp_winbind_offline_logon()) {
3061 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3062 return;
3065 if (!global_winbindd_offline_state) {
3066 /* Already online. */
3067 return;
3069 global_winbindd_offline_state = false;
3071 if (!wcache->tdb) {
3072 return;
3075 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3076 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3079 bool get_global_winbindd_state_offline(void)
3081 return global_winbindd_offline_state;
3084 /***********************************************************************
3085 Validate functions for all possible cache tdb keys.
3086 ***********************************************************************/
3088 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3089 struct tdb_validation_status *state)
3091 struct cache_entry *centry;
3093 centry = SMB_XMALLOC_P(struct cache_entry);
3094 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3095 if (!centry->data) {
3096 SAFE_FREE(centry);
3097 return NULL;
3099 centry->len = data.dsize;
3100 centry->ofs = 0;
3102 if (centry->len < 8) {
3103 /* huh? corrupt cache? */
3104 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3105 centry_free(centry);
3106 state->bad_entry = true;
3107 state->success = false;
3108 return NULL;
3111 centry->status = NT_STATUS(centry_uint32(centry));
3112 centry->sequence_number = centry_uint32(centry);
3113 return centry;
3116 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3117 struct tdb_validation_status *state)
3119 if (dbuf.dsize != 8) {
3120 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3121 keystr, (unsigned int)dbuf.dsize ));
3122 state->bad_entry = true;
3123 return 1;
3125 return 0;
3128 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3129 struct tdb_validation_status *state)
3131 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3132 if (!centry) {
3133 return 1;
3136 (void)centry_uint32(centry);
3137 if (NT_STATUS_IS_OK(centry->status)) {
3138 DOM_SID sid;
3139 (void)centry_sid(centry, mem_ctx, &sid);
3142 centry_free(centry);
3144 if (!(state->success)) {
3145 return 1;
3147 DEBUG(10,("validate_ns: %s ok\n", keystr));
3148 return 0;
3151 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3152 struct tdb_validation_status *state)
3154 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3155 if (!centry) {
3156 return 1;
3159 if (NT_STATUS_IS_OK(centry->status)) {
3160 (void)centry_uint32(centry);
3161 (void)centry_string(centry, mem_ctx);
3162 (void)centry_string(centry, mem_ctx);
3165 centry_free(centry);
3167 if (!(state->success)) {
3168 return 1;
3170 DEBUG(10,("validate_sn: %s ok\n", keystr));
3171 return 0;
3174 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3175 struct tdb_validation_status *state)
3177 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3178 DOM_SID sid;
3180 if (!centry) {
3181 return 1;
3184 (void)centry_string(centry, mem_ctx);
3185 (void)centry_string(centry, mem_ctx);
3186 (void)centry_string(centry, mem_ctx);
3187 (void)centry_string(centry, mem_ctx);
3188 (void)centry_uint32(centry);
3189 (void)centry_sid(centry, mem_ctx, &sid);
3190 (void)centry_sid(centry, mem_ctx, &sid);
3192 centry_free(centry);
3194 if (!(state->success)) {
3195 return 1;
3197 DEBUG(10,("validate_u: %s ok\n", keystr));
3198 return 0;
3201 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3202 struct tdb_validation_status *state)
3204 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3206 if (!centry) {
3207 return 1;
3210 (void)centry_nttime(centry);
3211 (void)centry_nttime(centry);
3212 (void)centry_uint16(centry);
3214 centry_free(centry);
3216 if (!(state->success)) {
3217 return 1;
3219 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3220 return 0;
3223 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3224 struct tdb_validation_status *state)
3226 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3228 if (!centry) {
3229 return 1;
3232 (void)centry_uint16(centry);
3233 (void)centry_uint16(centry);
3234 (void)centry_uint32(centry);
3235 (void)centry_nttime(centry);
3236 (void)centry_nttime(centry);
3238 centry_free(centry);
3240 if (!(state->success)) {
3241 return 1;
3243 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3244 return 0;
3247 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3248 struct tdb_validation_status *state)
3250 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3252 if (!centry) {
3253 return 1;
3256 (void)centry_time(centry);
3257 (void)centry_hash16(centry, mem_ctx);
3259 /* We only have 17 bytes more data in the salted cred case. */
3260 if (centry->len - centry->ofs == 17) {
3261 (void)centry_hash16(centry, mem_ctx);
3264 centry_free(centry);
3266 if (!(state->success)) {
3267 return 1;
3269 DEBUG(10,("validate_cred: %s ok\n", keystr));
3270 return 0;
3273 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3274 struct tdb_validation_status *state)
3276 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3277 int32 num_entries, i;
3279 if (!centry) {
3280 return 1;
3283 num_entries = (int32)centry_uint32(centry);
3285 for (i=0; i< num_entries; i++) {
3286 DOM_SID sid;
3287 (void)centry_string(centry, mem_ctx);
3288 (void)centry_string(centry, mem_ctx);
3289 (void)centry_string(centry, mem_ctx);
3290 (void)centry_string(centry, mem_ctx);
3291 (void)centry_sid(centry, mem_ctx, &sid);
3292 (void)centry_sid(centry, mem_ctx, &sid);
3295 centry_free(centry);
3297 if (!(state->success)) {
3298 return 1;
3300 DEBUG(10,("validate_ul: %s ok\n", keystr));
3301 return 0;
3304 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3305 struct tdb_validation_status *state)
3307 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3308 int32 num_entries, i;
3310 if (!centry) {
3311 return 1;
3314 num_entries = centry_uint32(centry);
3316 for (i=0; i< num_entries; i++) {
3317 (void)centry_string(centry, mem_ctx);
3318 (void)centry_string(centry, mem_ctx);
3319 (void)centry_uint32(centry);
3322 centry_free(centry);
3324 if (!(state->success)) {
3325 return 1;
3327 DEBUG(10,("validate_gl: %s ok\n", keystr));
3328 return 0;
3331 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3332 struct tdb_validation_status *state)
3334 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3335 int32 num_groups, i;
3337 if (!centry) {
3338 return 1;
3341 num_groups = centry_uint32(centry);
3343 for (i=0; i< num_groups; i++) {
3344 DOM_SID sid;
3345 centry_sid(centry, mem_ctx, &sid);
3348 centry_free(centry);
3350 if (!(state->success)) {
3351 return 1;
3353 DEBUG(10,("validate_ug: %s ok\n", keystr));
3354 return 0;
3357 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3358 struct tdb_validation_status *state)
3360 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3361 int32 num_aliases, i;
3363 if (!centry) {
3364 return 1;
3367 num_aliases = centry_uint32(centry);
3369 for (i=0; i < num_aliases; i++) {
3370 (void)centry_uint32(centry);
3373 centry_free(centry);
3375 if (!(state->success)) {
3376 return 1;
3378 DEBUG(10,("validate_ua: %s ok\n", keystr));
3379 return 0;
3382 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3383 struct tdb_validation_status *state)
3385 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3386 int32 num_names, i;
3388 if (!centry) {
3389 return 1;
3392 num_names = centry_uint32(centry);
3394 for (i=0; i< num_names; i++) {
3395 DOM_SID sid;
3396 centry_sid(centry, mem_ctx, &sid);
3397 (void)centry_string(centry, mem_ctx);
3398 (void)centry_uint32(centry);
3401 centry_free(centry);
3403 if (!(state->success)) {
3404 return 1;
3406 DEBUG(10,("validate_gm: %s ok\n", keystr));
3407 return 0;
3410 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3411 struct tdb_validation_status *state)
3413 /* Can't say anything about this other than must be nonzero. */
3414 if (dbuf.dsize == 0) {
3415 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3416 keystr));
3417 state->bad_entry = true;
3418 state->success = false;
3419 return 1;
3422 DEBUG(10,("validate_dr: %s ok\n", keystr));
3423 return 0;
3426 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3427 struct tdb_validation_status *state)
3429 /* Can't say anything about this other than must be nonzero. */
3430 if (dbuf.dsize == 0) {
3431 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3432 keystr));
3433 state->bad_entry = true;
3434 state->success = false;
3435 return 1;
3438 DEBUG(10,("validate_de: %s ok\n", keystr));
3439 return 0;
3442 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3443 TDB_DATA dbuf, struct tdb_validation_status *state)
3445 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3447 if (!centry) {
3448 return 1;
3451 (void)centry_string(centry, mem_ctx);
3452 (void)centry_string(centry, mem_ctx);
3453 (void)centry_string(centry, mem_ctx);
3454 (void)centry_uint32(centry);
3456 centry_free(centry);
3458 if (!(state->success)) {
3459 return 1;
3461 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3462 return 0;
3465 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3466 TDB_DATA dbuf,
3467 struct tdb_validation_status *state)
3469 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3471 if (!centry) {
3472 return 1;
3475 (void)centry_string( centry, mem_ctx );
3477 centry_free(centry);
3479 if (!(state->success)) {
3480 return 1;
3482 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3483 return 0;
3486 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3487 TDB_DATA dbuf,
3488 struct tdb_validation_status *state)
3490 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3492 if (!centry) {
3493 return 1;
3496 (void)centry_string( centry, mem_ctx );
3498 centry_free(centry);
3500 if (!(state->success)) {
3501 return 1;
3503 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3504 return 0;
3507 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3508 struct tdb_validation_status *state)
3510 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3511 int32 num_domains, i;
3513 if (!centry) {
3514 return 1;
3517 num_domains = centry_uint32(centry);
3519 for (i=0; i< num_domains; i++) {
3520 DOM_SID sid;
3521 (void)centry_string(centry, mem_ctx);
3522 (void)centry_string(centry, mem_ctx);
3523 (void)centry_sid(centry, mem_ctx, &sid);
3526 centry_free(centry);
3528 if (!(state->success)) {
3529 return 1;
3531 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3532 return 0;
3535 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3536 TDB_DATA dbuf,
3537 struct tdb_validation_status *state)
3539 if (dbuf.dsize == 0) {
3540 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3541 "key %s (len ==0) ?\n", keystr));
3542 state->bad_entry = true;
3543 state->success = false;
3544 return 1;
3547 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3548 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3549 return 0;
3552 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3553 struct tdb_validation_status *state)
3555 if (dbuf.dsize != 4) {
3556 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3557 keystr, (unsigned int)dbuf.dsize ));
3558 state->bad_entry = true;
3559 state->success = false;
3560 return 1;
3562 DEBUG(10,("validate_offline: %s ok\n", keystr));
3563 return 0;
3566 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3567 struct tdb_validation_status *state)
3569 if (dbuf.dsize != 4) {
3570 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3571 "key %s (len %u != 4) ?\n",
3572 keystr, (unsigned int)dbuf.dsize));
3573 state->bad_entry = true;
3574 state->success = false;
3575 return 1;
3578 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3579 return 0;
3582 /***********************************************************************
3583 A list of all possible cache tdb keys with associated validation
3584 functions.
3585 ***********************************************************************/
3587 struct key_val_struct {
3588 const char *keyname;
3589 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3590 } key_val[] = {
3591 {"SEQNUM/", validate_seqnum},
3592 {"NS/", validate_ns},
3593 {"SN/", validate_sn},
3594 {"U/", validate_u},
3595 {"LOC_POL/", validate_loc_pol},
3596 {"PWD_POL/", validate_pwd_pol},
3597 {"CRED/", validate_cred},
3598 {"UL/", validate_ul},
3599 {"GL/", validate_gl},
3600 {"UG/", validate_ug},
3601 {"UA", validate_ua},
3602 {"GM/", validate_gm},
3603 {"DR/", validate_dr},
3604 {"DE/", validate_de},
3605 {"NSS/PWINFO/", validate_pwinfo},
3606 {"TRUSTDOMS/", validate_trustdoms},
3607 {"TRUSTDOMCACHE/", validate_trustdomcache},
3608 {"NSS/NA/", validate_nss_na},
3609 {"NSS/AN/", validate_nss_an},
3610 {"WINBINDD_OFFLINE", validate_offline},
3611 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3612 {NULL, NULL}
3615 /***********************************************************************
3616 Function to look at every entry in the tdb and validate it as far as
3617 possible.
3618 ***********************************************************************/
3620 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3622 int i;
3623 unsigned int max_key_len = 1024;
3624 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3626 /* Paranoia check. */
3627 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3628 max_key_len = 1024 * 1024;
3630 if (kbuf.dsize > max_key_len) {
3631 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3632 "(%u) > (%u)\n\n",
3633 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3634 return 1;
3637 for (i = 0; key_val[i].keyname; i++) {
3638 size_t namelen = strlen(key_val[i].keyname);
3639 if (kbuf.dsize >= namelen && (
3640 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3641 TALLOC_CTX *mem_ctx;
3642 char *keystr;
3643 int ret;
3645 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3646 if (!keystr) {
3647 return 1;
3649 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3650 keystr[kbuf.dsize] = '\0';
3652 mem_ctx = talloc_init("validate_ctx");
3653 if (!mem_ctx) {
3654 SAFE_FREE(keystr);
3655 return 1;
3658 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3659 v_state);
3661 SAFE_FREE(keystr);
3662 talloc_destroy(mem_ctx);
3663 return ret;
3667 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3668 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3669 DEBUG(0,("data :\n"));
3670 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3671 v_state->unknown_key = true;
3672 v_state->success = false;
3673 return 1; /* terminate. */
3676 static void validate_panic(const char *const why)
3678 DEBUG(0,("validating cache: would panic %s\n", why ));
3679 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3680 exit(47);
3683 /***********************************************************************
3684 Try and validate every entry in the winbindd cache. If we fail here,
3685 delete the cache tdb and return non-zero.
3686 ***********************************************************************/
3688 int winbindd_validate_cache(void)
3690 int ret = -1;
3691 const char *tdb_path = lock_path("winbindd_cache.tdb");
3692 TDB_CONTEXT *tdb = NULL;
3694 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3695 smb_panic_fn = validate_panic;
3698 tdb = tdb_open_log(tdb_path,
3699 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3700 ( lp_winbind_offline_logon()
3701 ? TDB_DEFAULT
3702 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3703 O_RDWR|O_CREAT,
3704 0600);
3705 if (!tdb) {
3706 DEBUG(0, ("winbindd_validate_cache: "
3707 "error opening/initializing tdb\n"));
3708 goto done;
3710 tdb_close(tdb);
3712 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3714 if (ret != 0) {
3715 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3716 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3717 unlink(tdb_path);
3720 done:
3721 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3722 smb_panic_fn = smb_panic;
3723 return ret;
3726 /***********************************************************************
3727 Try and validate every entry in the winbindd cache.
3728 ***********************************************************************/
3730 int winbindd_validate_cache_nobackup(void)
3732 int ret = -1;
3733 const char *tdb_path = lock_path("winbindd_cache.tdb");
3735 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3736 smb_panic_fn = validate_panic;
3739 if (wcache == NULL || wcache->tdb == NULL) {
3740 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3741 } else {
3742 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3745 if (ret != 0) {
3746 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3747 "successful.\n"));
3750 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3751 "function\n"));
3752 smb_panic_fn = smb_panic;
3753 return ret;
3756 bool winbindd_cache_validate_and_initialize(void)
3758 close_winbindd_cache();
3760 if (lp_winbind_offline_logon()) {
3761 if (winbindd_validate_cache() < 0) {
3762 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3763 "could be restored.\n"));
3767 return initialize_winbindd_cache();
3770 /*********************************************************************
3771 ********************************************************************/
3773 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3774 struct winbindd_tdc_domain **domains,
3775 size_t *num_domains )
3777 struct winbindd_tdc_domain *list = NULL;
3778 size_t idx;
3779 int i;
3780 bool set_only = false;
3782 /* don't allow duplicates */
3784 idx = *num_domains;
3785 list = *domains;
3787 for ( i=0; i< (*num_domains); i++ ) {
3788 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3789 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3790 new_dom->name));
3791 idx = i;
3792 set_only = true;
3794 break;
3798 if ( !set_only ) {
3799 if ( !*domains ) {
3800 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3801 idx = 0;
3802 } else {
3803 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3804 struct winbindd_tdc_domain,
3805 (*num_domains)+1);
3806 idx = *num_domains;
3809 ZERO_STRUCT( list[idx] );
3812 if ( !list )
3813 return false;
3815 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3816 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3818 if ( !is_null_sid( &new_dom->sid ) ) {
3819 sid_copy( &list[idx].sid, &new_dom->sid );
3820 } else {
3821 sid_copy(&list[idx].sid, &global_sid_NULL);
3824 if ( new_dom->domain_flags != 0x0 )
3825 list[idx].trust_flags = new_dom->domain_flags;
3827 if ( new_dom->domain_type != 0x0 )
3828 list[idx].trust_type = new_dom->domain_type;
3830 if ( new_dom->domain_trust_attribs != 0x0 )
3831 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3833 if ( !set_only ) {
3834 *domains = list;
3835 *num_domains = idx + 1;
3838 return true;
3841 /*********************************************************************
3842 ********************************************************************/
3844 static TDB_DATA make_tdc_key( const char *domain_name )
3846 char *keystr = NULL;
3847 TDB_DATA key = { NULL, 0 };
3849 if ( !domain_name ) {
3850 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3851 return key;
3855 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3856 key = string_term_tdb_data(keystr);
3858 return key;
3861 /*********************************************************************
3862 ********************************************************************/
3864 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3865 size_t num_domains,
3866 unsigned char **buf )
3868 unsigned char *buffer = NULL;
3869 int len = 0;
3870 int buflen = 0;
3871 int i = 0;
3873 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3874 (int)num_domains));
3876 buflen = 0;
3878 again:
3879 len = 0;
3881 /* Store the number of array items first */
3882 len += tdb_pack( buffer+len, buflen-len, "d",
3883 num_domains );
3885 /* now pack each domain trust record */
3886 for ( i=0; i<num_domains; i++ ) {
3888 fstring tmp;
3890 if ( buflen > 0 ) {
3891 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3892 domains[i].domain_name,
3893 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3896 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3897 domains[i].domain_name,
3898 domains[i].dns_name,
3899 sid_to_fstring(tmp, &domains[i].sid),
3900 domains[i].trust_flags,
3901 domains[i].trust_attribs,
3902 domains[i].trust_type );
3905 if ( buflen < len ) {
3906 SAFE_FREE(buffer);
3907 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3908 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3909 buflen = -1;
3910 goto done;
3912 buflen = len;
3913 goto again;
3916 *buf = buffer;
3918 done:
3919 return buflen;
3922 /*********************************************************************
3923 ********************************************************************/
3925 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3926 struct winbindd_tdc_domain **domains )
3928 fstring domain_name, dns_name, sid_string;
3929 uint32 type, attribs, flags;
3930 int num_domains;
3931 int len = 0;
3932 int i;
3933 struct winbindd_tdc_domain *list = NULL;
3935 /* get the number of domains */
3936 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3937 if ( len == -1 ) {
3938 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3939 return 0;
3942 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3943 if ( !list ) {
3944 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3945 return 0;
3948 for ( i=0; i<num_domains; i++ ) {
3949 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3950 domain_name,
3951 dns_name,
3952 sid_string,
3953 &flags,
3954 &attribs,
3955 &type );
3957 if ( len == -1 ) {
3958 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3959 TALLOC_FREE( list );
3960 return 0;
3963 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3964 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3965 domain_name, dns_name, sid_string,
3966 flags, attribs, type));
3968 list[i].domain_name = talloc_strdup( list, domain_name );
3969 list[i].dns_name = talloc_strdup( list, dns_name );
3970 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3971 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3972 domain_name));
3974 list[i].trust_flags = flags;
3975 list[i].trust_attribs = attribs;
3976 list[i].trust_type = type;
3979 *domains = list;
3981 return num_domains;
3984 /*********************************************************************
3985 ********************************************************************/
3987 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3989 TDB_DATA key = make_tdc_key( lp_workgroup() );
3990 TDB_DATA data = { NULL, 0 };
3991 int ret;
3993 if ( !key.dptr )
3994 return false;
3996 /* See if we were asked to delete the cache entry */
3998 if ( !domains ) {
3999 ret = tdb_delete( wcache->tdb, key );
4000 goto done;
4003 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4005 if ( !data.dptr ) {
4006 ret = -1;
4007 goto done;
4010 ret = tdb_store( wcache->tdb, key, data, 0 );
4012 done:
4013 SAFE_FREE( data.dptr );
4014 SAFE_FREE( key.dptr );
4016 return ( ret != -1 );
4019 /*********************************************************************
4020 ********************************************************************/
4022 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4024 TDB_DATA key = make_tdc_key( lp_workgroup() );
4025 TDB_DATA data = { NULL, 0 };
4027 *domains = NULL;
4028 *num_domains = 0;
4030 if ( !key.dptr )
4031 return false;
4033 data = tdb_fetch( wcache->tdb, key );
4035 SAFE_FREE( key.dptr );
4037 if ( !data.dptr )
4038 return false;
4040 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4042 SAFE_FREE( data.dptr );
4044 if ( !*domains )
4045 return false;
4047 return true;
4050 /*********************************************************************
4051 ********************************************************************/
4053 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4055 struct winbindd_tdc_domain *dom_list = NULL;
4056 size_t num_domains = 0;
4057 bool ret = false;
4059 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4060 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4061 domain->name, domain->alt_name,
4062 sid_string_dbg(&domain->sid),
4063 domain->domain_flags,
4064 domain->domain_trust_attribs,
4065 domain->domain_type));
4067 if ( !init_wcache() ) {
4068 return false;
4071 /* fetch the list */
4073 wcache_tdc_fetch_list( &dom_list, &num_domains );
4075 /* add the new domain */
4077 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4078 goto done;
4081 /* pack the domain */
4083 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4084 goto done;
4087 /* Success */
4089 ret = true;
4090 done:
4091 TALLOC_FREE( dom_list );
4093 return ret;
4096 /*********************************************************************
4097 ********************************************************************/
4099 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4101 struct winbindd_tdc_domain *dom_list = NULL;
4102 size_t num_domains = 0;
4103 int i;
4104 struct winbindd_tdc_domain *d = NULL;
4106 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4108 if ( !init_wcache() ) {
4109 return false;
4112 /* fetch the list */
4114 wcache_tdc_fetch_list( &dom_list, &num_domains );
4116 for ( i=0; i<num_domains; i++ ) {
4117 if ( strequal(name, dom_list[i].domain_name) ||
4118 strequal(name, dom_list[i].dns_name) )
4120 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4121 name));
4123 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4124 if ( !d )
4125 break;
4127 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4128 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4129 sid_copy( &d->sid, &dom_list[i].sid );
4130 d->trust_flags = dom_list[i].trust_flags;
4131 d->trust_type = dom_list[i].trust_type;
4132 d->trust_attribs = dom_list[i].trust_attribs;
4134 break;
4138 TALLOC_FREE( dom_list );
4140 return d;
4144 /*********************************************************************
4145 ********************************************************************/
4147 void wcache_tdc_clear( void )
4149 if ( !init_wcache() )
4150 return;
4152 wcache_tdc_store_list( NULL, 0 );
4154 return;
4158 /*********************************************************************
4159 ********************************************************************/
4161 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4162 NTSTATUS status,
4163 const DOM_SID *user_sid,
4164 const char *homedir,
4165 const char *shell,
4166 const char *gecos,
4167 uint32 gid)
4169 struct cache_entry *centry;
4170 fstring tmp;
4172 if ( (centry = centry_start(domain, status)) == NULL )
4173 return;
4175 centry_put_string( centry, homedir );
4176 centry_put_string( centry, shell );
4177 centry_put_string( centry, gecos );
4178 centry_put_uint32( centry, gid );
4180 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4182 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4184 centry_free(centry);
4187 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4188 const DOM_SID *user_sid,
4189 TALLOC_CTX *ctx,
4190 ADS_STRUCT *ads, LDAPMessage *msg,
4191 char **homedir, char **shell, char **gecos,
4192 gid_t *p_gid)
4194 struct winbind_cache *cache = get_cache(domain);
4195 struct cache_entry *centry = NULL;
4196 NTSTATUS nt_status;
4197 fstring tmp;
4199 if (!cache->tdb)
4200 goto do_query;
4202 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4203 sid_to_fstring(tmp, user_sid));
4205 if (!centry)
4206 goto do_query;
4208 *homedir = centry_string( centry, ctx );
4209 *shell = centry_string( centry, ctx );
4210 *gecos = centry_string( centry, ctx );
4211 *p_gid = centry_uint32( centry );
4213 centry_free(centry);
4215 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4216 sid_string_dbg(user_sid)));
4218 return NT_STATUS_OK;
4220 do_query:
4222 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4223 homedir, shell, gecos, p_gid );
4225 if ( NT_STATUS_IS_OK(nt_status) ) {
4226 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4227 *homedir, *shell, *gecos, *p_gid );
4230 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4231 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4232 domain->name ));
4233 set_domain_offline( domain );
4236 return nt_status;
4240 /* the cache backend methods are exposed via this structure */
4241 struct winbindd_methods cache_methods = {
4242 true,
4243 query_user_list,
4244 enum_dom_groups,
4245 enum_local_groups,
4246 name_to_sid,
4247 sid_to_name,
4248 rids_to_names,
4249 query_user,
4250 lookup_usergroups,
4251 lookup_useraliases,
4252 lookup_groupmem,
4253 sequence_number,
4254 lockout_policy,
4255 password_policy,
4256 trusted_domains