s3:ntlmssp: make use of C99 types in ntlmssp_sign.c
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blobdb2f3b6b8b3607ce1df19305faf3a7592fc997ba
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"
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #undef DBGC_CLASS
33 #define DBGC_CLASS DBGC_WINBIND
35 #define WINBINDD_CACHE_VERSION 1
36 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
38 extern struct winbindd_methods reconnect_methods;
39 #ifdef HAVE_ADS
40 extern struct winbindd_methods ads_methods;
41 #endif
42 extern struct winbindd_methods builtin_passdb_methods;
45 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
46 * Here are the list of entry types that are *not* stored
47 * as form struct cache_entry in the cache.
50 static const char *non_centry_keys[] = {
51 "SEQNUM/",
52 "DR/",
53 "DE/",
54 "WINBINDD_OFFLINE",
55 WINBINDD_CACHE_VERSION_KEYSTR,
56 NULL
59 /************************************************************************
60 Is this key a non-centry type ?
61 ************************************************************************/
63 static bool is_non_centry_key(TDB_DATA kbuf)
65 int i;
67 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
68 return false;
70 for (i = 0; non_centry_keys[i] != NULL; i++) {
71 size_t namelen = strlen(non_centry_keys[i]);
72 if (kbuf.dsize < namelen) {
73 continue;
75 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
76 return true;
79 return false;
82 /* Global online/offline state - False when online. winbindd starts up online
83 and sets this to true if the first query fails and there's an entry in
84 the cache tdb telling us to stay offline. */
86 static bool global_winbindd_offline_state;
88 struct winbind_cache {
89 TDB_CONTEXT *tdb;
92 struct cache_entry {
93 NTSTATUS status;
94 uint32 sequence_number;
95 uint8 *data;
96 uint32 len, ofs;
99 void (*smb_panic_fn)(const char *const why) = smb_panic;
101 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
103 static struct winbind_cache *wcache;
105 void winbindd_check_cache_size(time_t t)
107 static time_t last_check_time;
108 struct stat st;
110 if (last_check_time == (time_t)0)
111 last_check_time = t;
113 if (t - last_check_time < 60 && t - last_check_time > 0)
114 return;
116 if (wcache == NULL || wcache->tdb == NULL) {
117 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
118 return;
121 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
122 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
123 return;
126 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
127 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
128 (unsigned long)st.st_size,
129 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
130 wcache_flush_cache();
134 /* get the winbind_cache structure */
135 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
137 struct winbind_cache *ret = wcache;
139 /* We have to know what type of domain we are dealing with first. */
141 if (domain->internal) {
142 domain->backend = &builtin_passdb_methods;
143 domain->initialized = True;
145 if ( !domain->initialized ) {
146 init_dc_connection( domain );
150 OK. listen up becasue I'm only going to say this once.
151 We have the following scenarios to consider
152 (a) trusted AD domains on a Samba DC,
153 (b) trusted AD domains and we are joined to a non-kerberos domain
154 (c) trusted AD domains and we are joined to a kerberos (AD) domain
156 For (a) we can always contact the trusted domain using krb5
157 since we have the domain trust account password
159 For (b) we can only use RPC since we have no way of
160 getting a krb5 ticket in our own domain
162 For (c) we can always use krb5 since we have a kerberos trust
164 --jerry
167 if (!domain->backend) {
168 #ifdef HAVE_ADS
169 struct winbindd_domain *our_domain = domain;
171 /* find our domain first so we can figure out if we
172 are joined to a kerberized domain */
174 if ( !domain->primary )
175 our_domain = find_our_domain();
177 if ((our_domain->active_directory || IS_DC)
178 && domain->active_directory
179 && !lp_winbind_rpc_only()) {
180 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
181 domain->backend = &ads_methods;
182 } else {
183 #endif /* HAVE_ADS */
184 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
185 domain->backend = &reconnect_methods;
186 #ifdef HAVE_ADS
188 #endif /* HAVE_ADS */
191 if (ret)
192 return ret;
194 ret = SMB_XMALLOC_P(struct winbind_cache);
195 ZERO_STRUCTP(ret);
197 wcache = ret;
198 wcache_flush_cache();
200 return ret;
204 free a centry structure
206 static void centry_free(struct cache_entry *centry)
208 if (!centry)
209 return;
210 SAFE_FREE(centry->data);
211 free(centry);
214 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
216 if (centry->len - centry->ofs < nbytes) {
217 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
218 (unsigned int)nbytes,
219 centry->len - centry->ofs));
220 return false;
222 return true;
226 pull a uint32 from a cache entry
228 static uint32 centry_uint32(struct cache_entry *centry)
230 uint32 ret;
232 if (!centry_check_bytes(centry, 4)) {
233 smb_panic_fn("centry_uint32");
235 ret = IVAL(centry->data, centry->ofs);
236 centry->ofs += 4;
237 return ret;
241 pull a uint16 from a cache entry
243 static uint16 centry_uint16(struct cache_entry *centry)
245 uint16 ret;
246 if (!centry_check_bytes(centry, 2)) {
247 smb_panic_fn("centry_uint16");
249 ret = CVAL(centry->data, centry->ofs);
250 centry->ofs += 2;
251 return ret;
255 pull a uint8 from a cache entry
257 static uint8 centry_uint8(struct cache_entry *centry)
259 uint8 ret;
260 if (!centry_check_bytes(centry, 1)) {
261 smb_panic_fn("centry_uint8");
263 ret = CVAL(centry->data, centry->ofs);
264 centry->ofs += 1;
265 return ret;
269 pull a NTTIME from a cache entry
271 static NTTIME centry_nttime(struct cache_entry *centry)
273 NTTIME ret;
274 if (!centry_check_bytes(centry, 8)) {
275 smb_panic_fn("centry_nttime");
277 ret = IVAL(centry->data, centry->ofs);
278 centry->ofs += 4;
279 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
280 centry->ofs += 4;
281 return ret;
285 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
287 static time_t centry_time(struct cache_entry *centry)
289 return (time_t)centry_nttime(centry);
292 /* pull a string from a cache entry, using the supplied
293 talloc context
295 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
297 uint32 len;
298 char *ret;
300 len = centry_uint8(centry);
302 if (len == 0xFF) {
303 /* a deliberate NULL string */
304 return NULL;
307 if (!centry_check_bytes(centry, (size_t)len)) {
308 smb_panic_fn("centry_string");
311 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
312 if (!ret) {
313 smb_panic_fn("centry_string out of memory\n");
315 memcpy(ret,centry->data + centry->ofs, len);
316 ret[len] = 0;
317 centry->ofs += len;
318 return ret;
321 /* pull a hash16 from a cache entry, using the supplied
322 talloc context
324 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
326 uint32 len;
327 char *ret;
329 len = centry_uint8(centry);
331 if (len != 16) {
332 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
333 len ));
334 return NULL;
337 if (!centry_check_bytes(centry, 16)) {
338 return NULL;
341 ret = TALLOC_ARRAY(mem_ctx, char, 16);
342 if (!ret) {
343 smb_panic_fn("centry_hash out of memory\n");
345 memcpy(ret,centry->data + centry->ofs, 16);
346 centry->ofs += 16;
347 return ret;
350 /* pull a sid from a cache entry, using the supplied
351 talloc context
353 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
355 char *sid_string;
356 bool ret;
358 sid_string = centry_string(centry, talloc_tos());
359 if (sid_string == NULL) {
360 return false;
362 ret = string_to_sid(sid, sid_string);
363 TALLOC_FREE(sid_string);
364 return ret;
369 pull a NTSTATUS from a cache entry
371 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
373 NTSTATUS status;
375 status = NT_STATUS(centry_uint32(centry));
376 return status;
380 /* the server is considered down if it can't give us a sequence number */
381 static bool wcache_server_down(struct winbindd_domain *domain)
383 bool ret;
385 if (!wcache->tdb)
386 return false;
388 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
390 if (ret)
391 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
392 domain->name ));
393 return ret;
396 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
397 uint32_t *last_seq_check)
399 char *key;
400 TDB_DATA data;
402 if (wcache->tdb == NULL) {
403 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
404 return false;
407 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
408 if (key == NULL) {
409 DEBUG(10, ("talloc failed\n"));
410 return false;
413 data = tdb_fetch_bystring(wcache->tdb, key);
414 TALLOC_FREE(key);
416 if (data.dptr == NULL) {
417 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
418 domain_name));
419 return false;
421 if (data.dsize != 8) {
422 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
423 (int)data.dsize));
424 SAFE_FREE(data.dptr);
425 return false;
428 *seqnum = IVAL(data.dptr, 0);
429 *last_seq_check = IVAL(data.dptr, 4);
430 SAFE_FREE(data.dptr);
432 return true;
435 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
437 uint32 last_check, time_diff;
439 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
440 &last_check)) {
441 return NT_STATUS_UNSUCCESSFUL;
443 domain->last_seq_check = last_check;
445 /* have we expired? */
447 time_diff = now - domain->last_seq_check;
448 if ( time_diff > lp_winbind_cache_time() ) {
449 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
450 domain->name, domain->sequence_number,
451 (uint32)domain->last_seq_check));
452 return NT_STATUS_UNSUCCESSFUL;
455 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
456 domain->name, domain->sequence_number,
457 (uint32)domain->last_seq_check));
459 return NT_STATUS_OK;
462 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
463 time_t last_seq_check)
465 char *key_str;
466 uint8_t buf[8];
467 int ret;
469 if (wcache->tdb == NULL) {
470 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
471 return false;
474 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
475 if (key_str == NULL) {
476 DEBUG(10, ("talloc_asprintf failed\n"));
477 return false;
480 SIVAL(buf, 0, seqnum);
481 SIVAL(buf, 4, last_seq_check);
483 ret = tdb_store_bystring(wcache->tdb, key_str,
484 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
485 TALLOC_FREE(key_str);
486 if (ret == -1) {
487 DEBUG(10, ("tdb_store_bystring failed: %s\n",
488 tdb_errorstr(wcache->tdb)));
489 TALLOC_FREE(key_str);
490 return false;
493 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
494 domain_name, seqnum, (unsigned)last_seq_check));
496 return true;
499 static bool store_cache_seqnum( struct winbindd_domain *domain )
501 return wcache_store_seqnum(domain->name, domain->sequence_number,
502 domain->last_seq_check);
506 refresh the domain sequence number. If force is true
507 then always refresh it, no matter how recently we fetched it
510 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
512 NTSTATUS status;
513 unsigned time_diff;
514 time_t t = time(NULL);
515 unsigned cache_time = lp_winbind_cache_time();
517 if (is_domain_offline(domain)) {
518 return;
521 get_cache( domain );
523 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
524 /* trying to reconnect is expensive, don't do it too often */
525 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
526 cache_time *= 8;
528 #endif
530 time_diff = t - domain->last_seq_check;
532 /* see if we have to refetch the domain sequence number */
533 if (!force && (time_diff < cache_time) &&
534 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
535 NT_STATUS_IS_OK(domain->last_status)) {
536 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
537 goto done;
540 /* try to get the sequence number from the tdb cache first */
541 /* this will update the timestamp as well */
543 status = fetch_cache_seqnum( domain, t );
544 if (NT_STATUS_IS_OK(status) &&
545 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
546 NT_STATUS_IS_OK(domain->last_status)) {
547 goto done;
550 /* important! make sure that we know if this is a native
551 mode domain or not. And that we can contact it. */
553 if ( winbindd_can_contact_domain( domain ) ) {
554 status = domain->backend->sequence_number(domain,
555 &domain->sequence_number);
556 } else {
557 /* just use the current time */
558 status = NT_STATUS_OK;
559 domain->sequence_number = time(NULL);
563 /* the above call could have set our domain->backend to NULL when
564 * coming from offline to online mode, make sure to reinitialize the
565 * backend - Guenther */
566 get_cache( domain );
568 if (!NT_STATUS_IS_OK(status)) {
569 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
570 domain->sequence_number = DOM_SEQUENCE_NONE;
573 domain->last_status = status;
574 domain->last_seq_check = time(NULL);
576 /* save the new sequence number in the cache */
577 store_cache_seqnum( domain );
579 done:
580 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
581 domain->name, domain->sequence_number));
583 return;
587 decide if a cache entry has expired
589 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
591 /* If we've been told to be offline - stay in that state... */
592 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
593 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
594 keystr, domain->name ));
595 return false;
598 /* when the domain is offline return the cached entry.
599 * This deals with transient offline states... */
601 if (!domain->online) {
602 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
603 keystr, domain->name ));
604 return false;
607 /* if the server is OK and our cache entry came from when it was down then
608 the entry is invalid */
609 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
610 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
611 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
612 keystr, domain->name ));
613 return true;
616 /* if the server is down or the cache entry is not older than the
617 current sequence number then it is OK */
618 if (wcache_server_down(domain) ||
619 centry->sequence_number == domain->sequence_number) {
620 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
621 keystr, domain->name ));
622 return false;
625 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
626 keystr, domain->name ));
628 /* it's expired */
629 return true;
632 static struct cache_entry *wcache_fetch_raw(char *kstr)
634 TDB_DATA data;
635 struct cache_entry *centry;
636 TDB_DATA key;
638 key = string_tdb_data(kstr);
639 data = tdb_fetch(wcache->tdb, key);
640 if (!data.dptr) {
641 /* a cache miss */
642 return NULL;
645 centry = SMB_XMALLOC_P(struct cache_entry);
646 centry->data = (unsigned char *)data.dptr;
647 centry->len = data.dsize;
648 centry->ofs = 0;
650 if (centry->len < 8) {
651 /* huh? corrupt cache? */
652 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
653 centry_free(centry);
654 return NULL;
657 centry->status = centry_ntstatus(centry);
658 centry->sequence_number = centry_uint32(centry);
660 return centry;
664 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
665 number and return status
667 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
668 struct winbindd_domain *domain,
669 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
670 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
671 struct winbindd_domain *domain,
672 const char *format, ...)
674 va_list ap;
675 char *kstr;
676 struct cache_entry *centry;
678 if (!winbindd_use_cache()) {
679 return NULL;
682 refresh_sequence_number(domain, false);
684 va_start(ap, format);
685 smb_xvasprintf(&kstr, format, ap);
686 va_end(ap);
688 centry = wcache_fetch_raw(kstr);
689 if (centry == NULL) {
690 free(kstr);
691 return NULL;
694 if (centry_expired(domain, kstr, centry)) {
696 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
697 kstr, domain->name ));
699 centry_free(centry);
700 free(kstr);
701 return NULL;
704 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
705 kstr, domain->name ));
707 free(kstr);
708 return centry;
711 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
712 static void wcache_delete(const char *format, ...)
714 va_list ap;
715 char *kstr;
716 TDB_DATA key;
718 va_start(ap, format);
719 smb_xvasprintf(&kstr, format, ap);
720 va_end(ap);
722 key = string_tdb_data(kstr);
724 tdb_delete(wcache->tdb, key);
725 free(kstr);
729 make sure we have at least len bytes available in a centry
731 static void centry_expand(struct cache_entry *centry, uint32 len)
733 if (centry->len - centry->ofs >= len)
734 return;
735 centry->len *= 2;
736 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
737 centry->len);
738 if (!centry->data) {
739 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
740 smb_panic_fn("out of memory in centry_expand");
745 push a uint32 into a centry
747 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
749 centry_expand(centry, 4);
750 SIVAL(centry->data, centry->ofs, v);
751 centry->ofs += 4;
755 push a uint16 into a centry
757 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
759 centry_expand(centry, 2);
760 SIVAL(centry->data, centry->ofs, v);
761 centry->ofs += 2;
765 push a uint8 into a centry
767 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
769 centry_expand(centry, 1);
770 SCVAL(centry->data, centry->ofs, v);
771 centry->ofs += 1;
775 push a string into a centry
777 static void centry_put_string(struct cache_entry *centry, const char *s)
779 int len;
781 if (!s) {
782 /* null strings are marked as len 0xFFFF */
783 centry_put_uint8(centry, 0xFF);
784 return;
787 len = strlen(s);
788 /* can't handle more than 254 char strings. Truncating is probably best */
789 if (len > 254) {
790 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
791 len = 254;
793 centry_put_uint8(centry, len);
794 centry_expand(centry, len);
795 memcpy(centry->data + centry->ofs, s, len);
796 centry->ofs += len;
800 push a 16 byte hash into a centry - treat as 16 byte string.
802 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
804 centry_put_uint8(centry, 16);
805 centry_expand(centry, 16);
806 memcpy(centry->data + centry->ofs, val, 16);
807 centry->ofs += 16;
810 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
812 fstring sid_string;
813 centry_put_string(centry, sid_to_fstring(sid_string, sid));
818 put NTSTATUS into a centry
820 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
822 uint32 status_value = NT_STATUS_V(status);
823 centry_put_uint32(centry, status_value);
828 push a NTTIME into a centry
830 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
832 centry_expand(centry, 8);
833 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
834 centry->ofs += 4;
835 SIVAL(centry->data, centry->ofs, nt >> 32);
836 centry->ofs += 4;
840 push a time_t into a centry - use a 64 bit size.
841 NTTIME here is being used as a convenient 64-bit size.
843 static void centry_put_time(struct cache_entry *centry, time_t t)
845 NTTIME nt = (NTTIME)t;
846 centry_put_nttime(centry, nt);
850 start a centry for output. When finished, call centry_end()
852 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
854 struct cache_entry *centry;
856 if (!wcache->tdb)
857 return NULL;
859 centry = SMB_XMALLOC_P(struct cache_entry);
861 centry->len = 8192; /* reasonable default */
862 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
863 centry->ofs = 0;
864 centry->sequence_number = domain->sequence_number;
865 centry_put_ntstatus(centry, status);
866 centry_put_uint32(centry, centry->sequence_number);
867 return centry;
871 finish a centry and write it to the tdb
873 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
874 static void centry_end(struct cache_entry *centry, const char *format, ...)
876 va_list ap;
877 char *kstr;
878 TDB_DATA key, data;
880 if (!winbindd_use_cache()) {
881 return;
884 va_start(ap, format);
885 smb_xvasprintf(&kstr, format, ap);
886 va_end(ap);
888 key = string_tdb_data(kstr);
889 data.dptr = centry->data;
890 data.dsize = centry->ofs;
892 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
893 free(kstr);
896 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
897 NTSTATUS status, const char *domain_name,
898 const char *name, const DOM_SID *sid,
899 enum lsa_SidType type)
901 struct cache_entry *centry;
902 fstring uname;
904 centry = centry_start(domain, status);
905 if (!centry)
906 return;
907 centry_put_uint32(centry, type);
908 centry_put_sid(centry, sid);
909 fstrcpy(uname, name);
910 strupper_m(uname);
911 centry_end(centry, "NS/%s/%s", domain_name, uname);
912 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
913 uname, sid_string_dbg(sid), nt_errstr(status)));
914 centry_free(centry);
917 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
918 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
920 struct cache_entry *centry;
921 fstring sid_string;
923 centry = centry_start(domain, status);
924 if (!centry)
925 return;
927 if (NT_STATUS_IS_OK(status)) {
928 centry_put_uint32(centry, type);
929 centry_put_string(centry, domain_name);
930 centry_put_string(centry, name);
933 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
934 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
935 name, nt_errstr(status)));
936 centry_free(centry);
940 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
941 struct wbint_userinfo *info)
943 struct cache_entry *centry;
944 fstring sid_string;
946 if (is_null_sid(&info->user_sid)) {
947 return;
950 centry = centry_start(domain, status);
951 if (!centry)
952 return;
953 centry_put_string(centry, info->acct_name);
954 centry_put_string(centry, info->full_name);
955 centry_put_string(centry, info->homedir);
956 centry_put_string(centry, info->shell);
957 centry_put_uint32(centry, info->primary_gid);
958 centry_put_sid(centry, &info->user_sid);
959 centry_put_sid(centry, &info->group_sid);
960 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
961 &info->user_sid));
962 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
963 centry_free(centry);
966 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
967 NTSTATUS status,
968 struct samr_DomInfo12 *lockout_policy)
970 struct cache_entry *centry;
972 centry = centry_start(domain, status);
973 if (!centry)
974 return;
976 centry_put_nttime(centry, lockout_policy->lockout_duration);
977 centry_put_nttime(centry, lockout_policy->lockout_window);
978 centry_put_uint16(centry, lockout_policy->lockout_threshold);
980 centry_end(centry, "LOC_POL/%s", domain->name);
982 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
984 centry_free(centry);
989 static void wcache_save_password_policy(struct winbindd_domain *domain,
990 NTSTATUS status,
991 struct samr_DomInfo1 *policy)
993 struct cache_entry *centry;
995 centry = centry_start(domain, status);
996 if (!centry)
997 return;
999 centry_put_uint16(centry, policy->min_password_length);
1000 centry_put_uint16(centry, policy->password_history_length);
1001 centry_put_uint32(centry, policy->password_properties);
1002 centry_put_nttime(centry, policy->max_password_age);
1003 centry_put_nttime(centry, policy->min_password_age);
1005 centry_end(centry, "PWD_POL/%s", domain->name);
1007 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1009 centry_free(centry);
1012 /***************************************************************************
1013 ***************************************************************************/
1015 static void wcache_save_username_alias(struct winbindd_domain *domain,
1016 NTSTATUS status,
1017 const char *name, const char *alias)
1019 struct cache_entry *centry;
1020 fstring uname;
1022 if ( (centry = centry_start(domain, status)) == NULL )
1023 return;
1025 centry_put_string( centry, alias );
1027 fstrcpy(uname, name);
1028 strupper_m(uname);
1029 centry_end(centry, "NSS/NA/%s", uname);
1031 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1033 centry_free(centry);
1036 static void wcache_save_alias_username(struct winbindd_domain *domain,
1037 NTSTATUS status,
1038 const char *alias, const char *name)
1040 struct cache_entry *centry;
1041 fstring uname;
1043 if ( (centry = centry_start(domain, status)) == NULL )
1044 return;
1046 centry_put_string( centry, name );
1048 fstrcpy(uname, alias);
1049 strupper_m(uname);
1050 centry_end(centry, "NSS/AN/%s", uname);
1052 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1054 centry_free(centry);
1057 /***************************************************************************
1058 ***************************************************************************/
1060 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1061 struct winbindd_domain *domain,
1062 const char *name, char **alias )
1064 struct winbind_cache *cache = get_cache(domain);
1065 struct cache_entry *centry = NULL;
1066 NTSTATUS status;
1067 char *upper_name;
1069 if ( domain->internal )
1070 return NT_STATUS_NOT_SUPPORTED;
1072 if (!cache->tdb)
1073 goto do_query;
1075 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1076 return NT_STATUS_NO_MEMORY;
1077 strupper_m(upper_name);
1079 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1081 SAFE_FREE( upper_name );
1083 if (!centry)
1084 goto do_query;
1086 status = centry->status;
1088 if (!NT_STATUS_IS_OK(status)) {
1089 centry_free(centry);
1090 return status;
1093 *alias = centry_string( centry, mem_ctx );
1095 centry_free(centry);
1097 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1098 name, *alias ? *alias : "(none)"));
1100 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1102 do_query:
1104 /* If its not in cache and we are offline, then fail */
1106 if ( get_global_winbindd_state_offline() || !domain->online ) {
1107 DEBUG(8,("resolve_username_to_alias: rejecting query "
1108 "in offline mode\n"));
1109 return NT_STATUS_NOT_FOUND;
1112 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1114 if ( NT_STATUS_IS_OK( status ) ) {
1115 wcache_save_username_alias(domain, status, name, *alias);
1118 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1119 wcache_save_username_alias(domain, status, name, "(NULL)");
1122 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1123 nt_errstr(status)));
1125 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1126 set_domain_offline( domain );
1129 return status;
1132 /***************************************************************************
1133 ***************************************************************************/
1135 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1136 struct winbindd_domain *domain,
1137 const char *alias, char **name )
1139 struct winbind_cache *cache = get_cache(domain);
1140 struct cache_entry *centry = NULL;
1141 NTSTATUS status;
1142 char *upper_name;
1144 if ( domain->internal )
1145 return NT_STATUS_NOT_SUPPORTED;
1147 if (!cache->tdb)
1148 goto do_query;
1150 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1151 return NT_STATUS_NO_MEMORY;
1152 strupper_m(upper_name);
1154 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1156 SAFE_FREE( upper_name );
1158 if (!centry)
1159 goto do_query;
1161 status = centry->status;
1163 if (!NT_STATUS_IS_OK(status)) {
1164 centry_free(centry);
1165 return status;
1168 *name = centry_string( centry, mem_ctx );
1170 centry_free(centry);
1172 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1173 alias, *name ? *name : "(none)"));
1175 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1177 do_query:
1179 /* If its not in cache and we are offline, then fail */
1181 if ( get_global_winbindd_state_offline() || !domain->online ) {
1182 DEBUG(8,("resolve_alias_to_username: rejecting query "
1183 "in offline mode\n"));
1184 return NT_STATUS_NOT_FOUND;
1187 /* an alias cannot contain a domain prefix or '@' */
1189 if (strchr(alias, '\\') || strchr(alias, '@')) {
1190 DEBUG(10,("resolve_alias_to_username: skipping fully "
1191 "qualified name %s\n", alias));
1192 return NT_STATUS_OBJECT_NAME_INVALID;
1195 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1197 if ( NT_STATUS_IS_OK( status ) ) {
1198 wcache_save_alias_username( domain, status, alias, *name );
1201 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1202 wcache_save_alias_username(domain, status, alias, "(NULL)");
1205 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1206 nt_errstr(status)));
1208 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1209 set_domain_offline( domain );
1212 return status;
1215 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1217 struct winbind_cache *cache = get_cache(domain);
1218 TDB_DATA data;
1219 fstring key_str, tmp;
1220 uint32 rid;
1222 if (!cache->tdb) {
1223 return NT_STATUS_INTERNAL_DB_ERROR;
1226 if (is_null_sid(sid)) {
1227 return NT_STATUS_INVALID_SID;
1230 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1231 return NT_STATUS_INVALID_SID;
1234 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1236 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1237 if (!data.dptr) {
1238 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1241 SAFE_FREE(data.dptr);
1242 return NT_STATUS_OK;
1245 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1246 as new salted ones. */
1248 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1249 TALLOC_CTX *mem_ctx,
1250 const DOM_SID *sid,
1251 const uint8 **cached_nt_pass,
1252 const uint8 **cached_salt)
1254 struct winbind_cache *cache = get_cache(domain);
1255 struct cache_entry *centry = NULL;
1256 NTSTATUS status;
1257 time_t t;
1258 uint32 rid;
1259 fstring tmp;
1261 if (!cache->tdb) {
1262 return NT_STATUS_INTERNAL_DB_ERROR;
1265 if (is_null_sid(sid)) {
1266 return NT_STATUS_INVALID_SID;
1269 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1270 return NT_STATUS_INVALID_SID;
1273 /* Try and get a salted cred first. If we can't
1274 fall back to an unsalted cred. */
1276 centry = wcache_fetch(cache, domain, "CRED/%s",
1277 sid_to_fstring(tmp, sid));
1278 if (!centry) {
1279 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1280 sid_string_dbg(sid)));
1281 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1284 t = centry_time(centry);
1286 /* In the salted case this isn't actually the nt_hash itself,
1287 but the MD5 of the salt + nt_hash. Let the caller
1288 sort this out. It can tell as we only return the cached_salt
1289 if we are returning a salted cred. */
1291 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1292 if (*cached_nt_pass == NULL) {
1293 fstring sidstr;
1295 sid_to_fstring(sidstr, sid);
1297 /* Bad (old) cred cache. Delete and pretend we
1298 don't have it. */
1299 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1300 sidstr));
1301 wcache_delete("CRED/%s", sidstr);
1302 centry_free(centry);
1303 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1306 /* We only have 17 bytes more data in the salted cred case. */
1307 if (centry->len - centry->ofs == 17) {
1308 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1309 } else {
1310 *cached_salt = NULL;
1313 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1314 if (*cached_salt) {
1315 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1318 status = centry->status;
1320 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1321 sid_string_dbg(sid), nt_errstr(status) ));
1323 centry_free(centry);
1324 return status;
1327 /* Store creds for a SID - only writes out new salted ones. */
1329 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1330 TALLOC_CTX *mem_ctx,
1331 const DOM_SID *sid,
1332 const uint8 nt_pass[NT_HASH_LEN])
1334 struct cache_entry *centry;
1335 fstring sid_string;
1336 uint32 rid;
1337 uint8 cred_salt[NT_HASH_LEN];
1338 uint8 salted_hash[NT_HASH_LEN];
1340 if (is_null_sid(sid)) {
1341 return NT_STATUS_INVALID_SID;
1344 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1345 return NT_STATUS_INVALID_SID;
1348 centry = centry_start(domain, NT_STATUS_OK);
1349 if (!centry) {
1350 return NT_STATUS_INTERNAL_DB_ERROR;
1353 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1355 centry_put_time(centry, time(NULL));
1357 /* Create a salt and then salt the hash. */
1358 generate_random_buffer(cred_salt, NT_HASH_LEN);
1359 E_md5hash(cred_salt, nt_pass, salted_hash);
1361 centry_put_hash16(centry, salted_hash);
1362 centry_put_hash16(centry, cred_salt);
1363 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1365 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1367 centry_free(centry);
1369 return NT_STATUS_OK;
1373 /* Query display info. This is the basic user list fn */
1374 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1375 TALLOC_CTX *mem_ctx,
1376 uint32 *num_entries,
1377 struct wbint_userinfo **info)
1379 struct winbind_cache *cache = get_cache(domain);
1380 struct cache_entry *centry = NULL;
1381 NTSTATUS status;
1382 unsigned int i, retry;
1383 bool old_status = domain->online;
1385 if (!cache->tdb)
1386 goto do_query;
1388 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1389 if (!centry)
1390 goto do_query;
1392 do_fetch_cache:
1393 *num_entries = centry_uint32(centry);
1395 if (*num_entries == 0)
1396 goto do_cached;
1398 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1399 if (! (*info)) {
1400 smb_panic_fn("query_user_list out of memory");
1402 for (i=0; i<(*num_entries); i++) {
1403 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1404 (*info)[i].full_name = centry_string(centry, mem_ctx);
1405 (*info)[i].homedir = centry_string(centry, mem_ctx);
1406 (*info)[i].shell = centry_string(centry, mem_ctx);
1407 centry_sid(centry, &(*info)[i].user_sid);
1408 centry_sid(centry, &(*info)[i].group_sid);
1411 do_cached:
1412 status = centry->status;
1414 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1415 domain->name, nt_errstr(status) ));
1417 centry_free(centry);
1418 return status;
1420 do_query:
1421 *num_entries = 0;
1422 *info = NULL;
1424 /* Return status value returned by seq number check */
1426 if (!NT_STATUS_IS_OK(domain->last_status))
1427 return domain->last_status;
1429 /* Put the query_user_list() in a retry loop. There appears to be
1430 * some bug either with Windows 2000 or Samba's handling of large
1431 * rpc replies. This manifests itself as sudden disconnection
1432 * at a random point in the enumeration of a large (60k) user list.
1433 * The retry loop simply tries the operation again. )-: It's not
1434 * pretty but an acceptable workaround until we work out what the
1435 * real problem is. */
1437 retry = 0;
1438 do {
1440 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1441 domain->name ));
1443 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1444 if (!NT_STATUS_IS_OK(status)) {
1445 DEBUG(3, ("query_user_list: returned 0x%08x, "
1446 "retrying\n", NT_STATUS_V(status)));
1448 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1449 DEBUG(3, ("query_user_list: flushing "
1450 "connection cache\n"));
1451 invalidate_cm_connection(&domain->conn);
1453 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1454 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1455 if (!domain->internal && old_status) {
1456 set_domain_offline(domain);
1458 /* store partial response. */
1459 if (*num_entries > 0) {
1461 * humm, what about the status used for cache?
1462 * Should it be NT_STATUS_OK?
1464 break;
1467 * domain is offline now, and there is no user entries,
1468 * try to fetch from cache again.
1470 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1471 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1472 /* partial response... */
1473 if (!centry) {
1474 goto skip_save;
1475 } else {
1476 goto do_fetch_cache;
1478 } else {
1479 goto skip_save;
1483 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1484 (retry++ < 5));
1486 /* and save it */
1487 refresh_sequence_number(domain, false);
1488 if (!NT_STATUS_IS_OK(status)) {
1489 return status;
1491 centry = centry_start(domain, status);
1492 if (!centry)
1493 goto skip_save;
1494 centry_put_uint32(centry, *num_entries);
1495 for (i=0; i<(*num_entries); i++) {
1496 centry_put_string(centry, (*info)[i].acct_name);
1497 centry_put_string(centry, (*info)[i].full_name);
1498 centry_put_string(centry, (*info)[i].homedir);
1499 centry_put_string(centry, (*info)[i].shell);
1500 centry_put_sid(centry, &(*info)[i].user_sid);
1501 centry_put_sid(centry, &(*info)[i].group_sid);
1502 if (domain->backend && domain->backend->consistent) {
1503 /* when the backend is consistent we can pre-prime some mappings */
1504 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1505 domain->name,
1506 (*info)[i].acct_name,
1507 &(*info)[i].user_sid,
1508 SID_NAME_USER);
1509 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1510 &(*info)[i].user_sid,
1511 domain->name,
1512 (*info)[i].acct_name,
1513 SID_NAME_USER);
1514 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1517 centry_end(centry, "UL/%s", domain->name);
1518 centry_free(centry);
1520 skip_save:
1521 return status;
1524 /* list all domain groups */
1525 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1526 TALLOC_CTX *mem_ctx,
1527 uint32 *num_entries,
1528 struct acct_info **info)
1530 struct winbind_cache *cache = get_cache(domain);
1531 struct cache_entry *centry = NULL;
1532 NTSTATUS status;
1533 unsigned int i;
1534 bool old_status;
1536 old_status = domain->online;
1537 if (!cache->tdb)
1538 goto do_query;
1540 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1541 if (!centry)
1542 goto do_query;
1544 do_fetch_cache:
1545 *num_entries = centry_uint32(centry);
1547 if (*num_entries == 0)
1548 goto do_cached;
1550 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1551 if (! (*info)) {
1552 smb_panic_fn("enum_dom_groups out of memory");
1554 for (i=0; i<(*num_entries); i++) {
1555 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1556 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1557 (*info)[i].rid = centry_uint32(centry);
1560 do_cached:
1561 status = centry->status;
1563 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1564 domain->name, nt_errstr(status) ));
1566 centry_free(centry);
1567 return status;
1569 do_query:
1570 *num_entries = 0;
1571 *info = NULL;
1573 /* Return status value returned by seq number check */
1575 if (!NT_STATUS_IS_OK(domain->last_status))
1576 return domain->last_status;
1578 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1579 domain->name ));
1581 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1583 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1584 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1585 if (!domain->internal && old_status) {
1586 set_domain_offline(domain);
1588 if (cache->tdb &&
1589 !domain->online &&
1590 !domain->internal &&
1591 old_status) {
1592 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1593 if (centry) {
1594 goto do_fetch_cache;
1598 /* and save it */
1599 refresh_sequence_number(domain, false);
1600 if (!NT_STATUS_IS_OK(status)) {
1601 return status;
1603 centry = centry_start(domain, status);
1604 if (!centry)
1605 goto skip_save;
1606 centry_put_uint32(centry, *num_entries);
1607 for (i=0; i<(*num_entries); i++) {
1608 centry_put_string(centry, (*info)[i].acct_name);
1609 centry_put_string(centry, (*info)[i].acct_desc);
1610 centry_put_uint32(centry, (*info)[i].rid);
1612 centry_end(centry, "GL/%s/domain", domain->name);
1613 centry_free(centry);
1615 skip_save:
1616 return status;
1619 /* list all domain groups */
1620 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1621 TALLOC_CTX *mem_ctx,
1622 uint32 *num_entries,
1623 struct acct_info **info)
1625 struct winbind_cache *cache = get_cache(domain);
1626 struct cache_entry *centry = NULL;
1627 NTSTATUS status;
1628 unsigned int i;
1629 bool old_status;
1631 old_status = domain->online;
1632 if (!cache->tdb)
1633 goto do_query;
1635 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1636 if (!centry)
1637 goto do_query;
1639 do_fetch_cache:
1640 *num_entries = centry_uint32(centry);
1642 if (*num_entries == 0)
1643 goto do_cached;
1645 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1646 if (! (*info)) {
1647 smb_panic_fn("enum_dom_groups out of memory");
1649 for (i=0; i<(*num_entries); i++) {
1650 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1651 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1652 (*info)[i].rid = centry_uint32(centry);
1655 do_cached:
1657 /* If we are returning cached data and the domain controller
1658 is down then we don't know whether the data is up to date
1659 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1660 indicate this. */
1662 if (wcache_server_down(domain)) {
1663 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1664 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1665 } else
1666 status = centry->status;
1668 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1669 domain->name, nt_errstr(status) ));
1671 centry_free(centry);
1672 return status;
1674 do_query:
1675 *num_entries = 0;
1676 *info = NULL;
1678 /* Return status value returned by seq number check */
1680 if (!NT_STATUS_IS_OK(domain->last_status))
1681 return domain->last_status;
1683 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1684 domain->name ));
1686 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1688 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1689 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1690 if (!domain->internal && old_status) {
1691 set_domain_offline(domain);
1693 if (cache->tdb &&
1694 !domain->internal &&
1695 !domain->online &&
1696 old_status) {
1697 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1698 if (centry) {
1699 goto do_fetch_cache;
1703 /* and save it */
1704 refresh_sequence_number(domain, false);
1705 if (!NT_STATUS_IS_OK(status)) {
1706 return status;
1708 centry = centry_start(domain, status);
1709 if (!centry)
1710 goto skip_save;
1711 centry_put_uint32(centry, *num_entries);
1712 for (i=0; i<(*num_entries); i++) {
1713 centry_put_string(centry, (*info)[i].acct_name);
1714 centry_put_string(centry, (*info)[i].acct_desc);
1715 centry_put_uint32(centry, (*info)[i].rid);
1717 centry_end(centry, "GL/%s/local", domain->name);
1718 centry_free(centry);
1720 skip_save:
1721 return status;
1724 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1725 const char *domain_name,
1726 const char *name,
1727 struct dom_sid *sid,
1728 enum lsa_SidType *type)
1730 struct winbind_cache *cache = get_cache(domain);
1731 struct cache_entry *centry;
1732 NTSTATUS status;
1733 char *uname;
1735 if (cache->tdb == NULL) {
1736 return NT_STATUS_NOT_FOUND;
1739 uname = talloc_strdup_upper(talloc_tos(), name);
1740 if (uname == NULL) {
1741 return NT_STATUS_NO_MEMORY;
1744 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1745 TALLOC_FREE(uname);
1746 if (centry == NULL) {
1747 return NT_STATUS_NOT_FOUND;
1750 status = centry->status;
1751 if (NT_STATUS_IS_OK(status)) {
1752 *type = (enum lsa_SidType)centry_uint32(centry);
1753 centry_sid(centry, sid);
1756 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1757 "%s\n", domain->name, nt_errstr(status) ));
1759 centry_free(centry);
1760 return status;
1763 /* convert a single name to a sid in a domain */
1764 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1765 TALLOC_CTX *mem_ctx,
1766 const char *domain_name,
1767 const char *name,
1768 uint32_t flags,
1769 DOM_SID *sid,
1770 enum lsa_SidType *type)
1772 NTSTATUS status;
1773 bool old_status;
1775 old_status = domain->online;
1777 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1778 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1779 return status;
1782 ZERO_STRUCTP(sid);
1784 /* If the seq number check indicated that there is a problem
1785 * with this DC, then return that status... except for
1786 * access_denied. This is special because the dc may be in
1787 * "restrict anonymous = 1" mode, in which case it will deny
1788 * most unauthenticated operations, but *will* allow the LSA
1789 * name-to-sid that we try as a fallback. */
1791 if (!(NT_STATUS_IS_OK(domain->last_status)
1792 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1793 return domain->last_status;
1795 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1796 domain->name ));
1798 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1799 name, flags, sid, type);
1801 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1802 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1803 if (!domain->internal && old_status) {
1804 set_domain_offline(domain);
1806 if (!domain->internal &&
1807 !domain->online &&
1808 old_status) {
1809 NTSTATUS cache_status;
1810 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1811 return cache_status;
1814 /* and save it */
1815 refresh_sequence_number(domain, false);
1817 if (domain->online &&
1818 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1819 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1821 /* Only save the reverse mapping if this was not a UPN */
1822 if (!strchr(name, '@')) {
1823 strupper_m(CONST_DISCARD(char *,domain_name));
1824 strlower_m(CONST_DISCARD(char *,name));
1825 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1829 return status;
1832 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1833 const struct dom_sid *sid,
1834 TALLOC_CTX *mem_ctx,
1835 char **domain_name,
1836 char **name,
1837 enum lsa_SidType *type)
1839 struct winbind_cache *cache = get_cache(domain);
1840 struct cache_entry *centry;
1841 char *sid_string;
1842 NTSTATUS status;
1844 if (cache->tdb == NULL) {
1845 return NT_STATUS_NOT_FOUND;
1848 sid_string = sid_string_tos(sid);
1849 if (sid_string == NULL) {
1850 return NT_STATUS_NO_MEMORY;
1853 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1854 TALLOC_FREE(sid_string);
1855 if (centry == NULL) {
1856 return NT_STATUS_NOT_FOUND;
1859 if (NT_STATUS_IS_OK(centry->status)) {
1860 *type = (enum lsa_SidType)centry_uint32(centry);
1861 *domain_name = centry_string(centry, mem_ctx);
1862 *name = centry_string(centry, mem_ctx);
1865 status = centry->status;
1866 centry_free(centry);
1868 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1869 "%s\n", domain->name, nt_errstr(status) ));
1871 return status;
1874 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1875 given */
1876 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1877 TALLOC_CTX *mem_ctx,
1878 const DOM_SID *sid,
1879 char **domain_name,
1880 char **name,
1881 enum lsa_SidType *type)
1883 NTSTATUS status;
1884 bool old_status;
1886 old_status = domain->online;
1887 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1888 type);
1889 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1890 return status;
1893 *name = NULL;
1894 *domain_name = NULL;
1896 /* If the seq number check indicated that there is a problem
1897 * with this DC, then return that status... except for
1898 * access_denied. This is special because the dc may be in
1899 * "restrict anonymous = 1" mode, in which case it will deny
1900 * most unauthenticated operations, but *will* allow the LSA
1901 * sid-to-name that we try as a fallback. */
1903 if (!(NT_STATUS_IS_OK(domain->last_status)
1904 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1905 return domain->last_status;
1907 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1908 domain->name ));
1910 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1912 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1913 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1914 if (!domain->internal && old_status) {
1915 set_domain_offline(domain);
1917 if (!domain->internal &&
1918 !domain->online &&
1919 old_status) {
1920 NTSTATUS cache_status;
1921 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1922 domain_name, name, type);
1923 return cache_status;
1926 /* and save it */
1927 refresh_sequence_number(domain, false);
1928 if (!NT_STATUS_IS_OK(status)) {
1929 return status;
1931 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1933 /* We can't save the name to sid mapping here, as with sid history a
1934 * later name2sid would give the wrong sid. */
1936 return status;
1939 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1940 TALLOC_CTX *mem_ctx,
1941 const DOM_SID *domain_sid,
1942 uint32 *rids,
1943 size_t num_rids,
1944 char **domain_name,
1945 char ***names,
1946 enum lsa_SidType **types)
1948 struct winbind_cache *cache = get_cache(domain);
1949 size_t i;
1950 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1951 bool have_mapped;
1952 bool have_unmapped;
1953 bool old_status;
1955 old_status = domain->online;
1956 *domain_name = NULL;
1957 *names = NULL;
1958 *types = NULL;
1960 if (!cache->tdb) {
1961 goto do_query;
1964 if (num_rids == 0) {
1965 return NT_STATUS_OK;
1968 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1969 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1971 if ((*names == NULL) || (*types == NULL)) {
1972 result = NT_STATUS_NO_MEMORY;
1973 goto error;
1976 have_mapped = have_unmapped = false;
1978 for (i=0; i<num_rids; i++) {
1979 DOM_SID sid;
1980 struct cache_entry *centry;
1981 fstring tmp;
1983 if (!sid_compose(&sid, domain_sid, rids[i])) {
1984 result = NT_STATUS_INTERNAL_ERROR;
1985 goto error;
1988 centry = wcache_fetch(cache, domain, "SN/%s",
1989 sid_to_fstring(tmp, &sid));
1990 if (!centry) {
1991 goto do_query;
1994 (*types)[i] = SID_NAME_UNKNOWN;
1995 (*names)[i] = talloc_strdup(*names, "");
1997 if (NT_STATUS_IS_OK(centry->status)) {
1998 char *dom;
1999 have_mapped = true;
2000 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2002 dom = centry_string(centry, mem_ctx);
2003 if (*domain_name == NULL) {
2004 *domain_name = dom;
2005 } else {
2006 talloc_free(dom);
2009 (*names)[i] = centry_string(centry, *names);
2011 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2012 have_unmapped = true;
2014 } else {
2015 /* something's definitely wrong */
2016 result = centry->status;
2017 goto error;
2020 centry_free(centry);
2023 if (!have_mapped) {
2024 return NT_STATUS_NONE_MAPPED;
2026 if (!have_unmapped) {
2027 return NT_STATUS_OK;
2029 return STATUS_SOME_UNMAPPED;
2031 do_query:
2033 TALLOC_FREE(*names);
2034 TALLOC_FREE(*types);
2036 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2037 rids, num_rids, domain_name,
2038 names, types);
2040 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2041 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2042 if (!domain->internal && old_status) {
2043 set_domain_offline(domain);
2045 if (cache->tdb &&
2046 !domain->internal &&
2047 !domain->online &&
2048 old_status) {
2049 have_mapped = have_unmapped = false;
2051 for (i=0; i<num_rids; i++) {
2052 DOM_SID sid;
2053 struct cache_entry *centry;
2054 fstring tmp;
2056 if (!sid_compose(&sid, domain_sid, rids[i])) {
2057 result = NT_STATUS_INTERNAL_ERROR;
2058 goto error;
2061 centry = wcache_fetch(cache, domain, "SN/%s",
2062 sid_to_fstring(tmp, &sid));
2063 if (!centry) {
2064 (*types)[i] = SID_NAME_UNKNOWN;
2065 (*names)[i] = talloc_strdup(*names, "");
2066 continue;
2069 (*types)[i] = SID_NAME_UNKNOWN;
2070 (*names)[i] = talloc_strdup(*names, "");
2072 if (NT_STATUS_IS_OK(centry->status)) {
2073 char *dom;
2074 have_mapped = true;
2075 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2077 dom = centry_string(centry, mem_ctx);
2078 if (*domain_name == NULL) {
2079 *domain_name = dom;
2080 } else {
2081 talloc_free(dom);
2084 (*names)[i] = centry_string(centry, *names);
2086 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2087 have_unmapped = true;
2089 } else {
2090 /* something's definitely wrong */
2091 result = centry->status;
2092 goto error;
2095 centry_free(centry);
2098 if (!have_mapped) {
2099 return NT_STATUS_NONE_MAPPED;
2101 if (!have_unmapped) {
2102 return NT_STATUS_OK;
2104 return STATUS_SOME_UNMAPPED;
2108 None of the queried rids has been found so save all negative entries
2110 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2111 for (i = 0; i < num_rids; i++) {
2112 DOM_SID sid;
2113 const char *name = "";
2114 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2115 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2117 if (!sid_compose(&sid, domain_sid, rids[i])) {
2118 return NT_STATUS_INTERNAL_ERROR;
2121 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2122 name, type);
2125 return result;
2129 Some or all of the queried rids have been found.
2131 if (!NT_STATUS_IS_OK(result) &&
2132 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2133 return result;
2136 refresh_sequence_number(domain, false);
2138 for (i=0; i<num_rids; i++) {
2139 DOM_SID sid;
2140 NTSTATUS status;
2142 if (!sid_compose(&sid, domain_sid, rids[i])) {
2143 result = NT_STATUS_INTERNAL_ERROR;
2144 goto error;
2147 status = (*types)[i] == SID_NAME_UNKNOWN ?
2148 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2150 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2151 (*names)[i], (*types)[i]);
2154 return result;
2156 error:
2157 TALLOC_FREE(*names);
2158 TALLOC_FREE(*types);
2159 return result;
2162 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2163 TALLOC_CTX *mem_ctx,
2164 const struct dom_sid *user_sid,
2165 struct wbint_userinfo *info)
2167 struct winbind_cache *cache = get_cache(domain);
2168 struct cache_entry *centry = NULL;
2169 NTSTATUS status;
2170 char *sid_string;
2172 if (cache->tdb == NULL) {
2173 return NT_STATUS_NOT_FOUND;
2176 sid_string = sid_string_tos(user_sid);
2177 if (sid_string == NULL) {
2178 return NT_STATUS_NO_MEMORY;
2181 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2182 TALLOC_FREE(sid_string);
2183 if (centry == NULL) {
2184 return NT_STATUS_NOT_FOUND;
2188 * If we have an access denied cache entry and a cached info3
2189 * in the samlogon cache then do a query. This will force the
2190 * rpc back end to return the info3 data.
2193 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2194 netsamlogon_cache_have(user_sid)) {
2195 DEBUG(10, ("query_user: cached access denied and have cached "
2196 "info3\n"));
2197 domain->last_status = NT_STATUS_OK;
2198 centry_free(centry);
2199 return NT_STATUS_NOT_FOUND;
2202 /* if status is not ok then this is a negative hit
2203 and the rest of the data doesn't matter */
2204 status = centry->status;
2205 if (NT_STATUS_IS_OK(status)) {
2206 info->acct_name = centry_string(centry, mem_ctx);
2207 info->full_name = centry_string(centry, mem_ctx);
2208 info->homedir = centry_string(centry, mem_ctx);
2209 info->shell = centry_string(centry, mem_ctx);
2210 info->primary_gid = centry_uint32(centry);
2211 centry_sid(centry, &info->user_sid);
2212 centry_sid(centry, &info->group_sid);
2215 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2216 "%s\n", domain->name, nt_errstr(status) ));
2218 centry_free(centry);
2219 return status;
2222 /* Lookup user information from a rid */
2223 static NTSTATUS query_user(struct winbindd_domain *domain,
2224 TALLOC_CTX *mem_ctx,
2225 const DOM_SID *user_sid,
2226 struct wbint_userinfo *info)
2228 NTSTATUS status;
2229 bool old_status;
2231 old_status = domain->online;
2232 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2233 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2234 return status;
2237 ZERO_STRUCTP(info);
2239 /* Return status value returned by seq number check */
2241 if (!NT_STATUS_IS_OK(domain->last_status))
2242 return domain->last_status;
2244 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2245 domain->name ));
2247 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2249 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2250 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2251 if (!domain->internal && old_status) {
2252 set_domain_offline(domain);
2254 if (!domain->internal &&
2255 !domain->online &&
2256 old_status) {
2257 NTSTATUS cache_status;
2258 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2259 return cache_status;
2262 /* and save it */
2263 refresh_sequence_number(domain, false);
2264 if (!NT_STATUS_IS_OK(status)) {
2265 return status;
2267 wcache_save_user(domain, status, info);
2269 return status;
2272 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2273 TALLOC_CTX *mem_ctx,
2274 const struct dom_sid *user_sid,
2275 uint32_t *pnum_sids,
2276 struct dom_sid **psids)
2278 struct winbind_cache *cache = get_cache(domain);
2279 struct cache_entry *centry = NULL;
2280 NTSTATUS status;
2281 uint32_t i, num_sids;
2282 struct dom_sid *sids;
2283 fstring sid_string;
2285 if (cache->tdb == NULL) {
2286 return NT_STATUS_NOT_FOUND;
2289 centry = wcache_fetch(cache, domain, "UG/%s",
2290 sid_to_fstring(sid_string, user_sid));
2291 if (centry == NULL) {
2292 return NT_STATUS_NOT_FOUND;
2295 /* If we have an access denied cache entry and a cached info3 in the
2296 samlogon cache then do a query. This will force the rpc back end
2297 to return the info3 data. */
2299 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2300 && netsamlogon_cache_have(user_sid)) {
2301 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2302 "cached info3\n"));
2303 domain->last_status = NT_STATUS_OK;
2304 centry_free(centry);
2305 return NT_STATUS_NOT_FOUND;
2308 num_sids = centry_uint32(centry);
2309 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2310 if (sids == NULL) {
2311 centry_free(centry);
2312 return NT_STATUS_NO_MEMORY;
2315 for (i=0; i<num_sids; i++) {
2316 centry_sid(centry, &sids[i]);
2319 status = centry->status;
2321 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2322 "status: %s\n", domain->name, nt_errstr(status)));
2324 centry_free(centry);
2326 *pnum_sids = num_sids;
2327 *psids = sids;
2328 return status;
2331 /* Lookup groups a user is a member of. */
2332 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2333 TALLOC_CTX *mem_ctx,
2334 const DOM_SID *user_sid,
2335 uint32 *num_groups, DOM_SID **user_gids)
2337 struct cache_entry *centry = NULL;
2338 NTSTATUS status;
2339 unsigned int i;
2340 fstring sid_string;
2341 bool old_status;
2343 old_status = domain->online;
2344 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2345 num_groups, user_gids);
2346 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2347 return status;
2350 (*num_groups) = 0;
2351 (*user_gids) = NULL;
2353 /* Return status value returned by seq number check */
2355 if (!NT_STATUS_IS_OK(domain->last_status))
2356 return domain->last_status;
2358 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2359 domain->name ));
2361 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2363 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2364 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2365 if (!domain->internal && old_status) {
2366 set_domain_offline(domain);
2368 if (!domain->internal &&
2369 !domain->online &&
2370 old_status) {
2371 NTSTATUS cache_status;
2372 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2373 num_groups, user_gids);
2374 return cache_status;
2377 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2378 goto skip_save;
2380 /* and save it */
2381 refresh_sequence_number(domain, false);
2382 if (!NT_STATUS_IS_OK(status)) {
2383 return status;
2385 centry = centry_start(domain, status);
2386 if (!centry)
2387 goto skip_save;
2389 centry_put_uint32(centry, *num_groups);
2390 for (i=0; i<(*num_groups); i++) {
2391 centry_put_sid(centry, &(*user_gids)[i]);
2394 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2395 centry_free(centry);
2397 skip_save:
2398 return status;
2401 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2402 const struct dom_sid *sids)
2404 uint32_t i;
2405 char *sidlist;
2407 sidlist = talloc_strdup(mem_ctx, "");
2408 if (sidlist == NULL) {
2409 return NULL;
2411 for (i=0; i<num_sids; i++) {
2412 fstring tmp;
2413 sidlist = talloc_asprintf_append_buffer(
2414 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2415 if (sidlist == NULL) {
2416 return NULL;
2419 return sidlist;
2422 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2423 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2424 const struct dom_sid *sids,
2425 uint32_t *pnum_aliases, uint32_t **paliases)
2427 struct winbind_cache *cache = get_cache(domain);
2428 struct cache_entry *centry = NULL;
2429 uint32_t num_aliases;
2430 uint32_t *aliases;
2431 NTSTATUS status;
2432 char *sidlist;
2433 int i;
2435 if (cache->tdb == NULL) {
2436 return NT_STATUS_NOT_FOUND;
2439 if (num_sids == 0) {
2440 *pnum_aliases = 0;
2441 *paliases = NULL;
2442 return NT_STATUS_OK;
2445 /* We need to cache indexed by the whole list of SIDs, the aliases
2446 * resulting might come from any of the SIDs. */
2448 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2449 if (sidlist == NULL) {
2450 return NT_STATUS_NO_MEMORY;
2453 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2454 TALLOC_FREE(sidlist);
2455 if (centry == NULL) {
2456 return NT_STATUS_NOT_FOUND;
2459 num_aliases = centry_uint32(centry);
2460 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2461 if (aliases == NULL) {
2462 centry_free(centry);
2463 return NT_STATUS_NO_MEMORY;
2466 for (i=0; i<num_aliases; i++) {
2467 aliases[i] = centry_uint32(centry);
2470 status = centry->status;
2472 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2473 "status %s\n", domain->name, nt_errstr(status)));
2475 centry_free(centry);
2477 *pnum_aliases = num_aliases;
2478 *paliases = aliases;
2480 return status;
2483 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2484 TALLOC_CTX *mem_ctx,
2485 uint32 num_sids, const DOM_SID *sids,
2486 uint32 *num_aliases, uint32 **alias_rids)
2488 struct cache_entry *centry = NULL;
2489 NTSTATUS status;
2490 char *sidlist;
2491 int i;
2492 bool old_status;
2494 old_status = domain->online;
2495 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2496 num_aliases, alias_rids);
2497 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2498 return status;
2501 (*num_aliases) = 0;
2502 (*alias_rids) = NULL;
2504 if (!NT_STATUS_IS_OK(domain->last_status))
2505 return domain->last_status;
2507 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2508 "for domain %s\n", domain->name ));
2510 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2511 if (sidlist == NULL) {
2512 return NT_STATUS_NO_MEMORY;
2515 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2516 num_sids, sids,
2517 num_aliases, alias_rids);
2519 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2520 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2521 if (!domain->internal && old_status) {
2522 set_domain_offline(domain);
2524 if (!domain->internal &&
2525 !domain->online &&
2526 old_status) {
2527 NTSTATUS cache_status;
2528 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2529 sids, num_aliases, alias_rids);
2530 return cache_status;
2533 /* and save it */
2534 refresh_sequence_number(domain, false);
2535 if (!NT_STATUS_IS_OK(status)) {
2536 return status;
2538 centry = centry_start(domain, status);
2539 if (!centry)
2540 goto skip_save;
2541 centry_put_uint32(centry, *num_aliases);
2542 for (i=0; i<(*num_aliases); i++)
2543 centry_put_uint32(centry, (*alias_rids)[i]);
2544 centry_end(centry, "UA%s", sidlist);
2545 centry_free(centry);
2547 skip_save:
2548 return status;
2551 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2552 TALLOC_CTX *mem_ctx,
2553 const struct dom_sid *group_sid,
2554 uint32_t *num_names,
2555 struct dom_sid **sid_mem, char ***names,
2556 uint32_t **name_types)
2558 struct winbind_cache *cache = get_cache(domain);
2559 struct cache_entry *centry = NULL;
2560 NTSTATUS status;
2561 unsigned int i;
2562 char *sid_string;
2564 if (cache->tdb == NULL) {
2565 return NT_STATUS_NOT_FOUND;
2568 sid_string = sid_string_tos(group_sid);
2569 if (sid_string == NULL) {
2570 return NT_STATUS_NO_MEMORY;
2573 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2574 TALLOC_FREE(sid_string);
2575 if (centry == NULL) {
2576 return NT_STATUS_NOT_FOUND;
2579 *sid_mem = NULL;
2580 *names = NULL;
2581 *name_types = NULL;
2583 *num_names = centry_uint32(centry);
2584 if (*num_names == 0) {
2585 centry_free(centry);
2586 return NT_STATUS_OK;
2589 *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2590 *names = talloc_array(mem_ctx, char *, *num_names);
2591 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2593 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2594 TALLOC_FREE(*sid_mem);
2595 TALLOC_FREE(*names);
2596 TALLOC_FREE(*name_types);
2597 centry_free(centry);
2598 return NT_STATUS_NO_MEMORY;
2601 for (i=0; i<(*num_names); i++) {
2602 centry_sid(centry, &(*sid_mem)[i]);
2603 (*names)[i] = centry_string(centry, mem_ctx);
2604 (*name_types)[i] = centry_uint32(centry);
2607 status = centry->status;
2609 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2610 "status: %s\n", domain->name, nt_errstr(status)));
2612 centry_free(centry);
2613 return status;
2616 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2617 TALLOC_CTX *mem_ctx,
2618 const DOM_SID *group_sid,
2619 enum lsa_SidType type,
2620 uint32 *num_names,
2621 DOM_SID **sid_mem, char ***names,
2622 uint32 **name_types)
2624 struct cache_entry *centry = NULL;
2625 NTSTATUS status;
2626 unsigned int i;
2627 fstring sid_string;
2628 bool old_status;
2630 old_status = domain->online;
2631 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2632 sid_mem, names, name_types);
2633 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2634 return status;
2637 (*num_names) = 0;
2638 (*sid_mem) = NULL;
2639 (*names) = NULL;
2640 (*name_types) = NULL;
2642 /* Return status value returned by seq number check */
2644 if (!NT_STATUS_IS_OK(domain->last_status))
2645 return domain->last_status;
2647 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2648 domain->name ));
2650 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2651 type, num_names,
2652 sid_mem, names, name_types);
2654 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2655 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2656 if (!domain->internal && old_status) {
2657 set_domain_offline(domain);
2659 if (!domain->internal &&
2660 !domain->online &&
2661 old_status) {
2662 NTSTATUS cache_status;
2663 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2664 num_names, sid_mem, names,
2665 name_types);
2666 return cache_status;
2669 /* and save it */
2670 refresh_sequence_number(domain, false);
2671 if (!NT_STATUS_IS_OK(status)) {
2672 return status;
2674 centry = centry_start(domain, status);
2675 if (!centry)
2676 goto skip_save;
2677 centry_put_uint32(centry, *num_names);
2678 for (i=0; i<(*num_names); i++) {
2679 centry_put_sid(centry, &(*sid_mem)[i]);
2680 centry_put_string(centry, (*names)[i]);
2681 centry_put_uint32(centry, (*name_types)[i]);
2683 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2684 centry_free(centry);
2686 skip_save:
2687 return status;
2690 /* find the sequence number for a domain */
2691 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2693 refresh_sequence_number(domain, false);
2695 *seq = domain->sequence_number;
2697 return NT_STATUS_OK;
2700 /* enumerate trusted domains
2701 * (we need to have the list of trustdoms in the cache when we go offline) -
2702 * Guenther */
2703 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2704 TALLOC_CTX *mem_ctx,
2705 struct netr_DomainTrustList *trusts)
2707 NTSTATUS status;
2708 struct winbind_cache *cache;
2709 struct winbindd_tdc_domain *dom_list = NULL;
2710 size_t num_domains = 0;
2711 bool retval = false;
2712 int i;
2713 bool old_status;
2715 old_status = domain->online;
2716 trusts->count = 0;
2717 trusts->array = NULL;
2719 cache = get_cache(domain);
2720 if (!cache || !cache->tdb) {
2721 goto do_query;
2724 if (domain->online) {
2725 goto do_query;
2728 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2729 if (!retval || !num_domains || !dom_list) {
2730 TALLOC_FREE(dom_list);
2731 goto do_query;
2734 do_fetch_cache:
2735 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2736 if (!trusts->array) {
2737 TALLOC_FREE(dom_list);
2738 return NT_STATUS_NO_MEMORY;
2741 for (i = 0; i < num_domains; i++) {
2742 struct netr_DomainTrust *trust;
2743 struct dom_sid *sid;
2744 struct winbindd_domain *dom;
2746 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2747 if (dom && dom->internal) {
2748 continue;
2751 trust = &trusts->array[trusts->count];
2752 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2753 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2754 sid = talloc(trusts->array, struct dom_sid);
2755 if (!trust->netbios_name || !trust->dns_name ||
2756 !sid) {
2757 TALLOC_FREE(dom_list);
2758 TALLOC_FREE(trusts->array);
2759 return NT_STATUS_NO_MEMORY;
2762 trust->trust_flags = dom_list[i].trust_flags;
2763 trust->trust_attributes = dom_list[i].trust_attribs;
2764 trust->trust_type = dom_list[i].trust_type;
2765 sid_copy(sid, &dom_list[i].sid);
2766 trust->sid = sid;
2767 trusts->count++;
2770 TALLOC_FREE(dom_list);
2771 return NT_STATUS_OK;
2773 do_query:
2774 /* Return status value returned by seq number check */
2776 if (!NT_STATUS_IS_OK(domain->last_status))
2777 return domain->last_status;
2779 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2780 domain->name ));
2782 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2784 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2785 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2786 if (!domain->internal && old_status) {
2787 set_domain_offline(domain);
2789 if (!domain->internal &&
2790 !domain->online &&
2791 old_status) {
2792 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2793 if (retval && num_domains && dom_list) {
2794 TALLOC_FREE(trusts->array);
2795 trusts->count = 0;
2796 goto do_fetch_cache;
2800 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2801 * so that the generic centry handling still applies correctly -
2802 * Guenther*/
2804 if (!NT_STATUS_IS_ERR(status)) {
2805 status = NT_STATUS_OK;
2807 return status;
2810 /* get lockout policy */
2811 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2812 TALLOC_CTX *mem_ctx,
2813 struct samr_DomInfo12 *policy)
2815 struct winbind_cache *cache = get_cache(domain);
2816 struct cache_entry *centry = NULL;
2817 NTSTATUS status;
2818 bool old_status;
2820 old_status = domain->online;
2821 if (!cache->tdb)
2822 goto do_query;
2824 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2826 if (!centry)
2827 goto do_query;
2829 do_fetch_cache:
2830 policy->lockout_duration = centry_nttime(centry);
2831 policy->lockout_window = centry_nttime(centry);
2832 policy->lockout_threshold = centry_uint16(centry);
2834 status = centry->status;
2836 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2837 domain->name, nt_errstr(status) ));
2839 centry_free(centry);
2840 return status;
2842 do_query:
2843 ZERO_STRUCTP(policy);
2845 /* Return status value returned by seq number check */
2847 if (!NT_STATUS_IS_OK(domain->last_status))
2848 return domain->last_status;
2850 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2851 domain->name ));
2853 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2855 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2856 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2857 if (!domain->internal && old_status) {
2858 set_domain_offline(domain);
2860 if (cache->tdb &&
2861 !domain->internal &&
2862 !domain->online &&
2863 old_status) {
2864 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2865 if (centry) {
2866 goto do_fetch_cache;
2870 /* and save it */
2871 refresh_sequence_number(domain, false);
2872 if (!NT_STATUS_IS_OK(status)) {
2873 return status;
2875 wcache_save_lockout_policy(domain, status, policy);
2877 return status;
2880 /* get password policy */
2881 static NTSTATUS password_policy(struct winbindd_domain *domain,
2882 TALLOC_CTX *mem_ctx,
2883 struct samr_DomInfo1 *policy)
2885 struct winbind_cache *cache = get_cache(domain);
2886 struct cache_entry *centry = NULL;
2887 NTSTATUS status;
2888 bool old_status;
2890 old_status = domain->online;
2891 if (!cache->tdb)
2892 goto do_query;
2894 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2896 if (!centry)
2897 goto do_query;
2899 do_fetch_cache:
2900 policy->min_password_length = centry_uint16(centry);
2901 policy->password_history_length = centry_uint16(centry);
2902 policy->password_properties = centry_uint32(centry);
2903 policy->max_password_age = centry_nttime(centry);
2904 policy->min_password_age = centry_nttime(centry);
2906 status = centry->status;
2908 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2909 domain->name, nt_errstr(status) ));
2911 centry_free(centry);
2912 return status;
2914 do_query:
2915 ZERO_STRUCTP(policy);
2917 /* Return status value returned by seq number check */
2919 if (!NT_STATUS_IS_OK(domain->last_status))
2920 return domain->last_status;
2922 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2923 domain->name ));
2925 status = domain->backend->password_policy(domain, mem_ctx, policy);
2927 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2928 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2929 if (!domain->internal && old_status) {
2930 set_domain_offline(domain);
2932 if (cache->tdb &&
2933 !domain->internal &&
2934 !domain->online &&
2935 old_status) {
2936 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2937 if (centry) {
2938 goto do_fetch_cache;
2942 /* and save it */
2943 refresh_sequence_number(domain, false);
2944 if (!NT_STATUS_IS_OK(status)) {
2945 return status;
2947 wcache_save_password_policy(domain, status, policy);
2949 return status;
2953 /* Invalidate cached user and group lists coherently */
2955 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2956 void *state)
2958 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2959 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2960 tdb_delete(the_tdb, kbuf);
2962 return 0;
2965 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2967 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2968 struct netr_SamInfo3 *info3)
2970 DOM_SID sid;
2971 fstring key_str, sid_string;
2972 struct winbind_cache *cache;
2974 /* dont clear cached U/SID and UG/SID entries when we want to logon
2975 * offline - gd */
2977 if (lp_winbind_offline_logon()) {
2978 return;
2981 if (!domain)
2982 return;
2984 cache = get_cache(domain);
2986 if (!cache->tdb) {
2987 return;
2990 sid_compose(&sid, info3->base.domain_sid, info3->base.rid);
2992 /* Clear U/SID cache entry */
2993 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2994 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2995 tdb_delete(cache->tdb, string_tdb_data(key_str));
2997 /* Clear UG/SID cache entry */
2998 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2999 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3000 tdb_delete(cache->tdb, string_tdb_data(key_str));
3002 /* Samba/winbindd never needs this. */
3003 netsamlogon_clear_cached_user(info3);
3006 bool wcache_invalidate_cache(void)
3008 struct winbindd_domain *domain;
3010 for (domain = domain_list(); domain; domain = domain->next) {
3011 struct winbind_cache *cache = get_cache(domain);
3013 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3014 "entries for %s\n", domain->name));
3015 if (cache) {
3016 if (cache->tdb) {
3017 tdb_traverse(cache->tdb, traverse_fn, NULL);
3018 } else {
3019 return false;
3023 return true;
3026 bool wcache_invalidate_cache_noinit(void)
3028 struct winbindd_domain *domain;
3030 for (domain = domain_list(); domain; domain = domain->next) {
3031 struct winbind_cache *cache;
3033 /* Skip uninitialized domains. */
3034 if (!domain->initialized && !domain->internal) {
3035 continue;
3038 cache = get_cache(domain);
3040 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3041 "entries for %s\n", domain->name));
3042 if (cache) {
3043 if (cache->tdb) {
3044 tdb_traverse(cache->tdb, traverse_fn, NULL);
3046 * Flushing cache has nothing to with domains.
3047 * return here if we successfully flushed once.
3048 * To avoid unnecessary traversing the cache.
3050 return true;
3051 } else {
3052 return false;
3056 return true;
3059 bool init_wcache(void)
3061 if (wcache == NULL) {
3062 wcache = SMB_XMALLOC_P(struct winbind_cache);
3063 ZERO_STRUCTP(wcache);
3066 if (wcache->tdb != NULL)
3067 return true;
3069 /* when working offline we must not clear the cache on restart */
3070 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3071 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3072 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3073 O_RDWR|O_CREAT, 0600);
3075 if (wcache->tdb == NULL) {
3076 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3077 return false;
3080 return true;
3083 /************************************************************************
3084 This is called by the parent to initialize the cache file.
3085 We don't need sophisticated locking here as we know we're the
3086 only opener.
3087 ************************************************************************/
3089 bool initialize_winbindd_cache(void)
3091 bool cache_bad = true;
3092 uint32 vers;
3094 if (!init_wcache()) {
3095 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3096 return false;
3099 /* Check version number. */
3100 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3101 vers == WINBINDD_CACHE_VERSION) {
3102 cache_bad = false;
3105 if (cache_bad) {
3106 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3107 "and re-creating with version number %d\n",
3108 WINBINDD_CACHE_VERSION ));
3110 tdb_close(wcache->tdb);
3111 wcache->tdb = NULL;
3113 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3114 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3115 cache_path("winbindd_cache.tdb"),
3116 strerror(errno) ));
3117 return false;
3119 if (!init_wcache()) {
3120 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3121 "init_wcache failed.\n"));
3122 return false;
3125 /* Write the version. */
3126 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3127 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3128 tdb_errorstr(wcache->tdb) ));
3129 return false;
3133 tdb_close(wcache->tdb);
3134 wcache->tdb = NULL;
3135 return true;
3138 void close_winbindd_cache(void)
3140 if (!wcache) {
3141 return;
3143 if (wcache->tdb) {
3144 tdb_close(wcache->tdb);
3145 wcache->tdb = NULL;
3149 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
3150 char **domain_name, char **name,
3151 enum lsa_SidType *type)
3153 struct winbindd_domain *domain;
3154 NTSTATUS status;
3156 domain = find_lookup_domain_from_sid(sid);
3157 if (domain == NULL) {
3158 return false;
3160 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3161 type);
3162 return NT_STATUS_IS_OK(status);
3165 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
3166 const char *domain_name,
3167 const char *name,
3168 DOM_SID *sid,
3169 enum lsa_SidType *type)
3171 struct winbindd_domain *domain;
3172 NTSTATUS status;
3173 bool original_online_state;
3175 domain = find_lookup_domain_from_name(domain_name);
3176 if (domain == NULL) {
3177 return false;
3180 /* If we are doing a cached logon, temporarily set the domain
3181 offline so the cache won't expire the entry */
3183 original_online_state = domain->online;
3184 domain->online = false;
3185 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3186 domain->online = original_online_state;
3188 return NT_STATUS_IS_OK(status);
3191 void cache_name2sid(struct winbindd_domain *domain,
3192 const char *domain_name, const char *name,
3193 enum lsa_SidType type, const DOM_SID *sid)
3195 refresh_sequence_number(domain, false);
3196 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3197 sid, type);
3201 * The original idea that this cache only contains centries has
3202 * been blurred - now other stuff gets put in here. Ensure we
3203 * ignore these things on cleanup.
3206 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3207 TDB_DATA dbuf, void *state)
3209 struct cache_entry *centry;
3211 if (is_non_centry_key(kbuf)) {
3212 return 0;
3215 centry = wcache_fetch_raw((char *)kbuf.dptr);
3216 if (!centry) {
3217 return 0;
3220 if (!NT_STATUS_IS_OK(centry->status)) {
3221 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3222 tdb_delete(the_tdb, kbuf);
3225 centry_free(centry);
3226 return 0;
3229 /* flush the cache */
3230 void wcache_flush_cache(void)
3232 if (!wcache)
3233 return;
3234 if (wcache->tdb) {
3235 tdb_close(wcache->tdb);
3236 wcache->tdb = NULL;
3238 if (!winbindd_use_cache()) {
3239 return;
3242 /* when working offline we must not clear the cache on restart */
3243 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3244 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3245 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3246 O_RDWR|O_CREAT, 0600);
3248 if (!wcache->tdb) {
3249 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3250 return;
3253 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3255 DEBUG(10,("wcache_flush_cache success\n"));
3258 /* Count cached creds */
3260 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3261 void *state)
3263 int *cred_count = (int*)state;
3265 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3266 (*cred_count)++;
3268 return 0;
3271 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3273 struct winbind_cache *cache = get_cache(domain);
3275 *count = 0;
3277 if (!cache->tdb) {
3278 return NT_STATUS_INTERNAL_DB_ERROR;
3281 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3283 return NT_STATUS_OK;
3286 struct cred_list {
3287 struct cred_list *prev, *next;
3288 TDB_DATA key;
3289 fstring name;
3290 time_t created;
3292 static struct cred_list *wcache_cred_list;
3294 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3295 void *state)
3297 struct cred_list *cred;
3299 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3301 cred = SMB_MALLOC_P(struct cred_list);
3302 if (cred == NULL) {
3303 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3304 return -1;
3307 ZERO_STRUCTP(cred);
3309 /* save a copy of the key */
3311 fstrcpy(cred->name, (const char *)kbuf.dptr);
3312 DLIST_ADD(wcache_cred_list, cred);
3315 return 0;
3318 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
3320 struct winbind_cache *cache = get_cache(domain);
3321 NTSTATUS status;
3322 int ret;
3323 struct cred_list *cred, *oldest = NULL;
3325 if (!cache->tdb) {
3326 return NT_STATUS_INTERNAL_DB_ERROR;
3329 /* we possibly already have an entry */
3330 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3332 fstring key_str, tmp;
3334 DEBUG(11,("we already have an entry, deleting that\n"));
3336 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3338 tdb_delete(cache->tdb, string_tdb_data(key_str));
3340 return NT_STATUS_OK;
3343 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3344 if (ret == 0) {
3345 return NT_STATUS_OK;
3346 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3347 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3350 ZERO_STRUCTP(oldest);
3352 for (cred = wcache_cred_list; cred; cred = cred->next) {
3354 TDB_DATA data;
3355 time_t t;
3357 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3358 if (!data.dptr) {
3359 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3360 cred->name));
3361 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3362 goto done;
3365 t = IVAL(data.dptr, 0);
3366 SAFE_FREE(data.dptr);
3368 if (!oldest) {
3369 oldest = SMB_MALLOC_P(struct cred_list);
3370 if (oldest == NULL) {
3371 status = NT_STATUS_NO_MEMORY;
3372 goto done;
3375 fstrcpy(oldest->name, cred->name);
3376 oldest->created = t;
3377 continue;
3380 if (t < oldest->created) {
3381 fstrcpy(oldest->name, cred->name);
3382 oldest->created = t;
3386 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3387 status = NT_STATUS_OK;
3388 } else {
3389 status = NT_STATUS_UNSUCCESSFUL;
3391 done:
3392 SAFE_FREE(wcache_cred_list);
3393 SAFE_FREE(oldest);
3395 return status;
3398 /* Change the global online/offline state. */
3399 bool set_global_winbindd_state_offline(void)
3401 TDB_DATA data;
3403 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3405 /* Only go offline if someone has created
3406 the key "WINBINDD_OFFLINE" in the cache tdb. */
3408 if (wcache == NULL || wcache->tdb == NULL) {
3409 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3410 return false;
3413 if (!lp_winbind_offline_logon()) {
3414 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3415 return false;
3418 if (global_winbindd_offline_state) {
3419 /* Already offline. */
3420 return true;
3423 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3425 if (!data.dptr || data.dsize != 4) {
3426 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3427 SAFE_FREE(data.dptr);
3428 return false;
3429 } else {
3430 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3431 global_winbindd_offline_state = true;
3432 SAFE_FREE(data.dptr);
3433 return true;
3437 void set_global_winbindd_state_online(void)
3439 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3441 if (!lp_winbind_offline_logon()) {
3442 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3443 return;
3446 if (!global_winbindd_offline_state) {
3447 /* Already online. */
3448 return;
3450 global_winbindd_offline_state = false;
3452 if (!wcache->tdb) {
3453 return;
3456 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3457 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3460 bool get_global_winbindd_state_offline(void)
3462 return global_winbindd_offline_state;
3465 /***********************************************************************
3466 Validate functions for all possible cache tdb keys.
3467 ***********************************************************************/
3469 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3470 struct tdb_validation_status *state)
3472 struct cache_entry *centry;
3474 centry = SMB_XMALLOC_P(struct cache_entry);
3475 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3476 if (!centry->data) {
3477 SAFE_FREE(centry);
3478 return NULL;
3480 centry->len = data.dsize;
3481 centry->ofs = 0;
3483 if (centry->len < 8) {
3484 /* huh? corrupt cache? */
3485 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3486 centry_free(centry);
3487 state->bad_entry = true;
3488 state->success = false;
3489 return NULL;
3492 centry->status = NT_STATUS(centry_uint32(centry));
3493 centry->sequence_number = centry_uint32(centry);
3494 return centry;
3497 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3498 struct tdb_validation_status *state)
3500 if (dbuf.dsize != 8) {
3501 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3502 keystr, (unsigned int)dbuf.dsize ));
3503 state->bad_entry = true;
3504 return 1;
3506 return 0;
3509 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3510 struct tdb_validation_status *state)
3512 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3513 if (!centry) {
3514 return 1;
3517 (void)centry_uint32(centry);
3518 if (NT_STATUS_IS_OK(centry->status)) {
3519 DOM_SID sid;
3520 (void)centry_sid(centry, &sid);
3523 centry_free(centry);
3525 if (!(state->success)) {
3526 return 1;
3528 DEBUG(10,("validate_ns: %s ok\n", keystr));
3529 return 0;
3532 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3533 struct tdb_validation_status *state)
3535 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3536 if (!centry) {
3537 return 1;
3540 if (NT_STATUS_IS_OK(centry->status)) {
3541 (void)centry_uint32(centry);
3542 (void)centry_string(centry, mem_ctx);
3543 (void)centry_string(centry, mem_ctx);
3546 centry_free(centry);
3548 if (!(state->success)) {
3549 return 1;
3551 DEBUG(10,("validate_sn: %s ok\n", keystr));
3552 return 0;
3555 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3556 struct tdb_validation_status *state)
3558 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3559 DOM_SID sid;
3561 if (!centry) {
3562 return 1;
3565 (void)centry_string(centry, mem_ctx);
3566 (void)centry_string(centry, mem_ctx);
3567 (void)centry_string(centry, mem_ctx);
3568 (void)centry_string(centry, mem_ctx);
3569 (void)centry_uint32(centry);
3570 (void)centry_sid(centry, &sid);
3571 (void)centry_sid(centry, &sid);
3573 centry_free(centry);
3575 if (!(state->success)) {
3576 return 1;
3578 DEBUG(10,("validate_u: %s ok\n", keystr));
3579 return 0;
3582 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3583 struct tdb_validation_status *state)
3585 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3587 if (!centry) {
3588 return 1;
3591 (void)centry_nttime(centry);
3592 (void)centry_nttime(centry);
3593 (void)centry_uint16(centry);
3595 centry_free(centry);
3597 if (!(state->success)) {
3598 return 1;
3600 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3601 return 0;
3604 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3605 struct tdb_validation_status *state)
3607 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3609 if (!centry) {
3610 return 1;
3613 (void)centry_uint16(centry);
3614 (void)centry_uint16(centry);
3615 (void)centry_uint32(centry);
3616 (void)centry_nttime(centry);
3617 (void)centry_nttime(centry);
3619 centry_free(centry);
3621 if (!(state->success)) {
3622 return 1;
3624 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3625 return 0;
3628 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3629 struct tdb_validation_status *state)
3631 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3633 if (!centry) {
3634 return 1;
3637 (void)centry_time(centry);
3638 (void)centry_hash16(centry, mem_ctx);
3640 /* We only have 17 bytes more data in the salted cred case. */
3641 if (centry->len - centry->ofs == 17) {
3642 (void)centry_hash16(centry, mem_ctx);
3645 centry_free(centry);
3647 if (!(state->success)) {
3648 return 1;
3650 DEBUG(10,("validate_cred: %s ok\n", keystr));
3651 return 0;
3654 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3655 struct tdb_validation_status *state)
3657 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3658 int32 num_entries, i;
3660 if (!centry) {
3661 return 1;
3664 num_entries = (int32)centry_uint32(centry);
3666 for (i=0; i< num_entries; i++) {
3667 DOM_SID sid;
3668 (void)centry_string(centry, mem_ctx);
3669 (void)centry_string(centry, mem_ctx);
3670 (void)centry_string(centry, mem_ctx);
3671 (void)centry_string(centry, mem_ctx);
3672 (void)centry_sid(centry, &sid);
3673 (void)centry_sid(centry, &sid);
3676 centry_free(centry);
3678 if (!(state->success)) {
3679 return 1;
3681 DEBUG(10,("validate_ul: %s ok\n", keystr));
3682 return 0;
3685 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3686 struct tdb_validation_status *state)
3688 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3689 int32 num_entries, i;
3691 if (!centry) {
3692 return 1;
3695 num_entries = centry_uint32(centry);
3697 for (i=0; i< num_entries; i++) {
3698 (void)centry_string(centry, mem_ctx);
3699 (void)centry_string(centry, mem_ctx);
3700 (void)centry_uint32(centry);
3703 centry_free(centry);
3705 if (!(state->success)) {
3706 return 1;
3708 DEBUG(10,("validate_gl: %s ok\n", keystr));
3709 return 0;
3712 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3713 struct tdb_validation_status *state)
3715 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3716 int32 num_groups, i;
3718 if (!centry) {
3719 return 1;
3722 num_groups = centry_uint32(centry);
3724 for (i=0; i< num_groups; i++) {
3725 DOM_SID sid;
3726 centry_sid(centry, &sid);
3729 centry_free(centry);
3731 if (!(state->success)) {
3732 return 1;
3734 DEBUG(10,("validate_ug: %s ok\n", keystr));
3735 return 0;
3738 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3739 struct tdb_validation_status *state)
3741 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3742 int32 num_aliases, i;
3744 if (!centry) {
3745 return 1;
3748 num_aliases = centry_uint32(centry);
3750 for (i=0; i < num_aliases; i++) {
3751 (void)centry_uint32(centry);
3754 centry_free(centry);
3756 if (!(state->success)) {
3757 return 1;
3759 DEBUG(10,("validate_ua: %s ok\n", keystr));
3760 return 0;
3763 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3764 struct tdb_validation_status *state)
3766 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3767 int32 num_names, i;
3769 if (!centry) {
3770 return 1;
3773 num_names = centry_uint32(centry);
3775 for (i=0; i< num_names; i++) {
3776 DOM_SID sid;
3777 centry_sid(centry, &sid);
3778 (void)centry_string(centry, mem_ctx);
3779 (void)centry_uint32(centry);
3782 centry_free(centry);
3784 if (!(state->success)) {
3785 return 1;
3787 DEBUG(10,("validate_gm: %s ok\n", keystr));
3788 return 0;
3791 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3792 struct tdb_validation_status *state)
3794 /* Can't say anything about this other than must be nonzero. */
3795 if (dbuf.dsize == 0) {
3796 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3797 keystr));
3798 state->bad_entry = true;
3799 state->success = false;
3800 return 1;
3803 DEBUG(10,("validate_dr: %s ok\n", keystr));
3804 return 0;
3807 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3808 struct tdb_validation_status *state)
3810 /* Can't say anything about this other than must be nonzero. */
3811 if (dbuf.dsize == 0) {
3812 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3813 keystr));
3814 state->bad_entry = true;
3815 state->success = false;
3816 return 1;
3819 DEBUG(10,("validate_de: %s ok\n", keystr));
3820 return 0;
3823 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3824 TDB_DATA dbuf, struct tdb_validation_status *state)
3826 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3828 if (!centry) {
3829 return 1;
3832 (void)centry_string(centry, mem_ctx);
3833 (void)centry_string(centry, mem_ctx);
3834 (void)centry_string(centry, mem_ctx);
3835 (void)centry_uint32(centry);
3837 centry_free(centry);
3839 if (!(state->success)) {
3840 return 1;
3842 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3843 return 0;
3846 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3847 TDB_DATA dbuf,
3848 struct tdb_validation_status *state)
3850 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3852 if (!centry) {
3853 return 1;
3856 (void)centry_string( centry, mem_ctx );
3858 centry_free(centry);
3860 if (!(state->success)) {
3861 return 1;
3863 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3864 return 0;
3867 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3868 TDB_DATA dbuf,
3869 struct tdb_validation_status *state)
3871 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3873 if (!centry) {
3874 return 1;
3877 (void)centry_string( centry, mem_ctx );
3879 centry_free(centry);
3881 if (!(state->success)) {
3882 return 1;
3884 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3885 return 0;
3888 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3889 TDB_DATA dbuf,
3890 struct tdb_validation_status *state)
3892 if (dbuf.dsize == 0) {
3893 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3894 "key %s (len ==0) ?\n", keystr));
3895 state->bad_entry = true;
3896 state->success = false;
3897 return 1;
3900 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3901 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3902 return 0;
3905 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3906 struct tdb_validation_status *state)
3908 if (dbuf.dsize != 4) {
3909 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3910 keystr, (unsigned int)dbuf.dsize ));
3911 state->bad_entry = true;
3912 state->success = false;
3913 return 1;
3915 DEBUG(10,("validate_offline: %s ok\n", keystr));
3916 return 0;
3919 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3920 struct tdb_validation_status *state)
3923 * Ignore validation for now. The proper way to do this is with a
3924 * checksum. Just pure parsing does not really catch much.
3926 return 0;
3929 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3930 struct tdb_validation_status *state)
3932 if (dbuf.dsize != 4) {
3933 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3934 "key %s (len %u != 4) ?\n",
3935 keystr, (unsigned int)dbuf.dsize));
3936 state->bad_entry = true;
3937 state->success = false;
3938 return 1;
3941 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3942 return 0;
3945 /***********************************************************************
3946 A list of all possible cache tdb keys with associated validation
3947 functions.
3948 ***********************************************************************/
3950 struct key_val_struct {
3951 const char *keyname;
3952 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3953 } key_val[] = {
3954 {"SEQNUM/", validate_seqnum},
3955 {"NS/", validate_ns},
3956 {"SN/", validate_sn},
3957 {"U/", validate_u},
3958 {"LOC_POL/", validate_loc_pol},
3959 {"PWD_POL/", validate_pwd_pol},
3960 {"CRED/", validate_cred},
3961 {"UL/", validate_ul},
3962 {"GL/", validate_gl},
3963 {"UG/", validate_ug},
3964 {"UA", validate_ua},
3965 {"GM/", validate_gm},
3966 {"DR/", validate_dr},
3967 {"DE/", validate_de},
3968 {"NSS/PWINFO/", validate_pwinfo},
3969 {"TRUSTDOMCACHE/", validate_trustdomcache},
3970 {"NSS/NA/", validate_nss_na},
3971 {"NSS/AN/", validate_nss_an},
3972 {"WINBINDD_OFFLINE", validate_offline},
3973 {"NDR/", validate_ndr},
3974 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3975 {NULL, NULL}
3978 /***********************************************************************
3979 Function to look at every entry in the tdb and validate it as far as
3980 possible.
3981 ***********************************************************************/
3983 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3985 int i;
3986 unsigned int max_key_len = 1024;
3987 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3989 /* Paranoia check. */
3990 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3991 max_key_len = 1024 * 1024;
3993 if (kbuf.dsize > max_key_len) {
3994 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3995 "(%u) > (%u)\n\n",
3996 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3997 return 1;
4000 for (i = 0; key_val[i].keyname; i++) {
4001 size_t namelen = strlen(key_val[i].keyname);
4002 if (kbuf.dsize >= namelen && (
4003 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4004 TALLOC_CTX *mem_ctx;
4005 char *keystr;
4006 int ret;
4008 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4009 if (!keystr) {
4010 return 1;
4012 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4013 keystr[kbuf.dsize] = '\0';
4015 mem_ctx = talloc_init("validate_ctx");
4016 if (!mem_ctx) {
4017 SAFE_FREE(keystr);
4018 return 1;
4021 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4022 v_state);
4024 SAFE_FREE(keystr);
4025 talloc_destroy(mem_ctx);
4026 return ret;
4030 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4031 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4032 DEBUG(0,("data :\n"));
4033 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4034 v_state->unknown_key = true;
4035 v_state->success = false;
4036 return 1; /* terminate. */
4039 static void validate_panic(const char *const why)
4041 DEBUG(0,("validating cache: would panic %s\n", why ));
4042 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4043 exit(47);
4046 /***********************************************************************
4047 Try and validate every entry in the winbindd cache. If we fail here,
4048 delete the cache tdb and return non-zero.
4049 ***********************************************************************/
4051 int winbindd_validate_cache(void)
4053 int ret = -1;
4054 const char *tdb_path = cache_path("winbindd_cache.tdb");
4055 TDB_CONTEXT *tdb = NULL;
4057 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4058 smb_panic_fn = validate_panic;
4061 tdb = tdb_open_log(tdb_path,
4062 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4063 ( lp_winbind_offline_logon()
4064 ? TDB_DEFAULT
4065 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4066 O_RDWR|O_CREAT,
4067 0600);
4068 if (!tdb) {
4069 DEBUG(0, ("winbindd_validate_cache: "
4070 "error opening/initializing tdb\n"));
4071 goto done;
4073 tdb_close(tdb);
4075 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4077 if (ret != 0) {
4078 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4079 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4080 unlink(tdb_path);
4083 done:
4084 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4085 smb_panic_fn = smb_panic;
4086 return ret;
4089 /***********************************************************************
4090 Try and validate every entry in the winbindd cache.
4091 ***********************************************************************/
4093 int winbindd_validate_cache_nobackup(void)
4095 int ret = -1;
4096 const char *tdb_path = cache_path("winbindd_cache.tdb");
4098 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4099 smb_panic_fn = validate_panic;
4102 if (wcache == NULL || wcache->tdb == NULL) {
4103 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4104 } else {
4105 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4108 if (ret != 0) {
4109 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4110 "successful.\n"));
4113 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4114 "function\n"));
4115 smb_panic_fn = smb_panic;
4116 return ret;
4119 bool winbindd_cache_validate_and_initialize(void)
4121 close_winbindd_cache();
4123 if (lp_winbind_offline_logon()) {
4124 if (winbindd_validate_cache() < 0) {
4125 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4126 "could be restored.\n"));
4130 return initialize_winbindd_cache();
4133 /*********************************************************************
4134 ********************************************************************/
4136 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4137 struct winbindd_tdc_domain **domains,
4138 size_t *num_domains )
4140 struct winbindd_tdc_domain *list = NULL;
4141 size_t idx;
4142 int i;
4143 bool set_only = false;
4145 /* don't allow duplicates */
4147 idx = *num_domains;
4148 list = *domains;
4150 for ( i=0; i< (*num_domains); i++ ) {
4151 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4152 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4153 new_dom->name));
4154 idx = i;
4155 set_only = true;
4157 break;
4161 if ( !set_only ) {
4162 if ( !*domains ) {
4163 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4164 idx = 0;
4165 } else {
4166 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4167 struct winbindd_tdc_domain,
4168 (*num_domains)+1);
4169 idx = *num_domains;
4172 ZERO_STRUCT( list[idx] );
4175 if ( !list )
4176 return false;
4178 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4179 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4181 if ( !is_null_sid( &new_dom->sid ) ) {
4182 sid_copy( &list[idx].sid, &new_dom->sid );
4183 } else {
4184 sid_copy(&list[idx].sid, &global_sid_NULL);
4187 if ( new_dom->domain_flags != 0x0 )
4188 list[idx].trust_flags = new_dom->domain_flags;
4190 if ( new_dom->domain_type != 0x0 )
4191 list[idx].trust_type = new_dom->domain_type;
4193 if ( new_dom->domain_trust_attribs != 0x0 )
4194 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4196 if ( !set_only ) {
4197 *domains = list;
4198 *num_domains = idx + 1;
4201 return true;
4204 /*********************************************************************
4205 ********************************************************************/
4207 static TDB_DATA make_tdc_key( const char *domain_name )
4209 char *keystr = NULL;
4210 TDB_DATA key = { NULL, 0 };
4212 if ( !domain_name ) {
4213 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4214 return key;
4217 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4218 return key;
4220 key = string_term_tdb_data(keystr);
4222 return key;
4225 /*********************************************************************
4226 ********************************************************************/
4228 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4229 size_t num_domains,
4230 unsigned char **buf )
4232 unsigned char *buffer = NULL;
4233 int len = 0;
4234 int buflen = 0;
4235 int i = 0;
4237 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4238 (int)num_domains));
4240 buflen = 0;
4242 again:
4243 len = 0;
4245 /* Store the number of array items first */
4246 len += tdb_pack( buffer+len, buflen-len, "d",
4247 num_domains );
4249 /* now pack each domain trust record */
4250 for ( i=0; i<num_domains; i++ ) {
4252 fstring tmp;
4254 if ( buflen > 0 ) {
4255 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4256 domains[i].domain_name,
4257 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4260 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4261 domains[i].domain_name,
4262 domains[i].dns_name,
4263 sid_to_fstring(tmp, &domains[i].sid),
4264 domains[i].trust_flags,
4265 domains[i].trust_attribs,
4266 domains[i].trust_type );
4269 if ( buflen < len ) {
4270 SAFE_FREE(buffer);
4271 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4272 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4273 buflen = -1;
4274 goto done;
4276 buflen = len;
4277 goto again;
4280 *buf = buffer;
4282 done:
4283 return buflen;
4286 /*********************************************************************
4287 ********************************************************************/
4289 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4290 struct winbindd_tdc_domain **domains )
4292 fstring domain_name, dns_name, sid_string;
4293 uint32 type, attribs, flags;
4294 int num_domains;
4295 int len = 0;
4296 int i;
4297 struct winbindd_tdc_domain *list = NULL;
4299 /* get the number of domains */
4300 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4301 if ( len == -1 ) {
4302 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4303 return 0;
4306 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4307 if ( !list ) {
4308 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4309 return 0;
4312 for ( i=0; i<num_domains; i++ ) {
4313 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4314 domain_name,
4315 dns_name,
4316 sid_string,
4317 &flags,
4318 &attribs,
4319 &type );
4321 if ( len == -1 ) {
4322 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4323 TALLOC_FREE( list );
4324 return 0;
4327 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4328 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4329 domain_name, dns_name, sid_string,
4330 flags, attribs, type));
4332 list[i].domain_name = talloc_strdup( list, domain_name );
4333 list[i].dns_name = talloc_strdup( list, dns_name );
4334 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4335 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4336 domain_name));
4338 list[i].trust_flags = flags;
4339 list[i].trust_attribs = attribs;
4340 list[i].trust_type = type;
4343 *domains = list;
4345 return num_domains;
4348 /*********************************************************************
4349 ********************************************************************/
4351 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4353 TDB_DATA key = make_tdc_key( lp_workgroup() );
4354 TDB_DATA data = { NULL, 0 };
4355 int ret;
4357 if ( !key.dptr )
4358 return false;
4360 /* See if we were asked to delete the cache entry */
4362 if ( !domains ) {
4363 ret = tdb_delete( wcache->tdb, key );
4364 goto done;
4367 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4369 if ( !data.dptr ) {
4370 ret = -1;
4371 goto done;
4374 ret = tdb_store( wcache->tdb, key, data, 0 );
4376 done:
4377 SAFE_FREE( data.dptr );
4378 SAFE_FREE( key.dptr );
4380 return ( ret != -1 );
4383 /*********************************************************************
4384 ********************************************************************/
4386 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4388 TDB_DATA key = make_tdc_key( lp_workgroup() );
4389 TDB_DATA data = { NULL, 0 };
4391 *domains = NULL;
4392 *num_domains = 0;
4394 if ( !key.dptr )
4395 return false;
4397 data = tdb_fetch( wcache->tdb, key );
4399 SAFE_FREE( key.dptr );
4401 if ( !data.dptr )
4402 return false;
4404 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4406 SAFE_FREE( data.dptr );
4408 if ( !*domains )
4409 return false;
4411 return true;
4414 /*********************************************************************
4415 ********************************************************************/
4417 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4419 struct winbindd_tdc_domain *dom_list = NULL;
4420 size_t num_domains = 0;
4421 bool ret = false;
4423 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4424 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4425 domain->name, domain->alt_name,
4426 sid_string_dbg(&domain->sid),
4427 domain->domain_flags,
4428 domain->domain_trust_attribs,
4429 domain->domain_type));
4431 if ( !init_wcache() ) {
4432 return false;
4435 /* fetch the list */
4437 wcache_tdc_fetch_list( &dom_list, &num_domains );
4439 /* add the new domain */
4441 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4442 goto done;
4445 /* pack the domain */
4447 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4448 goto done;
4451 /* Success */
4453 ret = true;
4454 done:
4455 TALLOC_FREE( dom_list );
4457 return ret;
4460 /*********************************************************************
4461 ********************************************************************/
4463 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4465 struct winbindd_tdc_domain *dom_list = NULL;
4466 size_t num_domains = 0;
4467 int i;
4468 struct winbindd_tdc_domain *d = NULL;
4470 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4472 if ( !init_wcache() ) {
4473 return false;
4476 /* fetch the list */
4478 wcache_tdc_fetch_list( &dom_list, &num_domains );
4480 for ( i=0; i<num_domains; i++ ) {
4481 if ( strequal(name, dom_list[i].domain_name) ||
4482 strequal(name, dom_list[i].dns_name) )
4484 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4485 name));
4487 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4488 if ( !d )
4489 break;
4491 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4492 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4493 sid_copy( &d->sid, &dom_list[i].sid );
4494 d->trust_flags = dom_list[i].trust_flags;
4495 d->trust_type = dom_list[i].trust_type;
4496 d->trust_attribs = dom_list[i].trust_attribs;
4498 break;
4502 TALLOC_FREE( dom_list );
4504 return d;
4508 /*********************************************************************
4509 ********************************************************************/
4511 void wcache_tdc_clear( void )
4513 if ( !init_wcache() )
4514 return;
4516 wcache_tdc_store_list( NULL, 0 );
4518 return;
4522 /*********************************************************************
4523 ********************************************************************/
4525 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4526 NTSTATUS status,
4527 const DOM_SID *user_sid,
4528 const char *homedir,
4529 const char *shell,
4530 const char *gecos,
4531 uint32 gid)
4533 struct cache_entry *centry;
4534 fstring tmp;
4536 if ( (centry = centry_start(domain, status)) == NULL )
4537 return;
4539 centry_put_string( centry, homedir );
4540 centry_put_string( centry, shell );
4541 centry_put_string( centry, gecos );
4542 centry_put_uint32( centry, gid );
4544 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4546 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4548 centry_free(centry);
4551 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4552 const DOM_SID *user_sid,
4553 TALLOC_CTX *ctx,
4554 ADS_STRUCT *ads, LDAPMessage *msg,
4555 const char **homedir, const char **shell,
4556 const char **gecos, gid_t *p_gid)
4558 struct winbind_cache *cache = get_cache(domain);
4559 struct cache_entry *centry = NULL;
4560 NTSTATUS nt_status;
4561 fstring tmp;
4563 if (!cache->tdb)
4564 goto do_query;
4566 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4567 sid_to_fstring(tmp, user_sid));
4569 if (!centry)
4570 goto do_query;
4572 *homedir = centry_string( centry, ctx );
4573 *shell = centry_string( centry, ctx );
4574 *gecos = centry_string( centry, ctx );
4575 *p_gid = centry_uint32( centry );
4577 centry_free(centry);
4579 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4580 sid_string_dbg(user_sid)));
4582 return NT_STATUS_OK;
4584 do_query:
4586 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4587 homedir, shell, gecos, p_gid );
4589 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4591 if ( NT_STATUS_IS_OK(nt_status) ) {
4592 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4593 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4594 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4595 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4597 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4598 *homedir, *shell, *gecos, *p_gid );
4601 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4602 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4603 domain->name ));
4604 set_domain_offline( domain );
4607 return nt_status;
4611 /* the cache backend methods are exposed via this structure */
4612 struct winbindd_methods cache_methods = {
4613 true,
4614 query_user_list,
4615 enum_dom_groups,
4616 enum_local_groups,
4617 name_to_sid,
4618 sid_to_name,
4619 rids_to_names,
4620 query_user,
4621 lookup_usergroups,
4622 lookup_useraliases,
4623 lookup_groupmem,
4624 sequence_number,
4625 lockout_policy,
4626 password_policy,
4627 trusted_domains
4630 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4631 uint32_t opnum, const DATA_BLOB *req,
4632 TDB_DATA *pkey)
4634 char *key;
4635 size_t keylen;
4637 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4638 if (key == NULL) {
4639 return false;
4641 keylen = talloc_get_size(key) - 1;
4643 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4644 if (key == NULL) {
4645 return false;
4647 memcpy(key + keylen, req->data, req->length);
4649 pkey->dptr = (uint8_t *)key;
4650 pkey->dsize = talloc_get_size(key);
4651 return true;
4654 static bool wcache_opnum_cacheable(uint32_t opnum)
4656 switch (opnum) {
4657 case NDR_WBINT_PING:
4658 case NDR_WBINT_QUERYSEQUENCENUMBER:
4659 case NDR_WBINT_ALLOCATEUID:
4660 case NDR_WBINT_ALLOCATEGID:
4661 case NDR_WBINT_CHECKMACHINEACCOUNT:
4662 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4663 case NDR_WBINT_PINGDC:
4664 return false;
4666 return true;
4669 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4670 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4672 TDB_DATA key, data;
4673 bool ret = false;
4675 if (!wcache_opnum_cacheable(opnum)) {
4676 return false;
4679 if (wcache->tdb == NULL) {
4680 return false;
4683 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4684 return false;
4686 data = tdb_fetch(wcache->tdb, key);
4687 TALLOC_FREE(key.dptr);
4689 if (data.dptr == NULL) {
4690 return false;
4692 if (data.dsize < 4) {
4693 goto fail;
4696 if (!is_domain_offline(domain)) {
4697 uint32_t entry_seqnum, dom_seqnum, last_check;
4699 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4700 &last_check)) {
4701 goto fail;
4703 entry_seqnum = IVAL(data.dptr, 0);
4704 if (entry_seqnum != dom_seqnum) {
4705 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4706 (int)entry_seqnum));
4707 goto fail;
4711 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4712 data.dsize - 4);
4713 if (resp->data == NULL) {
4714 DEBUG(10, ("talloc failed\n"));
4715 goto fail;
4717 resp->length = data.dsize - 4;
4719 ret = true;
4720 fail:
4721 SAFE_FREE(data.dptr);
4722 return ret;
4725 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4726 const DATA_BLOB *req, const DATA_BLOB *resp)
4728 TDB_DATA key, data;
4729 uint32_t dom_seqnum, last_check;
4731 if (!wcache_opnum_cacheable(opnum)) {
4732 return;
4735 if (wcache->tdb == NULL) {
4736 return;
4739 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4740 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4741 domain->name));
4742 return;
4745 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4746 return;
4749 data.dsize = resp->length + 4;
4750 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4751 if (data.dptr == NULL) {
4752 goto done;
4755 SIVAL(data.dptr, 0, dom_seqnum);
4756 memcpy(data.dptr+4, resp->data, resp->length);
4758 tdb_store(wcache->tdb, key, data, 0);
4760 done:
4761 TALLOC_FREE(key.dptr);
4762 return;