Merge branch 'master' of /home/tridge/samba/git/combined
[Samba/aatanasov.git] / source3 / winbindd / winbindd_cache.c
blob6d48fe5f857cc77da7422fb207de8a13104afd45
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;
1384 if (!cache->tdb)
1385 goto do_query;
1387 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1388 if (!centry)
1389 goto do_query;
1391 *num_entries = centry_uint32(centry);
1393 if (*num_entries == 0)
1394 goto do_cached;
1396 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1397 if (! (*info)) {
1398 smb_panic_fn("query_user_list out of memory");
1400 for (i=0; i<(*num_entries); i++) {
1401 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1402 (*info)[i].full_name = centry_string(centry, mem_ctx);
1403 (*info)[i].homedir = centry_string(centry, mem_ctx);
1404 (*info)[i].shell = centry_string(centry, mem_ctx);
1405 centry_sid(centry, &(*info)[i].user_sid);
1406 centry_sid(centry, &(*info)[i].group_sid);
1409 do_cached:
1410 status = centry->status;
1412 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1413 domain->name, nt_errstr(status) ));
1415 centry_free(centry);
1416 return status;
1418 do_query:
1419 *num_entries = 0;
1420 *info = NULL;
1422 /* Return status value returned by seq number check */
1424 if (!NT_STATUS_IS_OK(domain->last_status))
1425 return domain->last_status;
1427 /* Put the query_user_list() in a retry loop. There appears to be
1428 * some bug either with Windows 2000 or Samba's handling of large
1429 * rpc replies. This manifests itself as sudden disconnection
1430 * at a random point in the enumeration of a large (60k) user list.
1431 * The retry loop simply tries the operation again. )-: It's not
1432 * pretty but an acceptable workaround until we work out what the
1433 * real problem is. */
1435 retry = 0;
1436 do {
1438 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1439 domain->name ));
1441 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1442 if (!NT_STATUS_IS_OK(status)) {
1443 DEBUG(3, ("query_user_list: returned 0x%08x, "
1444 "retrying\n", NT_STATUS_V(status)));
1446 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1447 DEBUG(3, ("query_user_list: flushing "
1448 "connection cache\n"));
1449 invalidate_cm_connection(&domain->conn);
1452 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1453 (retry++ < 5));
1455 /* and save it */
1456 refresh_sequence_number(domain, false);
1457 centry = centry_start(domain, status);
1458 if (!centry)
1459 goto skip_save;
1460 centry_put_uint32(centry, *num_entries);
1461 for (i=0; i<(*num_entries); i++) {
1462 centry_put_string(centry, (*info)[i].acct_name);
1463 centry_put_string(centry, (*info)[i].full_name);
1464 centry_put_string(centry, (*info)[i].homedir);
1465 centry_put_string(centry, (*info)[i].shell);
1466 centry_put_sid(centry, &(*info)[i].user_sid);
1467 centry_put_sid(centry, &(*info)[i].group_sid);
1468 if (domain->backend && domain->backend->consistent) {
1469 /* when the backend is consistent we can pre-prime some mappings */
1470 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1471 domain->name,
1472 (*info)[i].acct_name,
1473 &(*info)[i].user_sid,
1474 SID_NAME_USER);
1475 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1476 &(*info)[i].user_sid,
1477 domain->name,
1478 (*info)[i].acct_name,
1479 SID_NAME_USER);
1480 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1483 centry_end(centry, "UL/%s", domain->name);
1484 centry_free(centry);
1486 skip_save:
1487 return status;
1490 /* list all domain groups */
1491 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1492 TALLOC_CTX *mem_ctx,
1493 uint32 *num_entries,
1494 struct acct_info **info)
1496 struct winbind_cache *cache = get_cache(domain);
1497 struct cache_entry *centry = NULL;
1498 NTSTATUS status;
1499 unsigned int i;
1501 if (!cache->tdb)
1502 goto do_query;
1504 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1505 if (!centry)
1506 goto do_query;
1508 *num_entries = centry_uint32(centry);
1510 if (*num_entries == 0)
1511 goto do_cached;
1513 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1514 if (! (*info)) {
1515 smb_panic_fn("enum_dom_groups out of memory");
1517 for (i=0; i<(*num_entries); i++) {
1518 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1519 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1520 (*info)[i].rid = centry_uint32(centry);
1523 do_cached:
1524 status = centry->status;
1526 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1527 domain->name, nt_errstr(status) ));
1529 centry_free(centry);
1530 return status;
1532 do_query:
1533 *num_entries = 0;
1534 *info = NULL;
1536 /* Return status value returned by seq number check */
1538 if (!NT_STATUS_IS_OK(domain->last_status))
1539 return domain->last_status;
1541 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1542 domain->name ));
1544 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1546 /* and save it */
1547 refresh_sequence_number(domain, false);
1548 centry = centry_start(domain, status);
1549 if (!centry)
1550 goto skip_save;
1551 centry_put_uint32(centry, *num_entries);
1552 for (i=0; i<(*num_entries); i++) {
1553 centry_put_string(centry, (*info)[i].acct_name);
1554 centry_put_string(centry, (*info)[i].acct_desc);
1555 centry_put_uint32(centry, (*info)[i].rid);
1557 centry_end(centry, "GL/%s/domain", domain->name);
1558 centry_free(centry);
1560 skip_save:
1561 return status;
1564 /* list all domain groups */
1565 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1566 TALLOC_CTX *mem_ctx,
1567 uint32 *num_entries,
1568 struct acct_info **info)
1570 struct winbind_cache *cache = get_cache(domain);
1571 struct cache_entry *centry = NULL;
1572 NTSTATUS status;
1573 unsigned int i;
1575 if (!cache->tdb)
1576 goto do_query;
1578 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1579 if (!centry)
1580 goto do_query;
1582 *num_entries = centry_uint32(centry);
1584 if (*num_entries == 0)
1585 goto do_cached;
1587 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1588 if (! (*info)) {
1589 smb_panic_fn("enum_dom_groups out of memory");
1591 for (i=0; i<(*num_entries); i++) {
1592 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1593 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1594 (*info)[i].rid = centry_uint32(centry);
1597 do_cached:
1599 /* If we are returning cached data and the domain controller
1600 is down then we don't know whether the data is up to date
1601 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1602 indicate this. */
1604 if (wcache_server_down(domain)) {
1605 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1606 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1607 } else
1608 status = centry->status;
1610 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1611 domain->name, nt_errstr(status) ));
1613 centry_free(centry);
1614 return status;
1616 do_query:
1617 *num_entries = 0;
1618 *info = NULL;
1620 /* Return status value returned by seq number check */
1622 if (!NT_STATUS_IS_OK(domain->last_status))
1623 return domain->last_status;
1625 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1626 domain->name ));
1628 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1630 /* and save it */
1631 refresh_sequence_number(domain, false);
1632 centry = centry_start(domain, status);
1633 if (!centry)
1634 goto skip_save;
1635 centry_put_uint32(centry, *num_entries);
1636 for (i=0; i<(*num_entries); i++) {
1637 centry_put_string(centry, (*info)[i].acct_name);
1638 centry_put_string(centry, (*info)[i].acct_desc);
1639 centry_put_uint32(centry, (*info)[i].rid);
1641 centry_end(centry, "GL/%s/local", domain->name);
1642 centry_free(centry);
1644 skip_save:
1645 return status;
1648 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1649 const char *domain_name,
1650 const char *name,
1651 struct dom_sid *sid,
1652 enum lsa_SidType *type)
1654 struct winbind_cache *cache = get_cache(domain);
1655 struct cache_entry *centry;
1656 NTSTATUS status;
1657 char *uname;
1659 if (cache->tdb == NULL) {
1660 return NT_STATUS_NOT_FOUND;
1663 uname = talloc_strdup_upper(talloc_tos(), name);
1664 if (uname == NULL) {
1665 return NT_STATUS_NO_MEMORY;
1668 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1669 TALLOC_FREE(uname);
1670 if (centry == NULL) {
1671 return NT_STATUS_NOT_FOUND;
1674 status = centry->status;
1675 if (NT_STATUS_IS_OK(status)) {
1676 *type = (enum lsa_SidType)centry_uint32(centry);
1677 centry_sid(centry, sid);
1680 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1681 "%s\n", domain->name, nt_errstr(status) ));
1683 centry_free(centry);
1684 return status;
1687 /* convert a single name to a sid in a domain */
1688 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1689 TALLOC_CTX *mem_ctx,
1690 const char *domain_name,
1691 const char *name,
1692 uint32_t flags,
1693 DOM_SID *sid,
1694 enum lsa_SidType *type)
1696 NTSTATUS status;
1698 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1699 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1700 return status;
1703 ZERO_STRUCTP(sid);
1705 /* If the seq number check indicated that there is a problem
1706 * with this DC, then return that status... except for
1707 * access_denied. This is special because the dc may be in
1708 * "restrict anonymous = 1" mode, in which case it will deny
1709 * most unauthenticated operations, but *will* allow the LSA
1710 * name-to-sid that we try as a fallback. */
1712 if (!(NT_STATUS_IS_OK(domain->last_status)
1713 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1714 return domain->last_status;
1716 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1717 domain->name ));
1719 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1720 name, flags, sid, type);
1722 /* and save it */
1723 refresh_sequence_number(domain, false);
1725 if (domain->online &&
1726 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1727 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1729 /* Only save the reverse mapping if this was not a UPN */
1730 if (!strchr(name, '@')) {
1731 strupper_m(CONST_DISCARD(char *,domain_name));
1732 strlower_m(CONST_DISCARD(char *,name));
1733 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1737 return status;
1740 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1741 const struct dom_sid *sid,
1742 TALLOC_CTX *mem_ctx,
1743 char **domain_name,
1744 char **name,
1745 enum lsa_SidType *type)
1747 struct winbind_cache *cache = get_cache(domain);
1748 struct cache_entry *centry;
1749 char *sid_string;
1750 NTSTATUS status;
1752 if (cache->tdb == NULL) {
1753 return NT_STATUS_NOT_FOUND;
1756 sid_string = sid_string_tos(sid);
1757 if (sid_string == NULL) {
1758 return NT_STATUS_NO_MEMORY;
1761 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1762 TALLOC_FREE(sid_string);
1763 if (centry == NULL) {
1764 return NT_STATUS_NOT_FOUND;
1767 if (NT_STATUS_IS_OK(centry->status)) {
1768 *type = (enum lsa_SidType)centry_uint32(centry);
1769 *domain_name = centry_string(centry, mem_ctx);
1770 *name = centry_string(centry, mem_ctx);
1773 status = centry->status;
1774 centry_free(centry);
1776 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1777 "%s\n", domain->name, nt_errstr(status) ));
1779 return status;
1782 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1783 given */
1784 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1785 TALLOC_CTX *mem_ctx,
1786 const DOM_SID *sid,
1787 char **domain_name,
1788 char **name,
1789 enum lsa_SidType *type)
1791 NTSTATUS status;
1793 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1794 type);
1795 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1796 return status;
1799 *name = NULL;
1800 *domain_name = NULL;
1802 /* If the seq number check indicated that there is a problem
1803 * with this DC, then return that status... except for
1804 * access_denied. This is special because the dc may be in
1805 * "restrict anonymous = 1" mode, in which case it will deny
1806 * most unauthenticated operations, but *will* allow the LSA
1807 * sid-to-name that we try as a fallback. */
1809 if (!(NT_STATUS_IS_OK(domain->last_status)
1810 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1811 return domain->last_status;
1813 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1814 domain->name ));
1816 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1818 /* and save it */
1819 refresh_sequence_number(domain, false);
1820 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1822 /* We can't save the name to sid mapping here, as with sid history a
1823 * later name2sid would give the wrong sid. */
1825 return status;
1828 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1829 TALLOC_CTX *mem_ctx,
1830 const DOM_SID *domain_sid,
1831 uint32 *rids,
1832 size_t num_rids,
1833 char **domain_name,
1834 char ***names,
1835 enum lsa_SidType **types)
1837 struct winbind_cache *cache = get_cache(domain);
1838 size_t i;
1839 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1840 bool have_mapped;
1841 bool have_unmapped;
1843 *domain_name = NULL;
1844 *names = NULL;
1845 *types = NULL;
1847 if (!cache->tdb) {
1848 goto do_query;
1851 if (num_rids == 0) {
1852 return NT_STATUS_OK;
1855 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1856 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1858 if ((*names == NULL) || (*types == NULL)) {
1859 result = NT_STATUS_NO_MEMORY;
1860 goto error;
1863 have_mapped = have_unmapped = false;
1865 for (i=0; i<num_rids; i++) {
1866 DOM_SID sid;
1867 struct cache_entry *centry;
1868 fstring tmp;
1870 if (!sid_compose(&sid, domain_sid, rids[i])) {
1871 result = NT_STATUS_INTERNAL_ERROR;
1872 goto error;
1875 centry = wcache_fetch(cache, domain, "SN/%s",
1876 sid_to_fstring(tmp, &sid));
1877 if (!centry) {
1878 goto do_query;
1881 (*types)[i] = SID_NAME_UNKNOWN;
1882 (*names)[i] = talloc_strdup(*names, "");
1884 if (NT_STATUS_IS_OK(centry->status)) {
1885 char *dom;
1886 have_mapped = true;
1887 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1889 dom = centry_string(centry, mem_ctx);
1890 if (*domain_name == NULL) {
1891 *domain_name = dom;
1892 } else {
1893 talloc_free(dom);
1896 (*names)[i] = centry_string(centry, *names);
1898 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1899 have_unmapped = true;
1901 } else {
1902 /* something's definitely wrong */
1903 result = centry->status;
1904 goto error;
1907 centry_free(centry);
1910 if (!have_mapped) {
1911 return NT_STATUS_NONE_MAPPED;
1913 if (!have_unmapped) {
1914 return NT_STATUS_OK;
1916 return STATUS_SOME_UNMAPPED;
1918 do_query:
1920 TALLOC_FREE(*names);
1921 TALLOC_FREE(*types);
1923 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1924 rids, num_rids, domain_name,
1925 names, types);
1928 None of the queried rids has been found so save all negative entries
1930 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1931 for (i = 0; i < num_rids; i++) {
1932 DOM_SID sid;
1933 const char *name = "";
1934 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1935 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1937 if (!sid_compose(&sid, domain_sid, rids[i])) {
1938 return NT_STATUS_INTERNAL_ERROR;
1941 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1942 name, type);
1945 return result;
1949 Some or all of the queried rids have been found.
1951 if (!NT_STATUS_IS_OK(result) &&
1952 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1953 return result;
1956 refresh_sequence_number(domain, false);
1958 for (i=0; i<num_rids; i++) {
1959 DOM_SID sid;
1960 NTSTATUS status;
1962 if (!sid_compose(&sid, domain_sid, rids[i])) {
1963 result = NT_STATUS_INTERNAL_ERROR;
1964 goto error;
1967 status = (*types)[i] == SID_NAME_UNKNOWN ?
1968 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1970 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1971 (*names)[i], (*types)[i]);
1974 return result;
1976 error:
1977 TALLOC_FREE(*names);
1978 TALLOC_FREE(*types);
1979 return result;
1982 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
1983 TALLOC_CTX *mem_ctx,
1984 const struct dom_sid *user_sid,
1985 struct wbint_userinfo *info)
1987 struct winbind_cache *cache = get_cache(domain);
1988 struct cache_entry *centry = NULL;
1989 NTSTATUS status;
1990 char *sid_string;
1992 if (cache->tdb == NULL) {
1993 return NT_STATUS_NOT_FOUND;
1996 sid_string = sid_string_tos(user_sid);
1997 if (sid_string == NULL) {
1998 return NT_STATUS_NO_MEMORY;
2001 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2002 TALLOC_FREE(sid_string);
2003 if (centry == NULL) {
2004 return NT_STATUS_NOT_FOUND;
2008 * If we have an access denied cache entry and a cached info3
2009 * in the samlogon cache then do a query. This will force the
2010 * rpc back end to return the info3 data.
2013 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2014 netsamlogon_cache_have(user_sid)) {
2015 DEBUG(10, ("query_user: cached access denied and have cached "
2016 "info3\n"));
2017 domain->last_status = NT_STATUS_OK;
2018 centry_free(centry);
2019 return NT_STATUS_NOT_FOUND;
2022 /* if status is not ok then this is a negative hit
2023 and the rest of the data doesn't matter */
2024 status = centry->status;
2025 if (NT_STATUS_IS_OK(status)) {
2026 info->acct_name = centry_string(centry, mem_ctx);
2027 info->full_name = centry_string(centry, mem_ctx);
2028 info->homedir = centry_string(centry, mem_ctx);
2029 info->shell = centry_string(centry, mem_ctx);
2030 info->primary_gid = centry_uint32(centry);
2031 centry_sid(centry, &info->user_sid);
2032 centry_sid(centry, &info->group_sid);
2035 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2036 "%s\n", domain->name, nt_errstr(status) ));
2038 centry_free(centry);
2039 return status;
2042 /* Lookup user information from a rid */
2043 static NTSTATUS query_user(struct winbindd_domain *domain,
2044 TALLOC_CTX *mem_ctx,
2045 const DOM_SID *user_sid,
2046 struct wbint_userinfo *info)
2048 NTSTATUS status;
2050 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2051 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2052 return status;
2055 ZERO_STRUCTP(info);
2057 /* Return status value returned by seq number check */
2059 if (!NT_STATUS_IS_OK(domain->last_status))
2060 return domain->last_status;
2062 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2063 domain->name ));
2065 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2067 /* and save it */
2068 refresh_sequence_number(domain, false);
2069 wcache_save_user(domain, status, info);
2071 return status;
2074 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2075 TALLOC_CTX *mem_ctx,
2076 const struct dom_sid *user_sid,
2077 uint32_t *pnum_sids,
2078 struct dom_sid **psids)
2080 struct winbind_cache *cache = get_cache(domain);
2081 struct cache_entry *centry = NULL;
2082 NTSTATUS status;
2083 uint32_t i, num_sids;
2084 struct dom_sid *sids;
2085 fstring sid_string;
2087 if (cache->tdb == NULL) {
2088 return NT_STATUS_NOT_FOUND;
2091 centry = wcache_fetch(cache, domain, "UG/%s",
2092 sid_to_fstring(sid_string, user_sid));
2093 if (centry == NULL) {
2094 return NT_STATUS_NOT_FOUND;
2097 /* If we have an access denied cache entry and a cached info3 in the
2098 samlogon cache then do a query. This will force the rpc back end
2099 to return the info3 data. */
2101 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2102 && netsamlogon_cache_have(user_sid)) {
2103 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2104 "cached info3\n"));
2105 domain->last_status = NT_STATUS_OK;
2106 centry_free(centry);
2107 return NT_STATUS_NOT_FOUND;
2110 num_sids = centry_uint32(centry);
2111 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2112 if (sids == NULL) {
2113 centry_free(centry);
2114 return NT_STATUS_NO_MEMORY;
2117 for (i=0; i<num_sids; i++) {
2118 centry_sid(centry, &sids[i]);
2121 status = centry->status;
2123 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2124 "status: %s\n", domain->name, nt_errstr(status)));
2126 centry_free(centry);
2128 *pnum_sids = num_sids;
2129 *psids = sids;
2130 return status;
2133 /* Lookup groups a user is a member of. */
2134 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2135 TALLOC_CTX *mem_ctx,
2136 const DOM_SID *user_sid,
2137 uint32 *num_groups, DOM_SID **user_gids)
2139 struct cache_entry *centry = NULL;
2140 NTSTATUS status;
2141 unsigned int i;
2142 fstring sid_string;
2144 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2145 num_groups, user_gids);
2146 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2147 return status;
2150 (*num_groups) = 0;
2151 (*user_gids) = NULL;
2153 /* Return status value returned by seq number check */
2155 if (!NT_STATUS_IS_OK(domain->last_status))
2156 return domain->last_status;
2158 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2159 domain->name ));
2161 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2163 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2164 goto skip_save;
2166 /* and save it */
2167 refresh_sequence_number(domain, false);
2168 centry = centry_start(domain, status);
2169 if (!centry)
2170 goto skip_save;
2172 centry_put_uint32(centry, *num_groups);
2173 for (i=0; i<(*num_groups); i++) {
2174 centry_put_sid(centry, &(*user_gids)[i]);
2177 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2178 centry_free(centry);
2180 skip_save:
2181 return status;
2184 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2185 const struct dom_sid *sids)
2187 uint32_t i;
2188 char *sidlist;
2190 sidlist = talloc_strdup(mem_ctx, "");
2191 if (sidlist == NULL) {
2192 return NULL;
2194 for (i=0; i<num_sids; i++) {
2195 fstring tmp;
2196 sidlist = talloc_asprintf_append_buffer(
2197 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2198 if (sidlist == NULL) {
2199 return NULL;
2202 return sidlist;
2205 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2206 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2207 const struct dom_sid *sids,
2208 uint32_t *pnum_aliases, uint32_t **paliases)
2210 struct winbind_cache *cache = get_cache(domain);
2211 struct cache_entry *centry = NULL;
2212 uint32_t num_aliases;
2213 uint32_t *aliases;
2214 NTSTATUS status;
2215 char *sidlist;
2216 int i;
2218 if (cache->tdb == NULL) {
2219 return NT_STATUS_NOT_FOUND;
2222 if (num_sids == 0) {
2223 *pnum_aliases = 0;
2224 *paliases = NULL;
2225 return NT_STATUS_OK;
2228 /* We need to cache indexed by the whole list of SIDs, the aliases
2229 * resulting might come from any of the SIDs. */
2231 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2232 if (sidlist == NULL) {
2233 return NT_STATUS_NO_MEMORY;
2236 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2237 TALLOC_FREE(sidlist);
2238 if (centry == NULL) {
2239 return NT_STATUS_NOT_FOUND;
2242 num_aliases = centry_uint32(centry);
2243 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2244 if (aliases == NULL) {
2245 centry_free(centry);
2246 return NT_STATUS_NO_MEMORY;
2249 for (i=0; i<num_aliases; i++) {
2250 aliases[i] = centry_uint32(centry);
2253 status = centry->status;
2255 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2256 "status %s\n", domain->name, nt_errstr(status)));
2258 centry_free(centry);
2260 *pnum_aliases = num_aliases;
2261 *paliases = aliases;
2263 return status;
2266 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2267 TALLOC_CTX *mem_ctx,
2268 uint32 num_sids, const DOM_SID *sids,
2269 uint32 *num_aliases, uint32 **alias_rids)
2271 struct cache_entry *centry = NULL;
2272 NTSTATUS status;
2273 char *sidlist;
2274 int i;
2276 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2277 num_aliases, alias_rids);
2278 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2279 return status;
2282 (*num_aliases) = 0;
2283 (*alias_rids) = NULL;
2285 if (!NT_STATUS_IS_OK(domain->last_status))
2286 return domain->last_status;
2288 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2289 "for domain %s\n", domain->name ));
2291 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2292 if (sidlist == NULL) {
2293 return NT_STATUS_NO_MEMORY;
2296 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2297 num_sids, sids,
2298 num_aliases, alias_rids);
2300 /* and save it */
2301 refresh_sequence_number(domain, false);
2302 centry = centry_start(domain, status);
2303 if (!centry)
2304 goto skip_save;
2305 centry_put_uint32(centry, *num_aliases);
2306 for (i=0; i<(*num_aliases); i++)
2307 centry_put_uint32(centry, (*alias_rids)[i]);
2308 centry_end(centry, "UA%s", sidlist);
2309 centry_free(centry);
2311 skip_save:
2312 return status;
2315 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2316 TALLOC_CTX *mem_ctx,
2317 const struct dom_sid *group_sid,
2318 uint32_t *num_names,
2319 struct dom_sid **sid_mem, char ***names,
2320 uint32_t **name_types)
2322 struct winbind_cache *cache = get_cache(domain);
2323 struct cache_entry *centry = NULL;
2324 NTSTATUS status;
2325 unsigned int i;
2326 char *sid_string;
2328 if (cache->tdb == NULL) {
2329 return NT_STATUS_NOT_FOUND;
2332 sid_string = sid_string_tos(group_sid);
2333 if (sid_string == NULL) {
2334 return NT_STATUS_NO_MEMORY;
2337 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2338 TALLOC_FREE(sid_string);
2339 if (centry == NULL) {
2340 return NT_STATUS_NOT_FOUND;
2343 *sid_mem = NULL;
2344 *names = NULL;
2345 *name_types = NULL;
2347 *num_names = centry_uint32(centry);
2348 if (*num_names == 0) {
2349 centry_free(centry);
2350 return NT_STATUS_OK;
2353 *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2354 *names = talloc_array(mem_ctx, char *, *num_names);
2355 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2357 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2358 TALLOC_FREE(*sid_mem);
2359 TALLOC_FREE(*names);
2360 TALLOC_FREE(*name_types);
2361 centry_free(centry);
2362 return NT_STATUS_NO_MEMORY;
2365 for (i=0; i<(*num_names); i++) {
2366 centry_sid(centry, &(*sid_mem)[i]);
2367 (*names)[i] = centry_string(centry, mem_ctx);
2368 (*name_types)[i] = centry_uint32(centry);
2371 status = centry->status;
2373 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2374 "status: %s\n", domain->name, nt_errstr(status)));
2376 centry_free(centry);
2377 return status;
2380 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2381 TALLOC_CTX *mem_ctx,
2382 const DOM_SID *group_sid,
2383 enum lsa_SidType type,
2384 uint32 *num_names,
2385 DOM_SID **sid_mem, char ***names,
2386 uint32 **name_types)
2388 struct cache_entry *centry = NULL;
2389 NTSTATUS status;
2390 unsigned int i;
2391 fstring sid_string;
2393 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2394 sid_mem, names, name_types);
2395 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2396 return status;
2399 (*num_names) = 0;
2400 (*sid_mem) = NULL;
2401 (*names) = NULL;
2402 (*name_types) = NULL;
2404 /* Return status value returned by seq number check */
2406 if (!NT_STATUS_IS_OK(domain->last_status))
2407 return domain->last_status;
2409 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2410 domain->name ));
2412 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2413 type, num_names,
2414 sid_mem, names, name_types);
2416 /* and save it */
2417 refresh_sequence_number(domain, false);
2418 centry = centry_start(domain, status);
2419 if (!centry)
2420 goto skip_save;
2421 centry_put_uint32(centry, *num_names);
2422 for (i=0; i<(*num_names); i++) {
2423 centry_put_sid(centry, &(*sid_mem)[i]);
2424 centry_put_string(centry, (*names)[i]);
2425 centry_put_uint32(centry, (*name_types)[i]);
2427 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2428 centry_free(centry);
2430 skip_save:
2431 return status;
2434 /* find the sequence number for a domain */
2435 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2437 refresh_sequence_number(domain, false);
2439 *seq = domain->sequence_number;
2441 return NT_STATUS_OK;
2444 /* enumerate trusted domains
2445 * (we need to have the list of trustdoms in the cache when we go offline) -
2446 * Guenther */
2447 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2448 TALLOC_CTX *mem_ctx,
2449 uint32 *num_domains,
2450 char ***names,
2451 char ***alt_names,
2452 DOM_SID **dom_sids)
2454 struct winbind_cache *cache = get_cache(domain);
2455 struct cache_entry *centry = NULL;
2456 NTSTATUS status;
2457 int i;
2459 if (!cache->tdb)
2460 goto do_query;
2462 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2464 if (!centry) {
2465 goto do_query;
2468 *num_domains = centry_uint32(centry);
2470 if (*num_domains) {
2471 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2472 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2473 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2475 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2476 smb_panic_fn("trusted_domains out of memory");
2478 } else {
2479 (*names) = NULL;
2480 (*alt_names) = NULL;
2481 (*dom_sids) = NULL;
2484 for (i=0; i<(*num_domains); i++) {
2485 (*names)[i] = centry_string(centry, mem_ctx);
2486 (*alt_names)[i] = centry_string(centry, mem_ctx);
2487 if (!centry_sid(centry, &(*dom_sids)[i])) {
2488 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2492 status = centry->status;
2494 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2495 domain->name, *num_domains, nt_errstr(status) ));
2497 centry_free(centry);
2498 return status;
2500 do_query:
2501 (*num_domains) = 0;
2502 (*dom_sids) = NULL;
2503 (*names) = NULL;
2504 (*alt_names) = NULL;
2506 /* Return status value returned by seq number check */
2508 if (!NT_STATUS_IS_OK(domain->last_status))
2509 return domain->last_status;
2511 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2512 domain->name ));
2514 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2515 names, alt_names, dom_sids);
2517 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2518 * so that the generic centry handling still applies correctly -
2519 * Guenther*/
2521 if (!NT_STATUS_IS_ERR(status)) {
2522 status = NT_STATUS_OK;
2526 #if 0 /* Disabled as we want the trust dom list to be managed by
2527 the main parent and always to make the query. --jerry */
2529 /* and save it */
2530 refresh_sequence_number(domain, false);
2532 centry = centry_start(domain, status);
2533 if (!centry)
2534 goto skip_save;
2536 centry_put_uint32(centry, *num_domains);
2538 for (i=0; i<(*num_domains); i++) {
2539 centry_put_string(centry, (*names)[i]);
2540 centry_put_string(centry, (*alt_names)[i]);
2541 centry_put_sid(centry, &(*dom_sids)[i]);
2544 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2546 centry_free(centry);
2548 skip_save:
2549 #endif
2551 return status;
2554 /* get lockout policy */
2555 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2556 TALLOC_CTX *mem_ctx,
2557 struct samr_DomInfo12 *policy)
2559 struct winbind_cache *cache = get_cache(domain);
2560 struct cache_entry *centry = NULL;
2561 NTSTATUS status;
2563 if (!cache->tdb)
2564 goto do_query;
2566 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2568 if (!centry)
2569 goto do_query;
2571 policy->lockout_duration = centry_nttime(centry);
2572 policy->lockout_window = centry_nttime(centry);
2573 policy->lockout_threshold = centry_uint16(centry);
2575 status = centry->status;
2577 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2578 domain->name, nt_errstr(status) ));
2580 centry_free(centry);
2581 return status;
2583 do_query:
2584 ZERO_STRUCTP(policy);
2586 /* Return status value returned by seq number check */
2588 if (!NT_STATUS_IS_OK(domain->last_status))
2589 return domain->last_status;
2591 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2592 domain->name ));
2594 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2596 /* and save it */
2597 refresh_sequence_number(domain, false);
2598 wcache_save_lockout_policy(domain, status, policy);
2600 return status;
2603 /* get password policy */
2604 static NTSTATUS password_policy(struct winbindd_domain *domain,
2605 TALLOC_CTX *mem_ctx,
2606 struct samr_DomInfo1 *policy)
2608 struct winbind_cache *cache = get_cache(domain);
2609 struct cache_entry *centry = NULL;
2610 NTSTATUS status;
2612 if (!cache->tdb)
2613 goto do_query;
2615 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2617 if (!centry)
2618 goto do_query;
2620 policy->min_password_length = centry_uint16(centry);
2621 policy->password_history_length = centry_uint16(centry);
2622 policy->password_properties = centry_uint32(centry);
2623 policy->max_password_age = centry_nttime(centry);
2624 policy->min_password_age = centry_nttime(centry);
2626 status = centry->status;
2628 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2629 domain->name, nt_errstr(status) ));
2631 centry_free(centry);
2632 return status;
2634 do_query:
2635 ZERO_STRUCTP(policy);
2637 /* Return status value returned by seq number check */
2639 if (!NT_STATUS_IS_OK(domain->last_status))
2640 return domain->last_status;
2642 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2643 domain->name ));
2645 status = domain->backend->password_policy(domain, mem_ctx, policy);
2647 /* and save it */
2648 refresh_sequence_number(domain, false);
2649 if (NT_STATUS_IS_OK(status)) {
2650 wcache_save_password_policy(domain, status, policy);
2653 return status;
2657 /* Invalidate cached user and group lists coherently */
2659 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2660 void *state)
2662 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2663 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2664 tdb_delete(the_tdb, kbuf);
2666 return 0;
2669 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2671 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2672 struct netr_SamInfo3 *info3)
2674 DOM_SID sid;
2675 fstring key_str, sid_string;
2676 struct winbind_cache *cache;
2678 /* dont clear cached U/SID and UG/SID entries when we want to logon
2679 * offline - gd */
2681 if (lp_winbind_offline_logon()) {
2682 return;
2685 if (!domain)
2686 return;
2688 cache = get_cache(domain);
2690 if (!cache->tdb) {
2691 return;
2694 sid_copy(&sid, info3->base.domain_sid);
2695 sid_append_rid(&sid, info3->base.rid);
2697 /* Clear U/SID cache entry */
2698 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2699 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2700 tdb_delete(cache->tdb, string_tdb_data(key_str));
2702 /* Clear UG/SID cache entry */
2703 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2704 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2705 tdb_delete(cache->tdb, string_tdb_data(key_str));
2707 /* Samba/winbindd never needs this. */
2708 netsamlogon_clear_cached_user(info3);
2711 bool wcache_invalidate_cache(void)
2713 struct winbindd_domain *domain;
2715 for (domain = domain_list(); domain; domain = domain->next) {
2716 struct winbind_cache *cache = get_cache(domain);
2718 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2719 "entries for %s\n", domain->name));
2720 if (cache) {
2721 if (cache->tdb) {
2722 tdb_traverse(cache->tdb, traverse_fn, NULL);
2723 } else {
2724 return false;
2728 return true;
2731 bool init_wcache(void)
2733 if (wcache == NULL) {
2734 wcache = SMB_XMALLOC_P(struct winbind_cache);
2735 ZERO_STRUCTP(wcache);
2738 if (wcache->tdb != NULL)
2739 return true;
2741 /* when working offline we must not clear the cache on restart */
2742 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2743 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2744 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2745 O_RDWR|O_CREAT, 0600);
2747 if (wcache->tdb == NULL) {
2748 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2749 return false;
2752 return true;
2755 /************************************************************************
2756 This is called by the parent to initialize the cache file.
2757 We don't need sophisticated locking here as we know we're the
2758 only opener.
2759 ************************************************************************/
2761 bool initialize_winbindd_cache(void)
2763 bool cache_bad = true;
2764 uint32 vers;
2766 if (!init_wcache()) {
2767 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2768 return false;
2771 /* Check version number. */
2772 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2773 vers == WINBINDD_CACHE_VERSION) {
2774 cache_bad = false;
2777 if (cache_bad) {
2778 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2779 "and re-creating with version number %d\n",
2780 WINBINDD_CACHE_VERSION ));
2782 tdb_close(wcache->tdb);
2783 wcache->tdb = NULL;
2785 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2786 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2787 cache_path("winbindd_cache.tdb"),
2788 strerror(errno) ));
2789 return false;
2791 if (!init_wcache()) {
2792 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2793 "init_wcache failed.\n"));
2794 return false;
2797 /* Write the version. */
2798 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2799 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2800 tdb_errorstr(wcache->tdb) ));
2801 return false;
2805 tdb_close(wcache->tdb);
2806 wcache->tdb = NULL;
2807 return true;
2810 void close_winbindd_cache(void)
2812 if (!wcache) {
2813 return;
2815 if (wcache->tdb) {
2816 tdb_close(wcache->tdb);
2817 wcache->tdb = NULL;
2821 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2822 char **domain_name, char **name,
2823 enum lsa_SidType *type)
2825 struct winbindd_domain *domain;
2826 NTSTATUS status;
2828 domain = find_lookup_domain_from_sid(sid);
2829 if (domain == NULL) {
2830 return false;
2832 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
2833 type);
2834 return NT_STATUS_IS_OK(status);
2837 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2838 const char *domain_name,
2839 const char *name,
2840 DOM_SID *sid,
2841 enum lsa_SidType *type)
2843 struct winbindd_domain *domain;
2844 NTSTATUS status;
2845 bool original_online_state;
2847 domain = find_lookup_domain_from_name(domain_name);
2848 if (domain == NULL) {
2849 return false;
2852 /* If we are doing a cached logon, temporarily set the domain
2853 offline so the cache won't expire the entry */
2855 original_online_state = domain->online;
2856 domain->online = false;
2857 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
2858 domain->online = original_online_state;
2860 return NT_STATUS_IS_OK(status);
2863 void cache_name2sid(struct winbindd_domain *domain,
2864 const char *domain_name, const char *name,
2865 enum lsa_SidType type, const DOM_SID *sid)
2867 refresh_sequence_number(domain, false);
2868 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2869 sid, type);
2873 * The original idea that this cache only contains centries has
2874 * been blurred - now other stuff gets put in here. Ensure we
2875 * ignore these things on cleanup.
2878 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2879 TDB_DATA dbuf, void *state)
2881 struct cache_entry *centry;
2883 if (is_non_centry_key(kbuf)) {
2884 return 0;
2887 centry = wcache_fetch_raw((char *)kbuf.dptr);
2888 if (!centry) {
2889 return 0;
2892 if (!NT_STATUS_IS_OK(centry->status)) {
2893 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2894 tdb_delete(the_tdb, kbuf);
2897 centry_free(centry);
2898 return 0;
2901 /* flush the cache */
2902 void wcache_flush_cache(void)
2904 if (!wcache)
2905 return;
2906 if (wcache->tdb) {
2907 tdb_close(wcache->tdb);
2908 wcache->tdb = NULL;
2910 if (!winbindd_use_cache()) {
2911 return;
2914 /* when working offline we must not clear the cache on restart */
2915 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2916 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2917 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2918 O_RDWR|O_CREAT, 0600);
2920 if (!wcache->tdb) {
2921 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2922 return;
2925 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2927 DEBUG(10,("wcache_flush_cache success\n"));
2930 /* Count cached creds */
2932 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2933 void *state)
2935 int *cred_count = (int*)state;
2937 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2938 (*cred_count)++;
2940 return 0;
2943 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2945 struct winbind_cache *cache = get_cache(domain);
2947 *count = 0;
2949 if (!cache->tdb) {
2950 return NT_STATUS_INTERNAL_DB_ERROR;
2953 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2955 return NT_STATUS_OK;
2958 struct cred_list {
2959 struct cred_list *prev, *next;
2960 TDB_DATA key;
2961 fstring name;
2962 time_t created;
2964 static struct cred_list *wcache_cred_list;
2966 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2967 void *state)
2969 struct cred_list *cred;
2971 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2973 cred = SMB_MALLOC_P(struct cred_list);
2974 if (cred == NULL) {
2975 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2976 return -1;
2979 ZERO_STRUCTP(cred);
2981 /* save a copy of the key */
2983 fstrcpy(cred->name, (const char *)kbuf.dptr);
2984 DLIST_ADD(wcache_cred_list, cred);
2987 return 0;
2990 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2992 struct winbind_cache *cache = get_cache(domain);
2993 NTSTATUS status;
2994 int ret;
2995 struct cred_list *cred, *oldest = NULL;
2997 if (!cache->tdb) {
2998 return NT_STATUS_INTERNAL_DB_ERROR;
3001 /* we possibly already have an entry */
3002 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3004 fstring key_str, tmp;
3006 DEBUG(11,("we already have an entry, deleting that\n"));
3008 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3010 tdb_delete(cache->tdb, string_tdb_data(key_str));
3012 return NT_STATUS_OK;
3015 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3016 if (ret == 0) {
3017 return NT_STATUS_OK;
3018 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3019 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3022 ZERO_STRUCTP(oldest);
3024 for (cred = wcache_cred_list; cred; cred = cred->next) {
3026 TDB_DATA data;
3027 time_t t;
3029 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3030 if (!data.dptr) {
3031 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3032 cred->name));
3033 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3034 goto done;
3037 t = IVAL(data.dptr, 0);
3038 SAFE_FREE(data.dptr);
3040 if (!oldest) {
3041 oldest = SMB_MALLOC_P(struct cred_list);
3042 if (oldest == NULL) {
3043 status = NT_STATUS_NO_MEMORY;
3044 goto done;
3047 fstrcpy(oldest->name, cred->name);
3048 oldest->created = t;
3049 continue;
3052 if (t < oldest->created) {
3053 fstrcpy(oldest->name, cred->name);
3054 oldest->created = t;
3058 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3059 status = NT_STATUS_OK;
3060 } else {
3061 status = NT_STATUS_UNSUCCESSFUL;
3063 done:
3064 SAFE_FREE(wcache_cred_list);
3065 SAFE_FREE(oldest);
3067 return status;
3070 /* Change the global online/offline state. */
3071 bool set_global_winbindd_state_offline(void)
3073 TDB_DATA data;
3075 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3077 /* Only go offline if someone has created
3078 the key "WINBINDD_OFFLINE" in the cache tdb. */
3080 if (wcache == NULL || wcache->tdb == NULL) {
3081 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3082 return false;
3085 if (!lp_winbind_offline_logon()) {
3086 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3087 return false;
3090 if (global_winbindd_offline_state) {
3091 /* Already offline. */
3092 return true;
3095 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3097 if (!data.dptr || data.dsize != 4) {
3098 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3099 SAFE_FREE(data.dptr);
3100 return false;
3101 } else {
3102 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3103 global_winbindd_offline_state = true;
3104 SAFE_FREE(data.dptr);
3105 return true;
3109 void set_global_winbindd_state_online(void)
3111 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3113 if (!lp_winbind_offline_logon()) {
3114 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3115 return;
3118 if (!global_winbindd_offline_state) {
3119 /* Already online. */
3120 return;
3122 global_winbindd_offline_state = false;
3124 if (!wcache->tdb) {
3125 return;
3128 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3129 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3132 bool get_global_winbindd_state_offline(void)
3134 return global_winbindd_offline_state;
3137 /***********************************************************************
3138 Validate functions for all possible cache tdb keys.
3139 ***********************************************************************/
3141 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3142 struct tdb_validation_status *state)
3144 struct cache_entry *centry;
3146 centry = SMB_XMALLOC_P(struct cache_entry);
3147 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3148 if (!centry->data) {
3149 SAFE_FREE(centry);
3150 return NULL;
3152 centry->len = data.dsize;
3153 centry->ofs = 0;
3155 if (centry->len < 8) {
3156 /* huh? corrupt cache? */
3157 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3158 centry_free(centry);
3159 state->bad_entry = true;
3160 state->success = false;
3161 return NULL;
3164 centry->status = NT_STATUS(centry_uint32(centry));
3165 centry->sequence_number = centry_uint32(centry);
3166 return centry;
3169 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3170 struct tdb_validation_status *state)
3172 if (dbuf.dsize != 8) {
3173 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3174 keystr, (unsigned int)dbuf.dsize ));
3175 state->bad_entry = true;
3176 return 1;
3178 return 0;
3181 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3182 struct tdb_validation_status *state)
3184 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3185 if (!centry) {
3186 return 1;
3189 (void)centry_uint32(centry);
3190 if (NT_STATUS_IS_OK(centry->status)) {
3191 DOM_SID sid;
3192 (void)centry_sid(centry, &sid);
3195 centry_free(centry);
3197 if (!(state->success)) {
3198 return 1;
3200 DEBUG(10,("validate_ns: %s ok\n", keystr));
3201 return 0;
3204 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3205 struct tdb_validation_status *state)
3207 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3208 if (!centry) {
3209 return 1;
3212 if (NT_STATUS_IS_OK(centry->status)) {
3213 (void)centry_uint32(centry);
3214 (void)centry_string(centry, mem_ctx);
3215 (void)centry_string(centry, mem_ctx);
3218 centry_free(centry);
3220 if (!(state->success)) {
3221 return 1;
3223 DEBUG(10,("validate_sn: %s ok\n", keystr));
3224 return 0;
3227 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3228 struct tdb_validation_status *state)
3230 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3231 DOM_SID sid;
3233 if (!centry) {
3234 return 1;
3237 (void)centry_string(centry, mem_ctx);
3238 (void)centry_string(centry, mem_ctx);
3239 (void)centry_string(centry, mem_ctx);
3240 (void)centry_string(centry, mem_ctx);
3241 (void)centry_uint32(centry);
3242 (void)centry_sid(centry, &sid);
3243 (void)centry_sid(centry, &sid);
3245 centry_free(centry);
3247 if (!(state->success)) {
3248 return 1;
3250 DEBUG(10,("validate_u: %s ok\n", keystr));
3251 return 0;
3254 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3255 struct tdb_validation_status *state)
3257 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3259 if (!centry) {
3260 return 1;
3263 (void)centry_nttime(centry);
3264 (void)centry_nttime(centry);
3265 (void)centry_uint16(centry);
3267 centry_free(centry);
3269 if (!(state->success)) {
3270 return 1;
3272 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3273 return 0;
3276 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3277 struct tdb_validation_status *state)
3279 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3281 if (!centry) {
3282 return 1;
3285 (void)centry_uint16(centry);
3286 (void)centry_uint16(centry);
3287 (void)centry_uint32(centry);
3288 (void)centry_nttime(centry);
3289 (void)centry_nttime(centry);
3291 centry_free(centry);
3293 if (!(state->success)) {
3294 return 1;
3296 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3297 return 0;
3300 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3301 struct tdb_validation_status *state)
3303 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3305 if (!centry) {
3306 return 1;
3309 (void)centry_time(centry);
3310 (void)centry_hash16(centry, mem_ctx);
3312 /* We only have 17 bytes more data in the salted cred case. */
3313 if (centry->len - centry->ofs == 17) {
3314 (void)centry_hash16(centry, mem_ctx);
3317 centry_free(centry);
3319 if (!(state->success)) {
3320 return 1;
3322 DEBUG(10,("validate_cred: %s ok\n", keystr));
3323 return 0;
3326 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3327 struct tdb_validation_status *state)
3329 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3330 int32 num_entries, i;
3332 if (!centry) {
3333 return 1;
3336 num_entries = (int32)centry_uint32(centry);
3338 for (i=0; i< num_entries; i++) {
3339 DOM_SID sid;
3340 (void)centry_string(centry, mem_ctx);
3341 (void)centry_string(centry, mem_ctx);
3342 (void)centry_string(centry, mem_ctx);
3343 (void)centry_string(centry, mem_ctx);
3344 (void)centry_sid(centry, &sid);
3345 (void)centry_sid(centry, &sid);
3348 centry_free(centry);
3350 if (!(state->success)) {
3351 return 1;
3353 DEBUG(10,("validate_ul: %s ok\n", keystr));
3354 return 0;
3357 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3358 struct tdb_validation_status *state)
3360 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3361 int32 num_entries, i;
3363 if (!centry) {
3364 return 1;
3367 num_entries = centry_uint32(centry);
3369 for (i=0; i< num_entries; i++) {
3370 (void)centry_string(centry, mem_ctx);
3371 (void)centry_string(centry, mem_ctx);
3372 (void)centry_uint32(centry);
3375 centry_free(centry);
3377 if (!(state->success)) {
3378 return 1;
3380 DEBUG(10,("validate_gl: %s ok\n", keystr));
3381 return 0;
3384 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3385 struct tdb_validation_status *state)
3387 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3388 int32 num_groups, i;
3390 if (!centry) {
3391 return 1;
3394 num_groups = centry_uint32(centry);
3396 for (i=0; i< num_groups; i++) {
3397 DOM_SID sid;
3398 centry_sid(centry, &sid);
3401 centry_free(centry);
3403 if (!(state->success)) {
3404 return 1;
3406 DEBUG(10,("validate_ug: %s ok\n", keystr));
3407 return 0;
3410 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3411 struct tdb_validation_status *state)
3413 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3414 int32 num_aliases, i;
3416 if (!centry) {
3417 return 1;
3420 num_aliases = centry_uint32(centry);
3422 for (i=0; i < num_aliases; i++) {
3423 (void)centry_uint32(centry);
3426 centry_free(centry);
3428 if (!(state->success)) {
3429 return 1;
3431 DEBUG(10,("validate_ua: %s ok\n", keystr));
3432 return 0;
3435 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3436 struct tdb_validation_status *state)
3438 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3439 int32 num_names, i;
3441 if (!centry) {
3442 return 1;
3445 num_names = centry_uint32(centry);
3447 for (i=0; i< num_names; i++) {
3448 DOM_SID sid;
3449 centry_sid(centry, &sid);
3450 (void)centry_string(centry, mem_ctx);
3451 (void)centry_uint32(centry);
3454 centry_free(centry);
3456 if (!(state->success)) {
3457 return 1;
3459 DEBUG(10,("validate_gm: %s ok\n", keystr));
3460 return 0;
3463 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3464 struct tdb_validation_status *state)
3466 /* Can't say anything about this other than must be nonzero. */
3467 if (dbuf.dsize == 0) {
3468 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3469 keystr));
3470 state->bad_entry = true;
3471 state->success = false;
3472 return 1;
3475 DEBUG(10,("validate_dr: %s ok\n", keystr));
3476 return 0;
3479 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3480 struct tdb_validation_status *state)
3482 /* Can't say anything about this other than must be nonzero. */
3483 if (dbuf.dsize == 0) {
3484 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3485 keystr));
3486 state->bad_entry = true;
3487 state->success = false;
3488 return 1;
3491 DEBUG(10,("validate_de: %s ok\n", keystr));
3492 return 0;
3495 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3496 TDB_DATA dbuf, struct tdb_validation_status *state)
3498 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3500 if (!centry) {
3501 return 1;
3504 (void)centry_string(centry, mem_ctx);
3505 (void)centry_string(centry, mem_ctx);
3506 (void)centry_string(centry, mem_ctx);
3507 (void)centry_uint32(centry);
3509 centry_free(centry);
3511 if (!(state->success)) {
3512 return 1;
3514 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3515 return 0;
3518 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3519 TDB_DATA dbuf,
3520 struct tdb_validation_status *state)
3522 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3524 if (!centry) {
3525 return 1;
3528 (void)centry_string( centry, mem_ctx );
3530 centry_free(centry);
3532 if (!(state->success)) {
3533 return 1;
3535 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3536 return 0;
3539 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3540 TDB_DATA dbuf,
3541 struct tdb_validation_status *state)
3543 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3545 if (!centry) {
3546 return 1;
3549 (void)centry_string( centry, mem_ctx );
3551 centry_free(centry);
3553 if (!(state->success)) {
3554 return 1;
3556 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3557 return 0;
3560 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3561 struct tdb_validation_status *state)
3563 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3564 int32 num_domains, i;
3566 if (!centry) {
3567 return 1;
3570 num_domains = centry_uint32(centry);
3572 for (i=0; i< num_domains; i++) {
3573 DOM_SID sid;
3574 (void)centry_string(centry, mem_ctx);
3575 (void)centry_string(centry, mem_ctx);
3576 (void)centry_sid(centry, &sid);
3579 centry_free(centry);
3581 if (!(state->success)) {
3582 return 1;
3584 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3585 return 0;
3588 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3589 TDB_DATA dbuf,
3590 struct tdb_validation_status *state)
3592 if (dbuf.dsize == 0) {
3593 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3594 "key %s (len ==0) ?\n", keystr));
3595 state->bad_entry = true;
3596 state->success = false;
3597 return 1;
3600 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3601 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3602 return 0;
3605 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3606 struct tdb_validation_status *state)
3608 if (dbuf.dsize != 4) {
3609 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3610 keystr, (unsigned int)dbuf.dsize ));
3611 state->bad_entry = true;
3612 state->success = false;
3613 return 1;
3615 DEBUG(10,("validate_offline: %s ok\n", keystr));
3616 return 0;
3619 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3620 struct tdb_validation_status *state)
3623 * Ignore validation for now. The proper way to do this is with a
3624 * checksum. Just pure parsing does not really catch much.
3626 return 0;
3629 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3630 struct tdb_validation_status *state)
3632 if (dbuf.dsize != 4) {
3633 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3634 "key %s (len %u != 4) ?\n",
3635 keystr, (unsigned int)dbuf.dsize));
3636 state->bad_entry = true;
3637 state->success = false;
3638 return 1;
3641 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3642 return 0;
3645 /***********************************************************************
3646 A list of all possible cache tdb keys with associated validation
3647 functions.
3648 ***********************************************************************/
3650 struct key_val_struct {
3651 const char *keyname;
3652 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3653 } key_val[] = {
3654 {"SEQNUM/", validate_seqnum},
3655 {"NS/", validate_ns},
3656 {"SN/", validate_sn},
3657 {"U/", validate_u},
3658 {"LOC_POL/", validate_loc_pol},
3659 {"PWD_POL/", validate_pwd_pol},
3660 {"CRED/", validate_cred},
3661 {"UL/", validate_ul},
3662 {"GL/", validate_gl},
3663 {"UG/", validate_ug},
3664 {"UA", validate_ua},
3665 {"GM/", validate_gm},
3666 {"DR/", validate_dr},
3667 {"DE/", validate_de},
3668 {"NSS/PWINFO/", validate_pwinfo},
3669 {"TRUSTDOMS/", validate_trustdoms},
3670 {"TRUSTDOMCACHE/", validate_trustdomcache},
3671 {"NSS/NA/", validate_nss_na},
3672 {"NSS/AN/", validate_nss_an},
3673 {"WINBINDD_OFFLINE", validate_offline},
3674 {"NDR/", validate_ndr},
3675 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3676 {NULL, NULL}
3679 /***********************************************************************
3680 Function to look at every entry in the tdb and validate it as far as
3681 possible.
3682 ***********************************************************************/
3684 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3686 int i;
3687 unsigned int max_key_len = 1024;
3688 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3690 /* Paranoia check. */
3691 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3692 max_key_len = 1024 * 1024;
3694 if (kbuf.dsize > max_key_len) {
3695 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3696 "(%u) > (%u)\n\n",
3697 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3698 return 1;
3701 for (i = 0; key_val[i].keyname; i++) {
3702 size_t namelen = strlen(key_val[i].keyname);
3703 if (kbuf.dsize >= namelen && (
3704 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3705 TALLOC_CTX *mem_ctx;
3706 char *keystr;
3707 int ret;
3709 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3710 if (!keystr) {
3711 return 1;
3713 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3714 keystr[kbuf.dsize] = '\0';
3716 mem_ctx = talloc_init("validate_ctx");
3717 if (!mem_ctx) {
3718 SAFE_FREE(keystr);
3719 return 1;
3722 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3723 v_state);
3725 SAFE_FREE(keystr);
3726 talloc_destroy(mem_ctx);
3727 return ret;
3731 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3732 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3733 DEBUG(0,("data :\n"));
3734 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3735 v_state->unknown_key = true;
3736 v_state->success = false;
3737 return 1; /* terminate. */
3740 static void validate_panic(const char *const why)
3742 DEBUG(0,("validating cache: would panic %s\n", why ));
3743 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3744 exit(47);
3747 /***********************************************************************
3748 Try and validate every entry in the winbindd cache. If we fail here,
3749 delete the cache tdb and return non-zero.
3750 ***********************************************************************/
3752 int winbindd_validate_cache(void)
3754 int ret = -1;
3755 const char *tdb_path = cache_path("winbindd_cache.tdb");
3756 TDB_CONTEXT *tdb = NULL;
3758 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3759 smb_panic_fn = validate_panic;
3762 tdb = tdb_open_log(tdb_path,
3763 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3764 ( lp_winbind_offline_logon()
3765 ? TDB_DEFAULT
3766 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3767 O_RDWR|O_CREAT,
3768 0600);
3769 if (!tdb) {
3770 DEBUG(0, ("winbindd_validate_cache: "
3771 "error opening/initializing tdb\n"));
3772 goto done;
3774 tdb_close(tdb);
3776 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3778 if (ret != 0) {
3779 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3780 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3781 unlink(tdb_path);
3784 done:
3785 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3786 smb_panic_fn = smb_panic;
3787 return ret;
3790 /***********************************************************************
3791 Try and validate every entry in the winbindd cache.
3792 ***********************************************************************/
3794 int winbindd_validate_cache_nobackup(void)
3796 int ret = -1;
3797 const char *tdb_path = cache_path("winbindd_cache.tdb");
3799 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3800 smb_panic_fn = validate_panic;
3803 if (wcache == NULL || wcache->tdb == NULL) {
3804 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3805 } else {
3806 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3809 if (ret != 0) {
3810 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3811 "successful.\n"));
3814 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3815 "function\n"));
3816 smb_panic_fn = smb_panic;
3817 return ret;
3820 bool winbindd_cache_validate_and_initialize(void)
3822 close_winbindd_cache();
3824 if (lp_winbind_offline_logon()) {
3825 if (winbindd_validate_cache() < 0) {
3826 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3827 "could be restored.\n"));
3831 return initialize_winbindd_cache();
3834 /*********************************************************************
3835 ********************************************************************/
3837 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3838 struct winbindd_tdc_domain **domains,
3839 size_t *num_domains )
3841 struct winbindd_tdc_domain *list = NULL;
3842 size_t idx;
3843 int i;
3844 bool set_only = false;
3846 /* don't allow duplicates */
3848 idx = *num_domains;
3849 list = *domains;
3851 for ( i=0; i< (*num_domains); i++ ) {
3852 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3853 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3854 new_dom->name));
3855 idx = i;
3856 set_only = true;
3858 break;
3862 if ( !set_only ) {
3863 if ( !*domains ) {
3864 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3865 idx = 0;
3866 } else {
3867 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3868 struct winbindd_tdc_domain,
3869 (*num_domains)+1);
3870 idx = *num_domains;
3873 ZERO_STRUCT( list[idx] );
3876 if ( !list )
3877 return false;
3879 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3880 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3882 if ( !is_null_sid( &new_dom->sid ) ) {
3883 sid_copy( &list[idx].sid, &new_dom->sid );
3884 } else {
3885 sid_copy(&list[idx].sid, &global_sid_NULL);
3888 if ( new_dom->domain_flags != 0x0 )
3889 list[idx].trust_flags = new_dom->domain_flags;
3891 if ( new_dom->domain_type != 0x0 )
3892 list[idx].trust_type = new_dom->domain_type;
3894 if ( new_dom->domain_trust_attribs != 0x0 )
3895 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3897 if ( !set_only ) {
3898 *domains = list;
3899 *num_domains = idx + 1;
3902 return true;
3905 /*********************************************************************
3906 ********************************************************************/
3908 static TDB_DATA make_tdc_key( const char *domain_name )
3910 char *keystr = NULL;
3911 TDB_DATA key = { NULL, 0 };
3913 if ( !domain_name ) {
3914 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3915 return key;
3918 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3919 return key;
3921 key = string_term_tdb_data(keystr);
3923 return key;
3926 /*********************************************************************
3927 ********************************************************************/
3929 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3930 size_t num_domains,
3931 unsigned char **buf )
3933 unsigned char *buffer = NULL;
3934 int len = 0;
3935 int buflen = 0;
3936 int i = 0;
3938 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3939 (int)num_domains));
3941 buflen = 0;
3943 again:
3944 len = 0;
3946 /* Store the number of array items first */
3947 len += tdb_pack( buffer+len, buflen-len, "d",
3948 num_domains );
3950 /* now pack each domain trust record */
3951 for ( i=0; i<num_domains; i++ ) {
3953 fstring tmp;
3955 if ( buflen > 0 ) {
3956 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3957 domains[i].domain_name,
3958 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3961 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3962 domains[i].domain_name,
3963 domains[i].dns_name,
3964 sid_to_fstring(tmp, &domains[i].sid),
3965 domains[i].trust_flags,
3966 domains[i].trust_attribs,
3967 domains[i].trust_type );
3970 if ( buflen < len ) {
3971 SAFE_FREE(buffer);
3972 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3973 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3974 buflen = -1;
3975 goto done;
3977 buflen = len;
3978 goto again;
3981 *buf = buffer;
3983 done:
3984 return buflen;
3987 /*********************************************************************
3988 ********************************************************************/
3990 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3991 struct winbindd_tdc_domain **domains )
3993 fstring domain_name, dns_name, sid_string;
3994 uint32 type, attribs, flags;
3995 int num_domains;
3996 int len = 0;
3997 int i;
3998 struct winbindd_tdc_domain *list = NULL;
4000 /* get the number of domains */
4001 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4002 if ( len == -1 ) {
4003 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4004 return 0;
4007 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4008 if ( !list ) {
4009 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4010 return 0;
4013 for ( i=0; i<num_domains; i++ ) {
4014 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4015 domain_name,
4016 dns_name,
4017 sid_string,
4018 &flags,
4019 &attribs,
4020 &type );
4022 if ( len == -1 ) {
4023 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4024 TALLOC_FREE( list );
4025 return 0;
4028 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4029 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4030 domain_name, dns_name, sid_string,
4031 flags, attribs, type));
4033 list[i].domain_name = talloc_strdup( list, domain_name );
4034 list[i].dns_name = talloc_strdup( list, dns_name );
4035 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4036 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4037 domain_name));
4039 list[i].trust_flags = flags;
4040 list[i].trust_attribs = attribs;
4041 list[i].trust_type = type;
4044 *domains = list;
4046 return num_domains;
4049 /*********************************************************************
4050 ********************************************************************/
4052 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4054 TDB_DATA key = make_tdc_key( lp_workgroup() );
4055 TDB_DATA data = { NULL, 0 };
4056 int ret;
4058 if ( !key.dptr )
4059 return false;
4061 /* See if we were asked to delete the cache entry */
4063 if ( !domains ) {
4064 ret = tdb_delete( wcache->tdb, key );
4065 goto done;
4068 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4070 if ( !data.dptr ) {
4071 ret = -1;
4072 goto done;
4075 ret = tdb_store( wcache->tdb, key, data, 0 );
4077 done:
4078 SAFE_FREE( data.dptr );
4079 SAFE_FREE( key.dptr );
4081 return ( ret != -1 );
4084 /*********************************************************************
4085 ********************************************************************/
4087 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4089 TDB_DATA key = make_tdc_key( lp_workgroup() );
4090 TDB_DATA data = { NULL, 0 };
4092 *domains = NULL;
4093 *num_domains = 0;
4095 if ( !key.dptr )
4096 return false;
4098 data = tdb_fetch( wcache->tdb, key );
4100 SAFE_FREE( key.dptr );
4102 if ( !data.dptr )
4103 return false;
4105 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4107 SAFE_FREE( data.dptr );
4109 if ( !*domains )
4110 return false;
4112 return true;
4115 /*********************************************************************
4116 ********************************************************************/
4118 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4120 struct winbindd_tdc_domain *dom_list = NULL;
4121 size_t num_domains = 0;
4122 bool ret = false;
4124 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4125 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4126 domain->name, domain->alt_name,
4127 sid_string_dbg(&domain->sid),
4128 domain->domain_flags,
4129 domain->domain_trust_attribs,
4130 domain->domain_type));
4132 if ( !init_wcache() ) {
4133 return false;
4136 /* fetch the list */
4138 wcache_tdc_fetch_list( &dom_list, &num_domains );
4140 /* add the new domain */
4142 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4143 goto done;
4146 /* pack the domain */
4148 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4149 goto done;
4152 /* Success */
4154 ret = true;
4155 done:
4156 TALLOC_FREE( dom_list );
4158 return ret;
4161 /*********************************************************************
4162 ********************************************************************/
4164 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4166 struct winbindd_tdc_domain *dom_list = NULL;
4167 size_t num_domains = 0;
4168 int i;
4169 struct winbindd_tdc_domain *d = NULL;
4171 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4173 if ( !init_wcache() ) {
4174 return false;
4177 /* fetch the list */
4179 wcache_tdc_fetch_list( &dom_list, &num_domains );
4181 for ( i=0; i<num_domains; i++ ) {
4182 if ( strequal(name, dom_list[i].domain_name) ||
4183 strequal(name, dom_list[i].dns_name) )
4185 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4186 name));
4188 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4189 if ( !d )
4190 break;
4192 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4193 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4194 sid_copy( &d->sid, &dom_list[i].sid );
4195 d->trust_flags = dom_list[i].trust_flags;
4196 d->trust_type = dom_list[i].trust_type;
4197 d->trust_attribs = dom_list[i].trust_attribs;
4199 break;
4203 TALLOC_FREE( dom_list );
4205 return d;
4209 /*********************************************************************
4210 ********************************************************************/
4212 void wcache_tdc_clear( void )
4214 if ( !init_wcache() )
4215 return;
4217 wcache_tdc_store_list( NULL, 0 );
4219 return;
4223 /*********************************************************************
4224 ********************************************************************/
4226 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4227 NTSTATUS status,
4228 const DOM_SID *user_sid,
4229 const char *homedir,
4230 const char *shell,
4231 const char *gecos,
4232 uint32 gid)
4234 struct cache_entry *centry;
4235 fstring tmp;
4237 if ( (centry = centry_start(domain, status)) == NULL )
4238 return;
4240 centry_put_string( centry, homedir );
4241 centry_put_string( centry, shell );
4242 centry_put_string( centry, gecos );
4243 centry_put_uint32( centry, gid );
4245 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4247 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4249 centry_free(centry);
4252 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4253 const DOM_SID *user_sid,
4254 TALLOC_CTX *ctx,
4255 ADS_STRUCT *ads, LDAPMessage *msg,
4256 const char **homedir, const char **shell,
4257 const char **gecos, gid_t *p_gid)
4259 struct winbind_cache *cache = get_cache(domain);
4260 struct cache_entry *centry = NULL;
4261 NTSTATUS nt_status;
4262 fstring tmp;
4264 if (!cache->tdb)
4265 goto do_query;
4267 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4268 sid_to_fstring(tmp, user_sid));
4270 if (!centry)
4271 goto do_query;
4273 *homedir = centry_string( centry, ctx );
4274 *shell = centry_string( centry, ctx );
4275 *gecos = centry_string( centry, ctx );
4276 *p_gid = centry_uint32( centry );
4278 centry_free(centry);
4280 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4281 sid_string_dbg(user_sid)));
4283 return NT_STATUS_OK;
4285 do_query:
4287 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4288 homedir, shell, gecos, p_gid );
4290 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4292 if ( NT_STATUS_IS_OK(nt_status) ) {
4293 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4294 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4295 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4296 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4298 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4299 *homedir, *shell, *gecos, *p_gid );
4302 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4303 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4304 domain->name ));
4305 set_domain_offline( domain );
4308 return nt_status;
4312 /* the cache backend methods are exposed via this structure */
4313 struct winbindd_methods cache_methods = {
4314 true,
4315 query_user_list,
4316 enum_dom_groups,
4317 enum_local_groups,
4318 name_to_sid,
4319 sid_to_name,
4320 rids_to_names,
4321 query_user,
4322 lookup_usergroups,
4323 lookup_useraliases,
4324 lookup_groupmem,
4325 sequence_number,
4326 lockout_policy,
4327 password_policy,
4328 trusted_domains
4331 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4332 uint32_t opnum, const DATA_BLOB *req,
4333 TDB_DATA *pkey)
4335 char *key;
4336 size_t keylen;
4338 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4339 if (key == NULL) {
4340 return false;
4342 keylen = talloc_get_size(key) - 1;
4344 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4345 if (key == NULL) {
4346 return false;
4348 memcpy(key + keylen, req->data, req->length);
4350 pkey->dptr = (uint8_t *)key;
4351 pkey->dsize = talloc_get_size(key);
4352 return true;
4355 static bool wcache_opnum_cacheable(uint32_t opnum)
4357 switch (opnum) {
4358 case NDR_WBINT_PING:
4359 case NDR_WBINT_QUERYSEQUENCENUMBER:
4360 case NDR_WBINT_ALLOCATEUID:
4361 case NDR_WBINT_ALLOCATEGID:
4362 return false;
4364 return true;
4367 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4368 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4370 TDB_DATA key, data;
4371 bool ret = false;
4373 if (!wcache_opnum_cacheable(opnum)) {
4374 return false;
4377 if (wcache->tdb == NULL) {
4378 return false;
4381 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4382 return false;
4384 data = tdb_fetch(wcache->tdb, key);
4385 TALLOC_FREE(key.dptr);
4387 if (data.dptr == NULL) {
4388 return false;
4390 if (data.dsize < 4) {
4391 goto fail;
4394 if (IS_DOMAIN_ONLINE(domain)) {
4395 uint32_t entry_seqnum, dom_seqnum, last_check;
4397 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4398 &last_check)) {
4399 goto fail;
4401 entry_seqnum = IVAL(data.dptr, 0);
4402 if (entry_seqnum != dom_seqnum) {
4403 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4404 (int)entry_seqnum));
4405 goto fail;
4409 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4410 data.dsize - 4);
4411 if (resp->data == NULL) {
4412 DEBUG(10, ("talloc failed\n"));
4413 goto fail;
4415 resp->length = data.dsize - 4;
4417 ret = true;
4418 fail:
4419 SAFE_FREE(data.dptr);
4420 return ret;
4423 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4424 const DATA_BLOB *req, const DATA_BLOB *resp)
4426 TDB_DATA key, data;
4427 uint32_t dom_seqnum, last_check;
4429 if (!wcache_opnum_cacheable(opnum)) {
4430 return;
4433 if (wcache->tdb == NULL) {
4434 return;
4437 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4438 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4439 domain->name));
4440 return;
4443 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4444 return;
4447 data.dsize = resp->length + 4;
4448 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4449 if (data.dptr == NULL) {
4450 goto done;
4453 SIVAL(data.dptr, 0, dom_seqnum);
4454 memcpy(data.dptr+4, resp->data, resp->length);
4456 tdb_store(wcache->tdb, key, data, 0);
4458 done:
4459 TALLOC_FREE(key.dptr);
4460 return;