s3:winbind: Factor out wcache_store_seqnum()
[Samba/aatanasov.git] / source3 / winbindd / winbindd_cache.c
blobcc1f095919c6e4a4fd82b48f3faeb23f17b4aa27
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"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 #define WINBINDD_CACHE_VERSION 1
35 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
37 extern struct winbindd_methods reconnect_methods;
38 #ifdef HAVE_ADS
39 extern struct winbindd_methods ads_methods;
40 #endif
41 extern struct winbindd_methods builtin_passdb_methods;
44 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
45 * Here are the list of entry types that are *not* stored
46 * as form struct cache_entry in the cache.
49 static const char *non_centry_keys[] = {
50 "SEQNUM/",
51 "DR/",
52 "DE/",
53 "WINBINDD_OFFLINE",
54 WINBINDD_CACHE_VERSION_KEYSTR,
55 NULL
58 /************************************************************************
59 Is this key a non-centry type ?
60 ************************************************************************/
62 static bool is_non_centry_key(TDB_DATA kbuf)
64 int i;
66 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
67 return false;
69 for (i = 0; non_centry_keys[i] != NULL; i++) {
70 size_t namelen = strlen(non_centry_keys[i]);
71 if (kbuf.dsize < namelen) {
72 continue;
74 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
75 return true;
78 return false;
81 /* Global online/offline state - False when online. winbindd starts up online
82 and sets this to true if the first query fails and there's an entry in
83 the cache tdb telling us to stay offline. */
85 static bool global_winbindd_offline_state;
87 struct winbind_cache {
88 TDB_CONTEXT *tdb;
91 struct cache_entry {
92 NTSTATUS status;
93 uint32 sequence_number;
94 uint8 *data;
95 uint32 len, ofs;
98 void (*smb_panic_fn)(const char *const why) = smb_panic;
100 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
102 static struct winbind_cache *wcache;
104 void winbindd_check_cache_size(time_t t)
106 static time_t last_check_time;
107 struct stat st;
109 if (last_check_time == (time_t)0)
110 last_check_time = t;
112 if (t - last_check_time < 60 && t - last_check_time > 0)
113 return;
115 if (wcache == NULL || wcache->tdb == NULL) {
116 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
117 return;
120 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
121 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
122 return;
125 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
126 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
127 (unsigned long)st.st_size,
128 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
129 wcache_flush_cache();
133 /* get the winbind_cache structure */
134 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
136 struct winbind_cache *ret = wcache;
138 /* We have to know what type of domain we are dealing with first. */
140 if (domain->internal) {
141 domain->backend = &builtin_passdb_methods;
142 domain->initialized = True;
144 if ( !domain->initialized ) {
145 init_dc_connection( domain );
149 OK. listen up becasue I'm only going to say this once.
150 We have the following scenarios to consider
151 (a) trusted AD domains on a Samba DC,
152 (b) trusted AD domains and we are joined to a non-kerberos domain
153 (c) trusted AD domains and we are joined to a kerberos (AD) domain
155 For (a) we can always contact the trusted domain using krb5
156 since we have the domain trust account password
158 For (b) we can only use RPC since we have no way of
159 getting a krb5 ticket in our own domain
161 For (c) we can always use krb5 since we have a kerberos trust
163 --jerry
166 if (!domain->backend) {
167 #ifdef HAVE_ADS
168 struct winbindd_domain *our_domain = domain;
170 /* find our domain first so we can figure out if we
171 are joined to a kerberized domain */
173 if ( !domain->primary )
174 our_domain = find_our_domain();
176 if ((our_domain->active_directory || IS_DC)
177 && domain->active_directory
178 && !lp_winbind_rpc_only()) {
179 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
180 domain->backend = &ads_methods;
181 } else {
182 #endif /* HAVE_ADS */
183 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
184 domain->backend = &reconnect_methods;
185 #ifdef HAVE_ADS
187 #endif /* HAVE_ADS */
190 if (ret)
191 return ret;
193 ret = SMB_XMALLOC_P(struct winbind_cache);
194 ZERO_STRUCTP(ret);
196 wcache = ret;
197 wcache_flush_cache();
199 return ret;
203 free a centry structure
205 static void centry_free(struct cache_entry *centry)
207 if (!centry)
208 return;
209 SAFE_FREE(centry->data);
210 free(centry);
213 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
215 if (centry->len - centry->ofs < nbytes) {
216 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
217 (unsigned int)nbytes,
218 centry->len - centry->ofs));
219 return false;
221 return true;
225 pull a uint32 from a cache entry
227 static uint32 centry_uint32(struct cache_entry *centry)
229 uint32 ret;
231 if (!centry_check_bytes(centry, 4)) {
232 smb_panic_fn("centry_uint32");
234 ret = IVAL(centry->data, centry->ofs);
235 centry->ofs += 4;
236 return ret;
240 pull a uint16 from a cache entry
242 static uint16 centry_uint16(struct cache_entry *centry)
244 uint16 ret;
245 if (!centry_check_bytes(centry, 2)) {
246 smb_panic_fn("centry_uint16");
248 ret = CVAL(centry->data, centry->ofs);
249 centry->ofs += 2;
250 return ret;
254 pull a uint8 from a cache entry
256 static uint8 centry_uint8(struct cache_entry *centry)
258 uint8 ret;
259 if (!centry_check_bytes(centry, 1)) {
260 smb_panic_fn("centry_uint8");
262 ret = CVAL(centry->data, centry->ofs);
263 centry->ofs += 1;
264 return ret;
268 pull a NTTIME from a cache entry
270 static NTTIME centry_nttime(struct cache_entry *centry)
272 NTTIME ret;
273 if (!centry_check_bytes(centry, 8)) {
274 smb_panic_fn("centry_nttime");
276 ret = IVAL(centry->data, centry->ofs);
277 centry->ofs += 4;
278 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
279 centry->ofs += 4;
280 return ret;
284 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
286 static time_t centry_time(struct cache_entry *centry)
288 return (time_t)centry_nttime(centry);
291 /* pull a string from a cache entry, using the supplied
292 talloc context
294 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
296 uint32 len;
297 char *ret;
299 len = centry_uint8(centry);
301 if (len == 0xFF) {
302 /* a deliberate NULL string */
303 return NULL;
306 if (!centry_check_bytes(centry, (size_t)len)) {
307 smb_panic_fn("centry_string");
310 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
311 if (!ret) {
312 smb_panic_fn("centry_string out of memory\n");
314 memcpy(ret,centry->data + centry->ofs, len);
315 ret[len] = 0;
316 centry->ofs += len;
317 return ret;
320 /* pull a hash16 from a cache entry, using the supplied
321 talloc context
323 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
325 uint32 len;
326 char *ret;
328 len = centry_uint8(centry);
330 if (len != 16) {
331 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
332 len ));
333 return NULL;
336 if (!centry_check_bytes(centry, 16)) {
337 return NULL;
340 ret = TALLOC_ARRAY(mem_ctx, char, 16);
341 if (!ret) {
342 smb_panic_fn("centry_hash out of memory\n");
344 memcpy(ret,centry->data + centry->ofs, 16);
345 centry->ofs += 16;
346 return ret;
349 /* pull a sid from a cache entry, using the supplied
350 talloc context
352 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
354 char *sid_string;
355 bool ret;
357 sid_string = centry_string(centry, talloc_tos());
358 if (sid_string == NULL) {
359 return false;
361 ret = string_to_sid(sid, sid_string);
362 TALLOC_FREE(sid_string);
363 return ret;
368 pull a NTSTATUS from a cache entry
370 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
372 NTSTATUS status;
374 status = NT_STATUS(centry_uint32(centry));
375 return status;
379 /* the server is considered down if it can't give us a sequence number */
380 static bool wcache_server_down(struct winbindd_domain *domain)
382 bool ret;
384 if (!wcache->tdb)
385 return false;
387 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
389 if (ret)
390 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
391 domain->name ));
392 return ret;
395 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
396 uint32_t *last_seq_check)
398 char *key;
399 TDB_DATA data;
401 if (wcache->tdb == NULL) {
402 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
403 return false;
406 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
407 if (key == NULL) {
408 DEBUG(10, ("talloc failed\n"));
409 return false;
412 data = tdb_fetch_bystring(wcache->tdb, key);
413 TALLOC_FREE(key);
415 if (data.dptr == NULL) {
416 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
417 domain_name));
418 return false;
420 if (data.dsize != 8) {
421 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
422 (int)data.dsize));
423 SAFE_FREE(data.dptr);
424 return false;
427 *seqnum = IVAL(data.dptr, 0);
428 *last_seq_check = IVAL(data.dptr, 4);
429 SAFE_FREE(data.dptr);
431 return true;
434 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
436 uint32 last_check, time_diff;
438 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
439 &last_check)) {
440 return NT_STATUS_UNSUCCESSFUL;
442 domain->last_seq_check = last_check;
444 /* have we expired? */
446 time_diff = now - domain->last_seq_check;
447 if ( time_diff > lp_winbind_cache_time() ) {
448 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
449 domain->name, domain->sequence_number,
450 (uint32)domain->last_seq_check));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
458 return NT_STATUS_OK;
461 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
462 time_t last_seq_check)
464 char *key_str;
465 uint8_t buf[8];
466 int ret;
468 if (wcache->tdb == NULL) {
469 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
470 return false;
473 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
474 if (key_str == NULL) {
475 DEBUG(10, ("talloc_asprintf failed\n"));
476 return false;
479 SIVAL(buf, 0, seqnum);
480 SIVAL(buf, 4, last_seq_check);
482 ret = tdb_store_bystring(wcache->tdb, key_str,
483 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
484 TALLOC_FREE(key_str);
485 if (ret == -1) {
486 DEBUG(10, ("tdb_store_bystring failed: %s\n",
487 tdb_errorstr(wcache->tdb)));
488 TALLOC_FREE(key_str);
489 return false;
492 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
493 domain_name, seqnum, (unsigned)last_seq_check));
495 return true;
498 static bool store_cache_seqnum( struct winbindd_domain *domain )
500 return wcache_store_seqnum(domain->name, domain->sequence_number,
501 domain->last_seq_check);
505 refresh the domain sequence number. If force is true
506 then always refresh it, no matter how recently we fetched it
509 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
511 NTSTATUS status;
512 unsigned time_diff;
513 time_t t = time(NULL);
514 unsigned cache_time = lp_winbind_cache_time();
516 if ( IS_DOMAIN_OFFLINE(domain) ) {
517 return;
520 get_cache( domain );
522 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
523 /* trying to reconnect is expensive, don't do it too often */
524 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
525 cache_time *= 8;
527 #endif
529 time_diff = t - domain->last_seq_check;
531 /* see if we have to refetch the domain sequence number */
532 if (!force && (time_diff < cache_time) &&
533 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
534 NT_STATUS_IS_OK(domain->last_status)) {
535 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
536 goto done;
539 /* try to get the sequence number from the tdb cache first */
540 /* this will update the timestamp as well */
542 status = fetch_cache_seqnum( domain, t );
543 if (NT_STATUS_IS_OK(status) &&
544 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
545 NT_STATUS_IS_OK(domain->last_status)) {
546 goto done;
549 /* important! make sure that we know if this is a native
550 mode domain or not. And that we can contact it. */
552 if ( winbindd_can_contact_domain( domain ) ) {
553 status = domain->backend->sequence_number(domain,
554 &domain->sequence_number);
555 } else {
556 /* just use the current time */
557 status = NT_STATUS_OK;
558 domain->sequence_number = time(NULL);
562 /* the above call could have set our domain->backend to NULL when
563 * coming from offline to online mode, make sure to reinitialize the
564 * backend - Guenther */
565 get_cache( domain );
567 if (!NT_STATUS_IS_OK(status)) {
568 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
569 domain->sequence_number = DOM_SEQUENCE_NONE;
572 domain->last_status = status;
573 domain->last_seq_check = time(NULL);
575 /* save the new sequence number in the cache */
576 store_cache_seqnum( domain );
578 done:
579 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
580 domain->name, domain->sequence_number));
582 return;
586 decide if a cache entry has expired
588 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
590 /* If we've been told to be offline - stay in that state... */
591 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
592 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
593 keystr, domain->name ));
594 return false;
597 /* when the domain is offline return the cached entry.
598 * This deals with transient offline states... */
600 if (!domain->online) {
601 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
602 keystr, domain->name ));
603 return false;
606 /* if the server is OK and our cache entry came from when it was down then
607 the entry is invalid */
608 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
609 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
610 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
611 keystr, domain->name ));
612 return true;
615 /* if the server is down or the cache entry is not older than the
616 current sequence number then it is OK */
617 if (wcache_server_down(domain) ||
618 centry->sequence_number == domain->sequence_number) {
619 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
620 keystr, domain->name ));
621 return false;
624 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
625 keystr, domain->name ));
627 /* it's expired */
628 return true;
631 static struct cache_entry *wcache_fetch_raw(char *kstr)
633 TDB_DATA data;
634 struct cache_entry *centry;
635 TDB_DATA key;
637 key = string_tdb_data(kstr);
638 data = tdb_fetch(wcache->tdb, key);
639 if (!data.dptr) {
640 /* a cache miss */
641 return NULL;
644 centry = SMB_XMALLOC_P(struct cache_entry);
645 centry->data = (unsigned char *)data.dptr;
646 centry->len = data.dsize;
647 centry->ofs = 0;
649 if (centry->len < 8) {
650 /* huh? corrupt cache? */
651 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
652 centry_free(centry);
653 return NULL;
656 centry->status = centry_ntstatus(centry);
657 centry->sequence_number = centry_uint32(centry);
659 return centry;
663 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
664 number and return status
666 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
667 struct winbindd_domain *domain,
668 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
669 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
670 struct winbindd_domain *domain,
671 const char *format, ...)
673 va_list ap;
674 char *kstr;
675 struct cache_entry *centry;
677 if (!winbindd_use_cache()) {
678 return NULL;
681 refresh_sequence_number(domain, false);
683 va_start(ap, format);
684 smb_xvasprintf(&kstr, format, ap);
685 va_end(ap);
687 centry = wcache_fetch_raw(kstr);
688 if (centry == NULL) {
689 free(kstr);
690 return NULL;
693 if (centry_expired(domain, kstr, centry)) {
695 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
696 kstr, domain->name ));
698 centry_free(centry);
699 free(kstr);
700 return NULL;
703 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
704 kstr, domain->name ));
706 free(kstr);
707 return centry;
710 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
711 static void wcache_delete(const char *format, ...)
713 va_list ap;
714 char *kstr;
715 TDB_DATA key;
717 va_start(ap, format);
718 smb_xvasprintf(&kstr, format, ap);
719 va_end(ap);
721 key = string_tdb_data(kstr);
723 tdb_delete(wcache->tdb, key);
724 free(kstr);
728 make sure we have at least len bytes available in a centry
730 static void centry_expand(struct cache_entry *centry, uint32 len)
732 if (centry->len - centry->ofs >= len)
733 return;
734 centry->len *= 2;
735 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
736 centry->len);
737 if (!centry->data) {
738 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
739 smb_panic_fn("out of memory in centry_expand");
744 push a uint32 into a centry
746 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
748 centry_expand(centry, 4);
749 SIVAL(centry->data, centry->ofs, v);
750 centry->ofs += 4;
754 push a uint16 into a centry
756 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
758 centry_expand(centry, 2);
759 SIVAL(centry->data, centry->ofs, v);
760 centry->ofs += 2;
764 push a uint8 into a centry
766 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
768 centry_expand(centry, 1);
769 SCVAL(centry->data, centry->ofs, v);
770 centry->ofs += 1;
774 push a string into a centry
776 static void centry_put_string(struct cache_entry *centry, const char *s)
778 int len;
780 if (!s) {
781 /* null strings are marked as len 0xFFFF */
782 centry_put_uint8(centry, 0xFF);
783 return;
786 len = strlen(s);
787 /* can't handle more than 254 char strings. Truncating is probably best */
788 if (len > 254) {
789 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
790 len = 254;
792 centry_put_uint8(centry, len);
793 centry_expand(centry, len);
794 memcpy(centry->data + centry->ofs, s, len);
795 centry->ofs += len;
799 push a 16 byte hash into a centry - treat as 16 byte string.
801 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
803 centry_put_uint8(centry, 16);
804 centry_expand(centry, 16);
805 memcpy(centry->data + centry->ofs, val, 16);
806 centry->ofs += 16;
809 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
811 fstring sid_string;
812 centry_put_string(centry, sid_to_fstring(sid_string, sid));
817 put NTSTATUS into a centry
819 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
821 uint32 status_value = NT_STATUS_V(status);
822 centry_put_uint32(centry, status_value);
827 push a NTTIME into a centry
829 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
831 centry_expand(centry, 8);
832 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
833 centry->ofs += 4;
834 SIVAL(centry->data, centry->ofs, nt >> 32);
835 centry->ofs += 4;
839 push a time_t into a centry - use a 64 bit size.
840 NTTIME here is being used as a convenient 64-bit size.
842 static void centry_put_time(struct cache_entry *centry, time_t t)
844 NTTIME nt = (NTTIME)t;
845 centry_put_nttime(centry, nt);
849 start a centry for output. When finished, call centry_end()
851 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
853 struct cache_entry *centry;
855 if (!wcache->tdb)
856 return NULL;
858 centry = SMB_XMALLOC_P(struct cache_entry);
860 centry->len = 8192; /* reasonable default */
861 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
862 centry->ofs = 0;
863 centry->sequence_number = domain->sequence_number;
864 centry_put_ntstatus(centry, status);
865 centry_put_uint32(centry, centry->sequence_number);
866 return centry;
870 finish a centry and write it to the tdb
872 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
873 static void centry_end(struct cache_entry *centry, const char *format, ...)
875 va_list ap;
876 char *kstr;
877 TDB_DATA key, data;
879 if (!winbindd_use_cache()) {
880 return;
883 va_start(ap, format);
884 smb_xvasprintf(&kstr, format, ap);
885 va_end(ap);
887 key = string_tdb_data(kstr);
888 data.dptr = centry->data;
889 data.dsize = centry->ofs;
891 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
892 free(kstr);
895 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
896 NTSTATUS status, const char *domain_name,
897 const char *name, const DOM_SID *sid,
898 enum lsa_SidType type)
900 struct cache_entry *centry;
901 fstring uname;
903 centry = centry_start(domain, status);
904 if (!centry)
905 return;
906 centry_put_uint32(centry, type);
907 centry_put_sid(centry, sid);
908 fstrcpy(uname, name);
909 strupper_m(uname);
910 centry_end(centry, "NS/%s/%s", domain_name, uname);
911 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
912 uname, sid_string_dbg(sid), nt_errstr(status)));
913 centry_free(centry);
916 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
917 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
919 struct cache_entry *centry;
920 fstring sid_string;
922 centry = centry_start(domain, status);
923 if (!centry)
924 return;
926 if (NT_STATUS_IS_OK(status)) {
927 centry_put_uint32(centry, type);
928 centry_put_string(centry, domain_name);
929 centry_put_string(centry, name);
932 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
933 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
934 name, nt_errstr(status)));
935 centry_free(centry);
939 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
940 struct wbint_userinfo *info)
942 struct cache_entry *centry;
943 fstring sid_string;
945 if (is_null_sid(&info->user_sid)) {
946 return;
949 centry = centry_start(domain, status);
950 if (!centry)
951 return;
952 centry_put_string(centry, info->acct_name);
953 centry_put_string(centry, info->full_name);
954 centry_put_string(centry, info->homedir);
955 centry_put_string(centry, info->shell);
956 centry_put_uint32(centry, info->primary_gid);
957 centry_put_sid(centry, &info->user_sid);
958 centry_put_sid(centry, &info->group_sid);
959 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
960 &info->user_sid));
961 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
962 centry_free(centry);
965 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
966 NTSTATUS status,
967 struct samr_DomInfo12 *lockout_policy)
969 struct cache_entry *centry;
971 centry = centry_start(domain, status);
972 if (!centry)
973 return;
975 centry_put_nttime(centry, lockout_policy->lockout_duration);
976 centry_put_nttime(centry, lockout_policy->lockout_window);
977 centry_put_uint16(centry, lockout_policy->lockout_threshold);
979 centry_end(centry, "LOC_POL/%s", domain->name);
981 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
983 centry_free(centry);
988 static void wcache_save_password_policy(struct winbindd_domain *domain,
989 NTSTATUS status,
990 struct samr_DomInfo1 *policy)
992 struct cache_entry *centry;
994 centry = centry_start(domain, status);
995 if (!centry)
996 return;
998 centry_put_uint16(centry, policy->min_password_length);
999 centry_put_uint16(centry, policy->password_history_length);
1000 centry_put_uint32(centry, policy->password_properties);
1001 centry_put_nttime(centry, policy->max_password_age);
1002 centry_put_nttime(centry, policy->min_password_age);
1004 centry_end(centry, "PWD_POL/%s", domain->name);
1006 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1008 centry_free(centry);
1011 /***************************************************************************
1012 ***************************************************************************/
1014 static void wcache_save_username_alias(struct winbindd_domain *domain,
1015 NTSTATUS status,
1016 const char *name, const char *alias)
1018 struct cache_entry *centry;
1019 fstring uname;
1021 if ( (centry = centry_start(domain, status)) == NULL )
1022 return;
1024 centry_put_string( centry, alias );
1026 fstrcpy(uname, name);
1027 strupper_m(uname);
1028 centry_end(centry, "NSS/NA/%s", uname);
1030 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1032 centry_free(centry);
1035 static void wcache_save_alias_username(struct winbindd_domain *domain,
1036 NTSTATUS status,
1037 const char *alias, const char *name)
1039 struct cache_entry *centry;
1040 fstring uname;
1042 if ( (centry = centry_start(domain, status)) == NULL )
1043 return;
1045 centry_put_string( centry, name );
1047 fstrcpy(uname, alias);
1048 strupper_m(uname);
1049 centry_end(centry, "NSS/AN/%s", uname);
1051 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1053 centry_free(centry);
1056 /***************************************************************************
1057 ***************************************************************************/
1059 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1060 struct winbindd_domain *domain,
1061 const char *name, char **alias )
1063 struct winbind_cache *cache = get_cache(domain);
1064 struct cache_entry *centry = NULL;
1065 NTSTATUS status;
1066 char *upper_name;
1068 if ( domain->internal )
1069 return NT_STATUS_NOT_SUPPORTED;
1071 if (!cache->tdb)
1072 goto do_query;
1074 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1075 return NT_STATUS_NO_MEMORY;
1076 strupper_m(upper_name);
1078 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1080 SAFE_FREE( upper_name );
1082 if (!centry)
1083 goto do_query;
1085 status = centry->status;
1087 if (!NT_STATUS_IS_OK(status)) {
1088 centry_free(centry);
1089 return status;
1092 *alias = centry_string( centry, mem_ctx );
1094 centry_free(centry);
1096 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1097 name, *alias ? *alias : "(none)"));
1099 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1101 do_query:
1103 /* If its not in cache and we are offline, then fail */
1105 if ( get_global_winbindd_state_offline() || !domain->online ) {
1106 DEBUG(8,("resolve_username_to_alias: rejecting query "
1107 "in offline mode\n"));
1108 return NT_STATUS_NOT_FOUND;
1111 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1113 if ( NT_STATUS_IS_OK( status ) ) {
1114 wcache_save_username_alias(domain, status, name, *alias);
1117 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1118 wcache_save_username_alias(domain, status, name, "(NULL)");
1121 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1122 nt_errstr(status)));
1124 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1125 set_domain_offline( domain );
1128 return status;
1131 /***************************************************************************
1132 ***************************************************************************/
1134 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1135 struct winbindd_domain *domain,
1136 const char *alias, char **name )
1138 struct winbind_cache *cache = get_cache(domain);
1139 struct cache_entry *centry = NULL;
1140 NTSTATUS status;
1141 char *upper_name;
1143 if ( domain->internal )
1144 return NT_STATUS_NOT_SUPPORTED;
1146 if (!cache->tdb)
1147 goto do_query;
1149 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1150 return NT_STATUS_NO_MEMORY;
1151 strupper_m(upper_name);
1153 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1155 SAFE_FREE( upper_name );
1157 if (!centry)
1158 goto do_query;
1160 status = centry->status;
1162 if (!NT_STATUS_IS_OK(status)) {
1163 centry_free(centry);
1164 return status;
1167 *name = centry_string( centry, mem_ctx );
1169 centry_free(centry);
1171 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1172 alias, *name ? *name : "(none)"));
1174 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1176 do_query:
1178 /* If its not in cache and we are offline, then fail */
1180 if ( get_global_winbindd_state_offline() || !domain->online ) {
1181 DEBUG(8,("resolve_alias_to_username: rejecting query "
1182 "in offline mode\n"));
1183 return NT_STATUS_NOT_FOUND;
1186 /* an alias cannot contain a domain prefix or '@' */
1188 if (strchr(alias, '\\') || strchr(alias, '@')) {
1189 DEBUG(10,("resolve_alias_to_username: skipping fully "
1190 "qualified name %s\n", alias));
1191 return NT_STATUS_OBJECT_NAME_INVALID;
1194 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1196 if ( NT_STATUS_IS_OK( status ) ) {
1197 wcache_save_alias_username( domain, status, alias, *name );
1200 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1201 wcache_save_alias_username(domain, status, alias, "(NULL)");
1204 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1205 nt_errstr(status)));
1207 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1208 set_domain_offline( domain );
1211 return status;
1214 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1216 struct winbind_cache *cache = get_cache(domain);
1217 TDB_DATA data;
1218 fstring key_str, tmp;
1219 uint32 rid;
1221 if (!cache->tdb) {
1222 return NT_STATUS_INTERNAL_DB_ERROR;
1225 if (is_null_sid(sid)) {
1226 return NT_STATUS_INVALID_SID;
1229 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1230 return NT_STATUS_INVALID_SID;
1233 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1235 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1236 if (!data.dptr) {
1237 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1240 SAFE_FREE(data.dptr);
1241 return NT_STATUS_OK;
1244 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1245 as new salted ones. */
1247 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1248 TALLOC_CTX *mem_ctx,
1249 const DOM_SID *sid,
1250 const uint8 **cached_nt_pass,
1251 const uint8 **cached_salt)
1253 struct winbind_cache *cache = get_cache(domain);
1254 struct cache_entry *centry = NULL;
1255 NTSTATUS status;
1256 time_t t;
1257 uint32 rid;
1258 fstring tmp;
1260 if (!cache->tdb) {
1261 return NT_STATUS_INTERNAL_DB_ERROR;
1264 if (is_null_sid(sid)) {
1265 return NT_STATUS_INVALID_SID;
1268 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1269 return NT_STATUS_INVALID_SID;
1272 /* Try and get a salted cred first. If we can't
1273 fall back to an unsalted cred. */
1275 centry = wcache_fetch(cache, domain, "CRED/%s",
1276 sid_to_fstring(tmp, sid));
1277 if (!centry) {
1278 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1279 sid_string_dbg(sid)));
1280 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1283 t = centry_time(centry);
1285 /* In the salted case this isn't actually the nt_hash itself,
1286 but the MD5 of the salt + nt_hash. Let the caller
1287 sort this out. It can tell as we only return the cached_salt
1288 if we are returning a salted cred. */
1290 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1291 if (*cached_nt_pass == NULL) {
1292 fstring sidstr;
1294 sid_to_fstring(sidstr, sid);
1296 /* Bad (old) cred cache. Delete and pretend we
1297 don't have it. */
1298 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1299 sidstr));
1300 wcache_delete("CRED/%s", sidstr);
1301 centry_free(centry);
1302 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1305 /* We only have 17 bytes more data in the salted cred case. */
1306 if (centry->len - centry->ofs == 17) {
1307 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1308 } else {
1309 *cached_salt = NULL;
1312 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1313 if (*cached_salt) {
1314 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1317 status = centry->status;
1319 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1320 sid_string_dbg(sid), nt_errstr(status) ));
1322 centry_free(centry);
1323 return status;
1326 /* Store creds for a SID - only writes out new salted ones. */
1328 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1329 TALLOC_CTX *mem_ctx,
1330 const DOM_SID *sid,
1331 const uint8 nt_pass[NT_HASH_LEN])
1333 struct cache_entry *centry;
1334 fstring sid_string;
1335 uint32 rid;
1336 uint8 cred_salt[NT_HASH_LEN];
1337 uint8 salted_hash[NT_HASH_LEN];
1339 if (is_null_sid(sid)) {
1340 return NT_STATUS_INVALID_SID;
1343 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1344 return NT_STATUS_INVALID_SID;
1347 centry = centry_start(domain, NT_STATUS_OK);
1348 if (!centry) {
1349 return NT_STATUS_INTERNAL_DB_ERROR;
1352 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1354 centry_put_time(centry, time(NULL));
1356 /* Create a salt and then salt the hash. */
1357 generate_random_buffer(cred_salt, NT_HASH_LEN);
1358 E_md5hash(cred_salt, nt_pass, salted_hash);
1360 centry_put_hash16(centry, salted_hash);
1361 centry_put_hash16(centry, cred_salt);
1362 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1364 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1366 centry_free(centry);
1368 return NT_STATUS_OK;
1372 /* Query display info. This is the basic user list fn */
1373 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1374 TALLOC_CTX *mem_ctx,
1375 uint32 *num_entries,
1376 struct wbint_userinfo **info)
1378 struct winbind_cache *cache = get_cache(domain);
1379 struct cache_entry *centry = NULL;
1380 NTSTATUS status;
1381 unsigned int i, retry;
1383 if (!cache->tdb)
1384 goto do_query;
1386 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1387 if (!centry)
1388 goto do_query;
1390 *num_entries = centry_uint32(centry);
1392 if (*num_entries == 0)
1393 goto do_cached;
1395 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1396 if (! (*info)) {
1397 smb_panic_fn("query_user_list out of memory");
1399 for (i=0; i<(*num_entries); i++) {
1400 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1401 (*info)[i].full_name = centry_string(centry, mem_ctx);
1402 (*info)[i].homedir = centry_string(centry, mem_ctx);
1403 (*info)[i].shell = centry_string(centry, mem_ctx);
1404 centry_sid(centry, &(*info)[i].user_sid);
1405 centry_sid(centry, &(*info)[i].group_sid);
1408 do_cached:
1409 status = centry->status;
1411 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1412 domain->name, nt_errstr(status) ));
1414 centry_free(centry);
1415 return status;
1417 do_query:
1418 *num_entries = 0;
1419 *info = NULL;
1421 /* Return status value returned by seq number check */
1423 if (!NT_STATUS_IS_OK(domain->last_status))
1424 return domain->last_status;
1426 /* Put the query_user_list() in a retry loop. There appears to be
1427 * some bug either with Windows 2000 or Samba's handling of large
1428 * rpc replies. This manifests itself as sudden disconnection
1429 * at a random point in the enumeration of a large (60k) user list.
1430 * The retry loop simply tries the operation again. )-: It's not
1431 * pretty but an acceptable workaround until we work out what the
1432 * real problem is. */
1434 retry = 0;
1435 do {
1437 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1438 domain->name ));
1440 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1441 if (!NT_STATUS_IS_OK(status)) {
1442 DEBUG(3, ("query_user_list: returned 0x%08x, "
1443 "retrying\n", NT_STATUS_V(status)));
1445 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1446 DEBUG(3, ("query_user_list: flushing "
1447 "connection cache\n"));
1448 invalidate_cm_connection(&domain->conn);
1451 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1452 (retry++ < 5));
1454 /* and save it */
1455 refresh_sequence_number(domain, false);
1456 centry = centry_start(domain, status);
1457 if (!centry)
1458 goto skip_save;
1459 centry_put_uint32(centry, *num_entries);
1460 for (i=0; i<(*num_entries); i++) {
1461 centry_put_string(centry, (*info)[i].acct_name);
1462 centry_put_string(centry, (*info)[i].full_name);
1463 centry_put_string(centry, (*info)[i].homedir);
1464 centry_put_string(centry, (*info)[i].shell);
1465 centry_put_sid(centry, &(*info)[i].user_sid);
1466 centry_put_sid(centry, &(*info)[i].group_sid);
1467 if (domain->backend && domain->backend->consistent) {
1468 /* when the backend is consistent we can pre-prime some mappings */
1469 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1470 domain->name,
1471 (*info)[i].acct_name,
1472 &(*info)[i].user_sid,
1473 SID_NAME_USER);
1474 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1475 &(*info)[i].user_sid,
1476 domain->name,
1477 (*info)[i].acct_name,
1478 SID_NAME_USER);
1479 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1482 centry_end(centry, "UL/%s", domain->name);
1483 centry_free(centry);
1485 skip_save:
1486 return status;
1489 /* list all domain groups */
1490 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1491 TALLOC_CTX *mem_ctx,
1492 uint32 *num_entries,
1493 struct acct_info **info)
1495 struct winbind_cache *cache = get_cache(domain);
1496 struct cache_entry *centry = NULL;
1497 NTSTATUS status;
1498 unsigned int i;
1500 if (!cache->tdb)
1501 goto do_query;
1503 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1504 if (!centry)
1505 goto do_query;
1507 *num_entries = centry_uint32(centry);
1509 if (*num_entries == 0)
1510 goto do_cached;
1512 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1513 if (! (*info)) {
1514 smb_panic_fn("enum_dom_groups out of memory");
1516 for (i=0; i<(*num_entries); i++) {
1517 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1518 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1519 (*info)[i].rid = centry_uint32(centry);
1522 do_cached:
1523 status = centry->status;
1525 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1526 domain->name, nt_errstr(status) ));
1528 centry_free(centry);
1529 return status;
1531 do_query:
1532 *num_entries = 0;
1533 *info = NULL;
1535 /* Return status value returned by seq number check */
1537 if (!NT_STATUS_IS_OK(domain->last_status))
1538 return domain->last_status;
1540 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1541 domain->name ));
1543 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1545 /* and save it */
1546 refresh_sequence_number(domain, false);
1547 centry = centry_start(domain, status);
1548 if (!centry)
1549 goto skip_save;
1550 centry_put_uint32(centry, *num_entries);
1551 for (i=0; i<(*num_entries); i++) {
1552 centry_put_string(centry, (*info)[i].acct_name);
1553 centry_put_string(centry, (*info)[i].acct_desc);
1554 centry_put_uint32(centry, (*info)[i].rid);
1556 centry_end(centry, "GL/%s/domain", domain->name);
1557 centry_free(centry);
1559 skip_save:
1560 return status;
1563 /* list all domain groups */
1564 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1565 TALLOC_CTX *mem_ctx,
1566 uint32 *num_entries,
1567 struct acct_info **info)
1569 struct winbind_cache *cache = get_cache(domain);
1570 struct cache_entry *centry = NULL;
1571 NTSTATUS status;
1572 unsigned int i;
1574 if (!cache->tdb)
1575 goto do_query;
1577 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1578 if (!centry)
1579 goto do_query;
1581 *num_entries = centry_uint32(centry);
1583 if (*num_entries == 0)
1584 goto do_cached;
1586 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1587 if (! (*info)) {
1588 smb_panic_fn("enum_dom_groups out of memory");
1590 for (i=0; i<(*num_entries); i++) {
1591 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1592 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1593 (*info)[i].rid = centry_uint32(centry);
1596 do_cached:
1598 /* If we are returning cached data and the domain controller
1599 is down then we don't know whether the data is up to date
1600 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1601 indicate this. */
1603 if (wcache_server_down(domain)) {
1604 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1605 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1606 } else
1607 status = centry->status;
1609 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1610 domain->name, nt_errstr(status) ));
1612 centry_free(centry);
1613 return status;
1615 do_query:
1616 *num_entries = 0;
1617 *info = NULL;
1619 /* Return status value returned by seq number check */
1621 if (!NT_STATUS_IS_OK(domain->last_status))
1622 return domain->last_status;
1624 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1625 domain->name ));
1627 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1629 /* and save it */
1630 refresh_sequence_number(domain, false);
1631 centry = centry_start(domain, status);
1632 if (!centry)
1633 goto skip_save;
1634 centry_put_uint32(centry, *num_entries);
1635 for (i=0; i<(*num_entries); i++) {
1636 centry_put_string(centry, (*info)[i].acct_name);
1637 centry_put_string(centry, (*info)[i].acct_desc);
1638 centry_put_uint32(centry, (*info)[i].rid);
1640 centry_end(centry, "GL/%s/local", domain->name);
1641 centry_free(centry);
1643 skip_save:
1644 return status;
1647 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1648 const char *domain_name,
1649 const char *name,
1650 struct dom_sid *sid,
1651 enum lsa_SidType *type)
1653 struct winbind_cache *cache = get_cache(domain);
1654 struct cache_entry *centry;
1655 NTSTATUS status;
1656 char *uname;
1658 if (cache->tdb == NULL) {
1659 return NT_STATUS_NOT_FOUND;
1662 uname = talloc_strdup_upper(talloc_tos(), name);
1663 if (uname == NULL) {
1664 return NT_STATUS_NO_MEMORY;
1667 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1668 TALLOC_FREE(uname);
1669 if (centry == NULL) {
1670 return NT_STATUS_NOT_FOUND;
1673 status = centry->status;
1674 if (NT_STATUS_IS_OK(status)) {
1675 *type = (enum lsa_SidType)centry_uint32(centry);
1676 centry_sid(centry, sid);
1679 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1680 "%s\n", domain->name, nt_errstr(status) ));
1682 centry_free(centry);
1683 return status;
1686 /* convert a single name to a sid in a domain */
1687 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1688 TALLOC_CTX *mem_ctx,
1689 const char *domain_name,
1690 const char *name,
1691 uint32_t flags,
1692 DOM_SID *sid,
1693 enum lsa_SidType *type)
1695 NTSTATUS status;
1697 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1698 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1699 return status;
1702 ZERO_STRUCTP(sid);
1704 /* If the seq number check indicated that there is a problem
1705 * with this DC, then return that status... except for
1706 * access_denied. This is special because the dc may be in
1707 * "restrict anonymous = 1" mode, in which case it will deny
1708 * most unauthenticated operations, but *will* allow the LSA
1709 * name-to-sid that we try as a fallback. */
1711 if (!(NT_STATUS_IS_OK(domain->last_status)
1712 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1713 return domain->last_status;
1715 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1716 domain->name ));
1718 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1719 name, flags, sid, type);
1721 /* and save it */
1722 refresh_sequence_number(domain, false);
1724 if (domain->online &&
1725 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1726 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1728 /* Only save the reverse mapping if this was not a UPN */
1729 if (!strchr(name, '@')) {
1730 strupper_m(CONST_DISCARD(char *,domain_name));
1731 strlower_m(CONST_DISCARD(char *,name));
1732 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1736 return status;
1739 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1740 const struct dom_sid *sid,
1741 TALLOC_CTX *mem_ctx,
1742 char **domain_name,
1743 char **name,
1744 enum lsa_SidType *type)
1746 struct winbind_cache *cache = get_cache(domain);
1747 struct cache_entry *centry;
1748 char *sid_string;
1749 NTSTATUS status;
1751 if (cache->tdb == NULL) {
1752 return NT_STATUS_NOT_FOUND;
1755 sid_string = sid_string_tos(sid);
1756 if (sid_string == NULL) {
1757 return NT_STATUS_NO_MEMORY;
1760 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1761 TALLOC_FREE(sid_string);
1762 if (centry == NULL) {
1763 return NT_STATUS_NOT_FOUND;
1766 if (NT_STATUS_IS_OK(centry->status)) {
1767 *type = (enum lsa_SidType)centry_uint32(centry);
1768 *domain_name = centry_string(centry, mem_ctx);
1769 *name = centry_string(centry, mem_ctx);
1772 status = centry->status;
1773 centry_free(centry);
1775 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1776 "%s\n", domain->name, nt_errstr(status) ));
1778 return status;
1781 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1782 given */
1783 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1784 TALLOC_CTX *mem_ctx,
1785 const DOM_SID *sid,
1786 char **domain_name,
1787 char **name,
1788 enum lsa_SidType *type)
1790 NTSTATUS status;
1792 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1793 type);
1794 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1795 return status;
1798 *name = NULL;
1799 *domain_name = NULL;
1801 /* If the seq number check indicated that there is a problem
1802 * with this DC, then return that status... except for
1803 * access_denied. This is special because the dc may be in
1804 * "restrict anonymous = 1" mode, in which case it will deny
1805 * most unauthenticated operations, but *will* allow the LSA
1806 * sid-to-name that we try as a fallback. */
1808 if (!(NT_STATUS_IS_OK(domain->last_status)
1809 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1810 return domain->last_status;
1812 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1813 domain->name ));
1815 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1817 /* and save it */
1818 refresh_sequence_number(domain, false);
1819 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1821 /* We can't save the name to sid mapping here, as with sid history a
1822 * later name2sid would give the wrong sid. */
1824 return status;
1827 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1828 TALLOC_CTX *mem_ctx,
1829 const DOM_SID *domain_sid,
1830 uint32 *rids,
1831 size_t num_rids,
1832 char **domain_name,
1833 char ***names,
1834 enum lsa_SidType **types)
1836 struct winbind_cache *cache = get_cache(domain);
1837 size_t i;
1838 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1839 bool have_mapped;
1840 bool have_unmapped;
1842 *domain_name = NULL;
1843 *names = NULL;
1844 *types = NULL;
1846 if (!cache->tdb) {
1847 goto do_query;
1850 if (num_rids == 0) {
1851 return NT_STATUS_OK;
1854 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1855 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1857 if ((*names == NULL) || (*types == NULL)) {
1858 result = NT_STATUS_NO_MEMORY;
1859 goto error;
1862 have_mapped = have_unmapped = false;
1864 for (i=0; i<num_rids; i++) {
1865 DOM_SID sid;
1866 struct cache_entry *centry;
1867 fstring tmp;
1869 if (!sid_compose(&sid, domain_sid, rids[i])) {
1870 result = NT_STATUS_INTERNAL_ERROR;
1871 goto error;
1874 centry = wcache_fetch(cache, domain, "SN/%s",
1875 sid_to_fstring(tmp, &sid));
1876 if (!centry) {
1877 goto do_query;
1880 (*types)[i] = SID_NAME_UNKNOWN;
1881 (*names)[i] = talloc_strdup(*names, "");
1883 if (NT_STATUS_IS_OK(centry->status)) {
1884 char *dom;
1885 have_mapped = true;
1886 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1888 dom = centry_string(centry, mem_ctx);
1889 if (*domain_name == NULL) {
1890 *domain_name = dom;
1891 } else {
1892 talloc_free(dom);
1895 (*names)[i] = centry_string(centry, *names);
1897 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1898 have_unmapped = true;
1900 } else {
1901 /* something's definitely wrong */
1902 result = centry->status;
1903 goto error;
1906 centry_free(centry);
1909 if (!have_mapped) {
1910 return NT_STATUS_NONE_MAPPED;
1912 if (!have_unmapped) {
1913 return NT_STATUS_OK;
1915 return STATUS_SOME_UNMAPPED;
1917 do_query:
1919 TALLOC_FREE(*names);
1920 TALLOC_FREE(*types);
1922 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1923 rids, num_rids, domain_name,
1924 names, types);
1927 None of the queried rids has been found so save all negative entries
1929 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1930 for (i = 0; i < num_rids; i++) {
1931 DOM_SID sid;
1932 const char *name = "";
1933 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1934 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1936 if (!sid_compose(&sid, domain_sid, rids[i])) {
1937 return NT_STATUS_INTERNAL_ERROR;
1940 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1941 name, type);
1944 return result;
1948 Some or all of the queried rids have been found.
1950 if (!NT_STATUS_IS_OK(result) &&
1951 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1952 return result;
1955 refresh_sequence_number(domain, false);
1957 for (i=0; i<num_rids; i++) {
1958 DOM_SID sid;
1959 NTSTATUS status;
1961 if (!sid_compose(&sid, domain_sid, rids[i])) {
1962 result = NT_STATUS_INTERNAL_ERROR;
1963 goto error;
1966 status = (*types)[i] == SID_NAME_UNKNOWN ?
1967 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1969 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1970 (*names)[i], (*types)[i]);
1973 return result;
1975 error:
1976 TALLOC_FREE(*names);
1977 TALLOC_FREE(*types);
1978 return result;
1981 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
1982 TALLOC_CTX *mem_ctx,
1983 const struct dom_sid *user_sid,
1984 struct wbint_userinfo *info)
1986 struct winbind_cache *cache = get_cache(domain);
1987 struct cache_entry *centry = NULL;
1988 NTSTATUS status;
1989 char *sid_string;
1991 if (cache->tdb == NULL) {
1992 return NT_STATUS_NOT_FOUND;
1995 sid_string = sid_string_tos(user_sid);
1996 if (sid_string == NULL) {
1997 return NT_STATUS_NO_MEMORY;
2000 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2001 TALLOC_FREE(sid_string);
2002 if (centry == NULL) {
2003 return NT_STATUS_NOT_FOUND;
2007 * If we have an access denied cache entry and a cached info3
2008 * in the samlogon cache then do a query. This will force the
2009 * rpc back end to return the info3 data.
2012 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2013 netsamlogon_cache_have(user_sid)) {
2014 DEBUG(10, ("query_user: cached access denied and have cached "
2015 "info3\n"));
2016 domain->last_status = NT_STATUS_OK;
2017 centry_free(centry);
2018 return NT_STATUS_NOT_FOUND;
2021 /* if status is not ok then this is a negative hit
2022 and the rest of the data doesn't matter */
2023 status = centry->status;
2024 if (NT_STATUS_IS_OK(status)) {
2025 info->acct_name = centry_string(centry, mem_ctx);
2026 info->full_name = centry_string(centry, mem_ctx);
2027 info->homedir = centry_string(centry, mem_ctx);
2028 info->shell = centry_string(centry, mem_ctx);
2029 info->primary_gid = centry_uint32(centry);
2030 centry_sid(centry, &info->user_sid);
2031 centry_sid(centry, &info->group_sid);
2034 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2035 "%s\n", domain->name, nt_errstr(status) ));
2037 centry_free(centry);
2038 return status;
2041 /* Lookup user information from a rid */
2042 static NTSTATUS query_user(struct winbindd_domain *domain,
2043 TALLOC_CTX *mem_ctx,
2044 const DOM_SID *user_sid,
2045 struct wbint_userinfo *info)
2047 NTSTATUS status;
2049 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2050 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2051 return status;
2054 ZERO_STRUCTP(info);
2056 /* Return status value returned by seq number check */
2058 if (!NT_STATUS_IS_OK(domain->last_status))
2059 return domain->last_status;
2061 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2062 domain->name ));
2064 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2066 /* and save it */
2067 refresh_sequence_number(domain, false);
2068 wcache_save_user(domain, status, info);
2070 return status;
2073 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2074 TALLOC_CTX *mem_ctx,
2075 const struct dom_sid *user_sid,
2076 uint32_t *pnum_sids,
2077 struct dom_sid **psids)
2079 struct winbind_cache *cache = get_cache(domain);
2080 struct cache_entry *centry = NULL;
2081 NTSTATUS status;
2082 uint32_t i, num_sids;
2083 struct dom_sid *sids;
2084 fstring sid_string;
2086 if (cache->tdb == NULL) {
2087 return NT_STATUS_NOT_FOUND;
2090 centry = wcache_fetch(cache, domain, "UG/%s",
2091 sid_to_fstring(sid_string, user_sid));
2092 if (centry == NULL) {
2093 return NT_STATUS_NOT_FOUND;
2096 /* If we have an access denied cache entry and a cached info3 in the
2097 samlogon cache then do a query. This will force the rpc back end
2098 to return the info3 data. */
2100 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2101 && netsamlogon_cache_have(user_sid)) {
2102 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2103 "cached info3\n"));
2104 domain->last_status = NT_STATUS_OK;
2105 centry_free(centry);
2106 return NT_STATUS_NOT_FOUND;
2109 num_sids = centry_uint32(centry);
2110 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2111 if (sids == NULL) {
2112 return NT_STATUS_NO_MEMORY;
2115 for (i=0; i<num_sids; i++) {
2116 centry_sid(centry, &sids[i]);
2119 status = centry->status;
2121 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2122 "status: %s\n", domain->name, nt_errstr(status)));
2124 centry_free(centry);
2126 *pnum_sids = num_sids;
2127 *psids = sids;
2128 return status;
2131 /* Lookup groups a user is a member of. */
2132 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2133 TALLOC_CTX *mem_ctx,
2134 const DOM_SID *user_sid,
2135 uint32 *num_groups, DOM_SID **user_gids)
2137 struct cache_entry *centry = NULL;
2138 NTSTATUS status;
2139 unsigned int i;
2140 fstring sid_string;
2142 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2143 num_groups, user_gids);
2144 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2145 return status;
2148 (*num_groups) = 0;
2149 (*user_gids) = NULL;
2151 /* Return status value returned by seq number check */
2153 if (!NT_STATUS_IS_OK(domain->last_status))
2154 return domain->last_status;
2156 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2157 domain->name ));
2159 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2161 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2162 goto skip_save;
2164 /* and save it */
2165 refresh_sequence_number(domain, false);
2166 centry = centry_start(domain, status);
2167 if (!centry)
2168 goto skip_save;
2170 centry_put_uint32(centry, *num_groups);
2171 for (i=0; i<(*num_groups); i++) {
2172 centry_put_sid(centry, &(*user_gids)[i]);
2175 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2176 centry_free(centry);
2178 skip_save:
2179 return status;
2182 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2183 const struct dom_sid *sids)
2185 uint32_t i;
2186 char *sidlist;
2188 sidlist = talloc_strdup(mem_ctx, "");
2189 if (sidlist == NULL) {
2190 return NULL;
2192 for (i=0; i<num_sids; i++) {
2193 fstring tmp;
2194 sidlist = talloc_asprintf_append_buffer(
2195 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2196 if (sidlist == NULL) {
2197 return NULL;
2200 return sidlist;
2203 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2204 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2205 const struct dom_sid *sids,
2206 uint32_t *pnum_aliases, uint32_t **paliases)
2208 struct winbind_cache *cache = get_cache(domain);
2209 struct cache_entry *centry = NULL;
2210 uint32_t num_aliases;
2211 uint32_t *aliases;
2212 NTSTATUS status;
2213 char *sidlist;
2214 int i;
2216 if (cache->tdb == NULL) {
2217 return NT_STATUS_NOT_FOUND;
2220 if (num_sids == 0) {
2221 *pnum_aliases = 0;
2222 *paliases = NULL;
2223 return NT_STATUS_OK;
2226 /* We need to cache indexed by the whole list of SIDs, the aliases
2227 * resulting might come from any of the SIDs. */
2229 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2230 if (sidlist == NULL) {
2231 return NT_STATUS_NO_MEMORY;
2234 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2235 TALLOC_FREE(sidlist);
2236 if (centry == NULL) {
2237 return NT_STATUS_NOT_FOUND;
2240 num_aliases = centry_uint32(centry);
2241 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2242 if (aliases == NULL) {
2243 centry_free(centry);
2244 return NT_STATUS_NO_MEMORY;
2247 for (i=0; i<num_aliases; i++) {
2248 aliases[i] = centry_uint32(centry);
2251 status = centry->status;
2253 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2254 "status %s\n", domain->name, nt_errstr(status)));
2256 centry_free(centry);
2258 *pnum_aliases = num_aliases;
2259 *paliases = aliases;
2261 return status;
2264 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2265 TALLOC_CTX *mem_ctx,
2266 uint32 num_sids, const DOM_SID *sids,
2267 uint32 *num_aliases, uint32 **alias_rids)
2269 struct cache_entry *centry = NULL;
2270 NTSTATUS status;
2271 char *sidlist;
2272 int i;
2274 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2275 num_aliases, alias_rids);
2276 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2277 return status;
2280 (*num_aliases) = 0;
2281 (*alias_rids) = NULL;
2283 if (!NT_STATUS_IS_OK(domain->last_status))
2284 return domain->last_status;
2286 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2287 "for domain %s\n", domain->name ));
2289 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2290 if (sidlist == NULL) {
2291 return NT_STATUS_NO_MEMORY;
2294 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2295 num_sids, sids,
2296 num_aliases, alias_rids);
2298 /* and save it */
2299 refresh_sequence_number(domain, false);
2300 centry = centry_start(domain, status);
2301 if (!centry)
2302 goto skip_save;
2303 centry_put_uint32(centry, *num_aliases);
2304 for (i=0; i<(*num_aliases); i++)
2305 centry_put_uint32(centry, (*alias_rids)[i]);
2306 centry_end(centry, "UA%s", sidlist);
2307 centry_free(centry);
2309 skip_save:
2310 return status;
2313 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2314 TALLOC_CTX *mem_ctx,
2315 const struct dom_sid *group_sid,
2316 uint32_t *num_names,
2317 struct dom_sid **sid_mem, char ***names,
2318 uint32_t **name_types)
2320 struct winbind_cache *cache = get_cache(domain);
2321 struct cache_entry *centry = NULL;
2322 NTSTATUS status;
2323 unsigned int i;
2324 char *sid_string;
2326 if (cache->tdb == NULL) {
2327 return NT_STATUS_NOT_FOUND;
2330 sid_string = sid_string_tos(group_sid);
2331 if (sid_string == NULL) {
2332 return NT_STATUS_NO_MEMORY;
2335 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2336 TALLOC_FREE(sid_string);
2337 if (centry == NULL) {
2338 return NT_STATUS_NOT_FOUND;
2341 *sid_mem = NULL;
2342 *names = NULL;
2343 *name_types = NULL;
2345 *num_names = centry_uint32(centry);
2346 if (*num_names == 0) {
2347 centry_free(centry);
2348 return NT_STATUS_OK;
2351 *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2352 *names = talloc_array(mem_ctx, char *, *num_names);
2353 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2355 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2356 TALLOC_FREE(*sid_mem);
2357 TALLOC_FREE(*names);
2358 TALLOC_FREE(*name_types);
2359 centry_free(centry);
2360 return NT_STATUS_NO_MEMORY;
2363 for (i=0; i<(*num_names); i++) {
2364 centry_sid(centry, &(*sid_mem)[i]);
2365 (*names)[i] = centry_string(centry, mem_ctx);
2366 (*name_types)[i] = centry_uint32(centry);
2369 status = centry->status;
2371 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2372 "status: %s\n", domain->name, nt_errstr(status)));
2374 centry_free(centry);
2375 return status;
2378 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2379 TALLOC_CTX *mem_ctx,
2380 const DOM_SID *group_sid, uint32 *num_names,
2381 DOM_SID **sid_mem, char ***names,
2382 uint32 **name_types)
2384 struct cache_entry *centry = NULL;
2385 NTSTATUS status;
2386 unsigned int i;
2387 fstring sid_string;
2389 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2390 sid_mem, names, name_types);
2391 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2392 return status;
2395 (*num_names) = 0;
2396 (*sid_mem) = NULL;
2397 (*names) = NULL;
2398 (*name_types) = NULL;
2400 /* Return status value returned by seq number check */
2402 if (!NT_STATUS_IS_OK(domain->last_status))
2403 return domain->last_status;
2405 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2406 domain->name ));
2408 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2409 sid_mem, names, name_types);
2411 /* and save it */
2412 refresh_sequence_number(domain, false);
2413 centry = centry_start(domain, status);
2414 if (!centry)
2415 goto skip_save;
2416 centry_put_uint32(centry, *num_names);
2417 for (i=0; i<(*num_names); i++) {
2418 centry_put_sid(centry, &(*sid_mem)[i]);
2419 centry_put_string(centry, (*names)[i]);
2420 centry_put_uint32(centry, (*name_types)[i]);
2422 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2423 centry_free(centry);
2425 skip_save:
2426 return status;
2429 /* find the sequence number for a domain */
2430 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2432 refresh_sequence_number(domain, false);
2434 *seq = domain->sequence_number;
2436 return NT_STATUS_OK;
2439 /* enumerate trusted domains
2440 * (we need to have the list of trustdoms in the cache when we go offline) -
2441 * Guenther */
2442 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2443 TALLOC_CTX *mem_ctx,
2444 uint32 *num_domains,
2445 char ***names,
2446 char ***alt_names,
2447 DOM_SID **dom_sids)
2449 struct winbind_cache *cache = get_cache(domain);
2450 struct cache_entry *centry = NULL;
2451 NTSTATUS status;
2452 int i;
2454 if (!cache->tdb)
2455 goto do_query;
2457 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2459 if (!centry) {
2460 goto do_query;
2463 *num_domains = centry_uint32(centry);
2465 if (*num_domains) {
2466 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2467 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2468 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2470 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2471 smb_panic_fn("trusted_domains out of memory");
2473 } else {
2474 (*names) = NULL;
2475 (*alt_names) = NULL;
2476 (*dom_sids) = NULL;
2479 for (i=0; i<(*num_domains); i++) {
2480 (*names)[i] = centry_string(centry, mem_ctx);
2481 (*alt_names)[i] = centry_string(centry, mem_ctx);
2482 if (!centry_sid(centry, &(*dom_sids)[i])) {
2483 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2487 status = centry->status;
2489 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2490 domain->name, *num_domains, nt_errstr(status) ));
2492 centry_free(centry);
2493 return status;
2495 do_query:
2496 (*num_domains) = 0;
2497 (*dom_sids) = NULL;
2498 (*names) = NULL;
2499 (*alt_names) = NULL;
2501 /* Return status value returned by seq number check */
2503 if (!NT_STATUS_IS_OK(domain->last_status))
2504 return domain->last_status;
2506 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2507 domain->name ));
2509 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2510 names, alt_names, dom_sids);
2512 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2513 * so that the generic centry handling still applies correctly -
2514 * Guenther*/
2516 if (!NT_STATUS_IS_ERR(status)) {
2517 status = NT_STATUS_OK;
2521 #if 0 /* Disabled as we want the trust dom list to be managed by
2522 the main parent and always to make the query. --jerry */
2524 /* and save it */
2525 refresh_sequence_number(domain, false);
2527 centry = centry_start(domain, status);
2528 if (!centry)
2529 goto skip_save;
2531 centry_put_uint32(centry, *num_domains);
2533 for (i=0; i<(*num_domains); i++) {
2534 centry_put_string(centry, (*names)[i]);
2535 centry_put_string(centry, (*alt_names)[i]);
2536 centry_put_sid(centry, &(*dom_sids)[i]);
2539 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2541 centry_free(centry);
2543 skip_save:
2544 #endif
2546 return status;
2549 /* get lockout policy */
2550 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2551 TALLOC_CTX *mem_ctx,
2552 struct samr_DomInfo12 *policy)
2554 struct winbind_cache *cache = get_cache(domain);
2555 struct cache_entry *centry = NULL;
2556 NTSTATUS status;
2558 if (!cache->tdb)
2559 goto do_query;
2561 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2563 if (!centry)
2564 goto do_query;
2566 policy->lockout_duration = centry_nttime(centry);
2567 policy->lockout_window = centry_nttime(centry);
2568 policy->lockout_threshold = centry_uint16(centry);
2570 status = centry->status;
2572 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2573 domain->name, nt_errstr(status) ));
2575 centry_free(centry);
2576 return status;
2578 do_query:
2579 ZERO_STRUCTP(policy);
2581 /* Return status value returned by seq number check */
2583 if (!NT_STATUS_IS_OK(domain->last_status))
2584 return domain->last_status;
2586 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2587 domain->name ));
2589 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2591 /* and save it */
2592 refresh_sequence_number(domain, false);
2593 wcache_save_lockout_policy(domain, status, policy);
2595 return status;
2598 /* get password policy */
2599 static NTSTATUS password_policy(struct winbindd_domain *domain,
2600 TALLOC_CTX *mem_ctx,
2601 struct samr_DomInfo1 *policy)
2603 struct winbind_cache *cache = get_cache(domain);
2604 struct cache_entry *centry = NULL;
2605 NTSTATUS status;
2607 if (!cache->tdb)
2608 goto do_query;
2610 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2612 if (!centry)
2613 goto do_query;
2615 policy->min_password_length = centry_uint16(centry);
2616 policy->password_history_length = centry_uint16(centry);
2617 policy->password_properties = centry_uint32(centry);
2618 policy->max_password_age = centry_nttime(centry);
2619 policy->min_password_age = centry_nttime(centry);
2621 status = centry->status;
2623 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2624 domain->name, nt_errstr(status) ));
2626 centry_free(centry);
2627 return status;
2629 do_query:
2630 ZERO_STRUCTP(policy);
2632 /* Return status value returned by seq number check */
2634 if (!NT_STATUS_IS_OK(domain->last_status))
2635 return domain->last_status;
2637 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2638 domain->name ));
2640 status = domain->backend->password_policy(domain, mem_ctx, policy);
2642 /* and save it */
2643 refresh_sequence_number(domain, false);
2644 if (NT_STATUS_IS_OK(status)) {
2645 wcache_save_password_policy(domain, status, policy);
2648 return status;
2652 /* Invalidate cached user and group lists coherently */
2654 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2655 void *state)
2657 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2658 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2659 tdb_delete(the_tdb, kbuf);
2661 return 0;
2664 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2666 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2667 struct netr_SamInfo3 *info3)
2669 DOM_SID sid;
2670 fstring key_str, sid_string;
2671 struct winbind_cache *cache;
2673 /* dont clear cached U/SID and UG/SID entries when we want to logon
2674 * offline - gd */
2676 if (lp_winbind_offline_logon()) {
2677 return;
2680 if (!domain)
2681 return;
2683 cache = get_cache(domain);
2685 if (!cache->tdb) {
2686 return;
2689 sid_copy(&sid, info3->base.domain_sid);
2690 sid_append_rid(&sid, info3->base.rid);
2692 /* Clear U/SID cache entry */
2693 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2694 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2695 tdb_delete(cache->tdb, string_tdb_data(key_str));
2697 /* Clear UG/SID cache entry */
2698 fstr_sprintf(key_str, "UG/%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 /* Samba/winbindd never needs this. */
2703 netsamlogon_clear_cached_user(info3);
2706 bool wcache_invalidate_cache(void)
2708 struct winbindd_domain *domain;
2710 for (domain = domain_list(); domain; domain = domain->next) {
2711 struct winbind_cache *cache = get_cache(domain);
2713 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2714 "entries for %s\n", domain->name));
2715 if (cache) {
2716 if (cache->tdb) {
2717 tdb_traverse(cache->tdb, traverse_fn, NULL);
2718 } else {
2719 return false;
2723 return true;
2726 bool init_wcache(void)
2728 if (wcache == NULL) {
2729 wcache = SMB_XMALLOC_P(struct winbind_cache);
2730 ZERO_STRUCTP(wcache);
2733 if (wcache->tdb != NULL)
2734 return true;
2736 /* when working offline we must not clear the cache on restart */
2737 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2738 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2739 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2740 O_RDWR|O_CREAT, 0600);
2742 if (wcache->tdb == NULL) {
2743 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2744 return false;
2747 return true;
2750 /************************************************************************
2751 This is called by the parent to initialize the cache file.
2752 We don't need sophisticated locking here as we know we're the
2753 only opener.
2754 ************************************************************************/
2756 bool initialize_winbindd_cache(void)
2758 bool cache_bad = true;
2759 uint32 vers;
2761 if (!init_wcache()) {
2762 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2763 return false;
2766 /* Check version number. */
2767 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2768 vers == WINBINDD_CACHE_VERSION) {
2769 cache_bad = false;
2772 if (cache_bad) {
2773 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2774 "and re-creating with version number %d\n",
2775 WINBINDD_CACHE_VERSION ));
2777 tdb_close(wcache->tdb);
2778 wcache->tdb = NULL;
2780 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2781 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2782 cache_path("winbindd_cache.tdb"),
2783 strerror(errno) ));
2784 return false;
2786 if (!init_wcache()) {
2787 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2788 "init_wcache failed.\n"));
2789 return false;
2792 /* Write the version. */
2793 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2794 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2795 tdb_errorstr(wcache->tdb) ));
2796 return false;
2800 tdb_close(wcache->tdb);
2801 wcache->tdb = NULL;
2802 return true;
2805 void close_winbindd_cache(void)
2807 if (!wcache) {
2808 return;
2810 if (wcache->tdb) {
2811 tdb_close(wcache->tdb);
2812 wcache->tdb = NULL;
2816 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2817 char **domain_name, char **name,
2818 enum lsa_SidType *type)
2820 struct winbindd_domain *domain;
2821 NTSTATUS status;
2823 domain = find_lookup_domain_from_sid(sid);
2824 if (domain == NULL) {
2825 return false;
2827 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
2828 type);
2829 return NT_STATUS_IS_OK(status);
2832 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2833 const char *domain_name,
2834 const char *name,
2835 DOM_SID *sid,
2836 enum lsa_SidType *type)
2838 struct winbindd_domain *domain;
2839 NTSTATUS status;
2840 bool original_online_state;
2842 domain = find_lookup_domain_from_name(domain_name);
2843 if (domain == NULL) {
2844 return false;
2847 /* If we are doing a cached logon, temporarily set the domain
2848 offline so the cache won't expire the entry */
2850 original_online_state = domain->online;
2851 domain->online = false;
2852 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
2853 domain->online = original_online_state;
2855 return NT_STATUS_IS_OK(status);
2858 void cache_name2sid(struct winbindd_domain *domain,
2859 const char *domain_name, const char *name,
2860 enum lsa_SidType type, const DOM_SID *sid)
2862 refresh_sequence_number(domain, false);
2863 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2864 sid, type);
2868 * The original idea that this cache only contains centries has
2869 * been blurred - now other stuff gets put in here. Ensure we
2870 * ignore these things on cleanup.
2873 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2874 TDB_DATA dbuf, void *state)
2876 struct cache_entry *centry;
2878 if (is_non_centry_key(kbuf)) {
2879 return 0;
2882 centry = wcache_fetch_raw((char *)kbuf.dptr);
2883 if (!centry) {
2884 return 0;
2887 if (!NT_STATUS_IS_OK(centry->status)) {
2888 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2889 tdb_delete(the_tdb, kbuf);
2892 centry_free(centry);
2893 return 0;
2896 /* flush the cache */
2897 void wcache_flush_cache(void)
2899 if (!wcache)
2900 return;
2901 if (wcache->tdb) {
2902 tdb_close(wcache->tdb);
2903 wcache->tdb = NULL;
2905 if (!winbindd_use_cache()) {
2906 return;
2909 /* when working offline we must not clear the cache on restart */
2910 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2911 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2912 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2913 O_RDWR|O_CREAT, 0600);
2915 if (!wcache->tdb) {
2916 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2917 return;
2920 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2922 DEBUG(10,("wcache_flush_cache success\n"));
2925 /* Count cached creds */
2927 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2928 void *state)
2930 int *cred_count = (int*)state;
2932 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2933 (*cred_count)++;
2935 return 0;
2938 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2940 struct winbind_cache *cache = get_cache(domain);
2942 *count = 0;
2944 if (!cache->tdb) {
2945 return NT_STATUS_INTERNAL_DB_ERROR;
2948 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2950 return NT_STATUS_OK;
2953 struct cred_list {
2954 struct cred_list *prev, *next;
2955 TDB_DATA key;
2956 fstring name;
2957 time_t created;
2959 static struct cred_list *wcache_cred_list;
2961 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2962 void *state)
2964 struct cred_list *cred;
2966 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2968 cred = SMB_MALLOC_P(struct cred_list);
2969 if (cred == NULL) {
2970 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2971 return -1;
2974 ZERO_STRUCTP(cred);
2976 /* save a copy of the key */
2978 fstrcpy(cred->name, (const char *)kbuf.dptr);
2979 DLIST_ADD(wcache_cred_list, cred);
2982 return 0;
2985 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2987 struct winbind_cache *cache = get_cache(domain);
2988 NTSTATUS status;
2989 int ret;
2990 struct cred_list *cred, *oldest = NULL;
2992 if (!cache->tdb) {
2993 return NT_STATUS_INTERNAL_DB_ERROR;
2996 /* we possibly already have an entry */
2997 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2999 fstring key_str, tmp;
3001 DEBUG(11,("we already have an entry, deleting that\n"));
3003 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3005 tdb_delete(cache->tdb, string_tdb_data(key_str));
3007 return NT_STATUS_OK;
3010 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3011 if (ret == 0) {
3012 return NT_STATUS_OK;
3013 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3014 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3017 ZERO_STRUCTP(oldest);
3019 for (cred = wcache_cred_list; cred; cred = cred->next) {
3021 TDB_DATA data;
3022 time_t t;
3024 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3025 if (!data.dptr) {
3026 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3027 cred->name));
3028 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3029 goto done;
3032 t = IVAL(data.dptr, 0);
3033 SAFE_FREE(data.dptr);
3035 if (!oldest) {
3036 oldest = SMB_MALLOC_P(struct cred_list);
3037 if (oldest == NULL) {
3038 status = NT_STATUS_NO_MEMORY;
3039 goto done;
3042 fstrcpy(oldest->name, cred->name);
3043 oldest->created = t;
3044 continue;
3047 if (t < oldest->created) {
3048 fstrcpy(oldest->name, cred->name);
3049 oldest->created = t;
3053 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3054 status = NT_STATUS_OK;
3055 } else {
3056 status = NT_STATUS_UNSUCCESSFUL;
3058 done:
3059 SAFE_FREE(wcache_cred_list);
3060 SAFE_FREE(oldest);
3062 return status;
3065 /* Change the global online/offline state. */
3066 bool set_global_winbindd_state_offline(void)
3068 TDB_DATA data;
3070 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3072 /* Only go offline if someone has created
3073 the key "WINBINDD_OFFLINE" in the cache tdb. */
3075 if (wcache == NULL || wcache->tdb == NULL) {
3076 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3077 return false;
3080 if (!lp_winbind_offline_logon()) {
3081 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3082 return false;
3085 if (global_winbindd_offline_state) {
3086 /* Already offline. */
3087 return true;
3090 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3092 if (!data.dptr || data.dsize != 4) {
3093 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3094 SAFE_FREE(data.dptr);
3095 return false;
3096 } else {
3097 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3098 global_winbindd_offline_state = true;
3099 SAFE_FREE(data.dptr);
3100 return true;
3104 void set_global_winbindd_state_online(void)
3106 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3108 if (!lp_winbind_offline_logon()) {
3109 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3110 return;
3113 if (!global_winbindd_offline_state) {
3114 /* Already online. */
3115 return;
3117 global_winbindd_offline_state = false;
3119 if (!wcache->tdb) {
3120 return;
3123 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3124 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3127 bool get_global_winbindd_state_offline(void)
3129 return global_winbindd_offline_state;
3132 /***********************************************************************
3133 Validate functions for all possible cache tdb keys.
3134 ***********************************************************************/
3136 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3137 struct tdb_validation_status *state)
3139 struct cache_entry *centry;
3141 centry = SMB_XMALLOC_P(struct cache_entry);
3142 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3143 if (!centry->data) {
3144 SAFE_FREE(centry);
3145 return NULL;
3147 centry->len = data.dsize;
3148 centry->ofs = 0;
3150 if (centry->len < 8) {
3151 /* huh? corrupt cache? */
3152 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3153 centry_free(centry);
3154 state->bad_entry = true;
3155 state->success = false;
3156 return NULL;
3159 centry->status = NT_STATUS(centry_uint32(centry));
3160 centry->sequence_number = centry_uint32(centry);
3161 return centry;
3164 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3165 struct tdb_validation_status *state)
3167 if (dbuf.dsize != 8) {
3168 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3169 keystr, (unsigned int)dbuf.dsize ));
3170 state->bad_entry = true;
3171 return 1;
3173 return 0;
3176 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3177 struct tdb_validation_status *state)
3179 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3180 if (!centry) {
3181 return 1;
3184 (void)centry_uint32(centry);
3185 if (NT_STATUS_IS_OK(centry->status)) {
3186 DOM_SID sid;
3187 (void)centry_sid(centry, &sid);
3190 centry_free(centry);
3192 if (!(state->success)) {
3193 return 1;
3195 DEBUG(10,("validate_ns: %s ok\n", keystr));
3196 return 0;
3199 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3200 struct tdb_validation_status *state)
3202 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3203 if (!centry) {
3204 return 1;
3207 if (NT_STATUS_IS_OK(centry->status)) {
3208 (void)centry_uint32(centry);
3209 (void)centry_string(centry, mem_ctx);
3210 (void)centry_string(centry, mem_ctx);
3213 centry_free(centry);
3215 if (!(state->success)) {
3216 return 1;
3218 DEBUG(10,("validate_sn: %s ok\n", keystr));
3219 return 0;
3222 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3223 struct tdb_validation_status *state)
3225 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3226 DOM_SID sid;
3228 if (!centry) {
3229 return 1;
3232 (void)centry_string(centry, mem_ctx);
3233 (void)centry_string(centry, mem_ctx);
3234 (void)centry_string(centry, mem_ctx);
3235 (void)centry_string(centry, mem_ctx);
3236 (void)centry_uint32(centry);
3237 (void)centry_sid(centry, &sid);
3238 (void)centry_sid(centry, &sid);
3240 centry_free(centry);
3242 if (!(state->success)) {
3243 return 1;
3245 DEBUG(10,("validate_u: %s ok\n", keystr));
3246 return 0;
3249 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3250 struct tdb_validation_status *state)
3252 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3254 if (!centry) {
3255 return 1;
3258 (void)centry_nttime(centry);
3259 (void)centry_nttime(centry);
3260 (void)centry_uint16(centry);
3262 centry_free(centry);
3264 if (!(state->success)) {
3265 return 1;
3267 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3268 return 0;
3271 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3272 struct tdb_validation_status *state)
3274 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3276 if (!centry) {
3277 return 1;
3280 (void)centry_uint16(centry);
3281 (void)centry_uint16(centry);
3282 (void)centry_uint32(centry);
3283 (void)centry_nttime(centry);
3284 (void)centry_nttime(centry);
3286 centry_free(centry);
3288 if (!(state->success)) {
3289 return 1;
3291 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3292 return 0;
3295 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3296 struct tdb_validation_status *state)
3298 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3300 if (!centry) {
3301 return 1;
3304 (void)centry_time(centry);
3305 (void)centry_hash16(centry, mem_ctx);
3307 /* We only have 17 bytes more data in the salted cred case. */
3308 if (centry->len - centry->ofs == 17) {
3309 (void)centry_hash16(centry, mem_ctx);
3312 centry_free(centry);
3314 if (!(state->success)) {
3315 return 1;
3317 DEBUG(10,("validate_cred: %s ok\n", keystr));
3318 return 0;
3321 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3322 struct tdb_validation_status *state)
3324 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3325 int32 num_entries, i;
3327 if (!centry) {
3328 return 1;
3331 num_entries = (int32)centry_uint32(centry);
3333 for (i=0; i< num_entries; i++) {
3334 DOM_SID sid;
3335 (void)centry_string(centry, mem_ctx);
3336 (void)centry_string(centry, mem_ctx);
3337 (void)centry_string(centry, mem_ctx);
3338 (void)centry_string(centry, mem_ctx);
3339 (void)centry_sid(centry, &sid);
3340 (void)centry_sid(centry, &sid);
3343 centry_free(centry);
3345 if (!(state->success)) {
3346 return 1;
3348 DEBUG(10,("validate_ul: %s ok\n", keystr));
3349 return 0;
3352 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3353 struct tdb_validation_status *state)
3355 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3356 int32 num_entries, i;
3358 if (!centry) {
3359 return 1;
3362 num_entries = centry_uint32(centry);
3364 for (i=0; i< num_entries; i++) {
3365 (void)centry_string(centry, mem_ctx);
3366 (void)centry_string(centry, mem_ctx);
3367 (void)centry_uint32(centry);
3370 centry_free(centry);
3372 if (!(state->success)) {
3373 return 1;
3375 DEBUG(10,("validate_gl: %s ok\n", keystr));
3376 return 0;
3379 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3380 struct tdb_validation_status *state)
3382 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3383 int32 num_groups, i;
3385 if (!centry) {
3386 return 1;
3389 num_groups = centry_uint32(centry);
3391 for (i=0; i< num_groups; i++) {
3392 DOM_SID sid;
3393 centry_sid(centry, &sid);
3396 centry_free(centry);
3398 if (!(state->success)) {
3399 return 1;
3401 DEBUG(10,("validate_ug: %s ok\n", keystr));
3402 return 0;
3405 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3406 struct tdb_validation_status *state)
3408 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3409 int32 num_aliases, i;
3411 if (!centry) {
3412 return 1;
3415 num_aliases = centry_uint32(centry);
3417 for (i=0; i < num_aliases; i++) {
3418 (void)centry_uint32(centry);
3421 centry_free(centry);
3423 if (!(state->success)) {
3424 return 1;
3426 DEBUG(10,("validate_ua: %s ok\n", keystr));
3427 return 0;
3430 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3431 struct tdb_validation_status *state)
3433 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3434 int32 num_names, i;
3436 if (!centry) {
3437 return 1;
3440 num_names = centry_uint32(centry);
3442 for (i=0; i< num_names; i++) {
3443 DOM_SID sid;
3444 centry_sid(centry, &sid);
3445 (void)centry_string(centry, mem_ctx);
3446 (void)centry_uint32(centry);
3449 centry_free(centry);
3451 if (!(state->success)) {
3452 return 1;
3454 DEBUG(10,("validate_gm: %s ok\n", keystr));
3455 return 0;
3458 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3459 struct tdb_validation_status *state)
3461 /* Can't say anything about this other than must be nonzero. */
3462 if (dbuf.dsize == 0) {
3463 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3464 keystr));
3465 state->bad_entry = true;
3466 state->success = false;
3467 return 1;
3470 DEBUG(10,("validate_dr: %s ok\n", keystr));
3471 return 0;
3474 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3475 struct tdb_validation_status *state)
3477 /* Can't say anything about this other than must be nonzero. */
3478 if (dbuf.dsize == 0) {
3479 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3480 keystr));
3481 state->bad_entry = true;
3482 state->success = false;
3483 return 1;
3486 DEBUG(10,("validate_de: %s ok\n", keystr));
3487 return 0;
3490 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3491 TDB_DATA dbuf, struct tdb_validation_status *state)
3493 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3495 if (!centry) {
3496 return 1;
3499 (void)centry_string(centry, mem_ctx);
3500 (void)centry_string(centry, mem_ctx);
3501 (void)centry_string(centry, mem_ctx);
3502 (void)centry_uint32(centry);
3504 centry_free(centry);
3506 if (!(state->success)) {
3507 return 1;
3509 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3510 return 0;
3513 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3514 TDB_DATA dbuf,
3515 struct tdb_validation_status *state)
3517 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3519 if (!centry) {
3520 return 1;
3523 (void)centry_string( centry, mem_ctx );
3525 centry_free(centry);
3527 if (!(state->success)) {
3528 return 1;
3530 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3531 return 0;
3534 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3535 TDB_DATA dbuf,
3536 struct tdb_validation_status *state)
3538 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3540 if (!centry) {
3541 return 1;
3544 (void)centry_string( centry, mem_ctx );
3546 centry_free(centry);
3548 if (!(state->success)) {
3549 return 1;
3551 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3552 return 0;
3555 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3556 struct tdb_validation_status *state)
3558 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3559 int32 num_domains, i;
3561 if (!centry) {
3562 return 1;
3565 num_domains = centry_uint32(centry);
3567 for (i=0; i< num_domains; i++) {
3568 DOM_SID sid;
3569 (void)centry_string(centry, mem_ctx);
3570 (void)centry_string(centry, mem_ctx);
3571 (void)centry_sid(centry, &sid);
3574 centry_free(centry);
3576 if (!(state->success)) {
3577 return 1;
3579 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3580 return 0;
3583 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3584 TDB_DATA dbuf,
3585 struct tdb_validation_status *state)
3587 if (dbuf.dsize == 0) {
3588 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3589 "key %s (len ==0) ?\n", keystr));
3590 state->bad_entry = true;
3591 state->success = false;
3592 return 1;
3595 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3596 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3597 return 0;
3600 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3601 struct tdb_validation_status *state)
3603 if (dbuf.dsize != 4) {
3604 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3605 keystr, (unsigned int)dbuf.dsize ));
3606 state->bad_entry = true;
3607 state->success = false;
3608 return 1;
3610 DEBUG(10,("validate_offline: %s ok\n", keystr));
3611 return 0;
3614 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3615 struct tdb_validation_status *state)
3617 if (dbuf.dsize != 4) {
3618 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3619 "key %s (len %u != 4) ?\n",
3620 keystr, (unsigned int)dbuf.dsize));
3621 state->bad_entry = true;
3622 state->success = false;
3623 return 1;
3626 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3627 return 0;
3630 /***********************************************************************
3631 A list of all possible cache tdb keys with associated validation
3632 functions.
3633 ***********************************************************************/
3635 struct key_val_struct {
3636 const char *keyname;
3637 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3638 } key_val[] = {
3639 {"SEQNUM/", validate_seqnum},
3640 {"NS/", validate_ns},
3641 {"SN/", validate_sn},
3642 {"U/", validate_u},
3643 {"LOC_POL/", validate_loc_pol},
3644 {"PWD_POL/", validate_pwd_pol},
3645 {"CRED/", validate_cred},
3646 {"UL/", validate_ul},
3647 {"GL/", validate_gl},
3648 {"UG/", validate_ug},
3649 {"UA", validate_ua},
3650 {"GM/", validate_gm},
3651 {"DR/", validate_dr},
3652 {"DE/", validate_de},
3653 {"NSS/PWINFO/", validate_pwinfo},
3654 {"TRUSTDOMS/", validate_trustdoms},
3655 {"TRUSTDOMCACHE/", validate_trustdomcache},
3656 {"NSS/NA/", validate_nss_na},
3657 {"NSS/AN/", validate_nss_an},
3658 {"WINBINDD_OFFLINE", validate_offline},
3659 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3660 {NULL, NULL}
3663 /***********************************************************************
3664 Function to look at every entry in the tdb and validate it as far as
3665 possible.
3666 ***********************************************************************/
3668 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3670 int i;
3671 unsigned int max_key_len = 1024;
3672 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3674 /* Paranoia check. */
3675 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3676 max_key_len = 1024 * 1024;
3678 if (kbuf.dsize > max_key_len) {
3679 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3680 "(%u) > (%u)\n\n",
3681 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3682 return 1;
3685 for (i = 0; key_val[i].keyname; i++) {
3686 size_t namelen = strlen(key_val[i].keyname);
3687 if (kbuf.dsize >= namelen && (
3688 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3689 TALLOC_CTX *mem_ctx;
3690 char *keystr;
3691 int ret;
3693 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3694 if (!keystr) {
3695 return 1;
3697 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3698 keystr[kbuf.dsize] = '\0';
3700 mem_ctx = talloc_init("validate_ctx");
3701 if (!mem_ctx) {
3702 SAFE_FREE(keystr);
3703 return 1;
3706 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3707 v_state);
3709 SAFE_FREE(keystr);
3710 talloc_destroy(mem_ctx);
3711 return ret;
3715 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3716 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3717 DEBUG(0,("data :\n"));
3718 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3719 v_state->unknown_key = true;
3720 v_state->success = false;
3721 return 1; /* terminate. */
3724 static void validate_panic(const char *const why)
3726 DEBUG(0,("validating cache: would panic %s\n", why ));
3727 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3728 exit(47);
3731 /***********************************************************************
3732 Try and validate every entry in the winbindd cache. If we fail here,
3733 delete the cache tdb and return non-zero.
3734 ***********************************************************************/
3736 int winbindd_validate_cache(void)
3738 int ret = -1;
3739 const char *tdb_path = cache_path("winbindd_cache.tdb");
3740 TDB_CONTEXT *tdb = NULL;
3742 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3743 smb_panic_fn = validate_panic;
3746 tdb = tdb_open_log(tdb_path,
3747 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3748 ( lp_winbind_offline_logon()
3749 ? TDB_DEFAULT
3750 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3751 O_RDWR|O_CREAT,
3752 0600);
3753 if (!tdb) {
3754 DEBUG(0, ("winbindd_validate_cache: "
3755 "error opening/initializing tdb\n"));
3756 goto done;
3758 tdb_close(tdb);
3760 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3762 if (ret != 0) {
3763 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3764 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3765 unlink(tdb_path);
3768 done:
3769 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3770 smb_panic_fn = smb_panic;
3771 return ret;
3774 /***********************************************************************
3775 Try and validate every entry in the winbindd cache.
3776 ***********************************************************************/
3778 int winbindd_validate_cache_nobackup(void)
3780 int ret = -1;
3781 const char *tdb_path = cache_path("winbindd_cache.tdb");
3783 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3784 smb_panic_fn = validate_panic;
3787 if (wcache == NULL || wcache->tdb == NULL) {
3788 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3789 } else {
3790 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3793 if (ret != 0) {
3794 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3795 "successful.\n"));
3798 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3799 "function\n"));
3800 smb_panic_fn = smb_panic;
3801 return ret;
3804 bool winbindd_cache_validate_and_initialize(void)
3806 close_winbindd_cache();
3808 if (lp_winbind_offline_logon()) {
3809 if (winbindd_validate_cache() < 0) {
3810 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3811 "could be restored.\n"));
3815 return initialize_winbindd_cache();
3818 /*********************************************************************
3819 ********************************************************************/
3821 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3822 struct winbindd_tdc_domain **domains,
3823 size_t *num_domains )
3825 struct winbindd_tdc_domain *list = NULL;
3826 size_t idx;
3827 int i;
3828 bool set_only = false;
3830 /* don't allow duplicates */
3832 idx = *num_domains;
3833 list = *domains;
3835 for ( i=0; i< (*num_domains); i++ ) {
3836 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3837 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3838 new_dom->name));
3839 idx = i;
3840 set_only = true;
3842 break;
3846 if ( !set_only ) {
3847 if ( !*domains ) {
3848 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3849 idx = 0;
3850 } else {
3851 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3852 struct winbindd_tdc_domain,
3853 (*num_domains)+1);
3854 idx = *num_domains;
3857 ZERO_STRUCT( list[idx] );
3860 if ( !list )
3861 return false;
3863 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3864 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3866 if ( !is_null_sid( &new_dom->sid ) ) {
3867 sid_copy( &list[idx].sid, &new_dom->sid );
3868 } else {
3869 sid_copy(&list[idx].sid, &global_sid_NULL);
3872 if ( new_dom->domain_flags != 0x0 )
3873 list[idx].trust_flags = new_dom->domain_flags;
3875 if ( new_dom->domain_type != 0x0 )
3876 list[idx].trust_type = new_dom->domain_type;
3878 if ( new_dom->domain_trust_attribs != 0x0 )
3879 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3881 if ( !set_only ) {
3882 *domains = list;
3883 *num_domains = idx + 1;
3886 return true;
3889 /*********************************************************************
3890 ********************************************************************/
3892 static TDB_DATA make_tdc_key( const char *domain_name )
3894 char *keystr = NULL;
3895 TDB_DATA key = { NULL, 0 };
3897 if ( !domain_name ) {
3898 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3899 return key;
3902 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3903 return key;
3905 key = string_term_tdb_data(keystr);
3907 return key;
3910 /*********************************************************************
3911 ********************************************************************/
3913 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3914 size_t num_domains,
3915 unsigned char **buf )
3917 unsigned char *buffer = NULL;
3918 int len = 0;
3919 int buflen = 0;
3920 int i = 0;
3922 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3923 (int)num_domains));
3925 buflen = 0;
3927 again:
3928 len = 0;
3930 /* Store the number of array items first */
3931 len += tdb_pack( buffer+len, buflen-len, "d",
3932 num_domains );
3934 /* now pack each domain trust record */
3935 for ( i=0; i<num_domains; i++ ) {
3937 fstring tmp;
3939 if ( buflen > 0 ) {
3940 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3941 domains[i].domain_name,
3942 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3945 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3946 domains[i].domain_name,
3947 domains[i].dns_name,
3948 sid_to_fstring(tmp, &domains[i].sid),
3949 domains[i].trust_flags,
3950 domains[i].trust_attribs,
3951 domains[i].trust_type );
3954 if ( buflen < len ) {
3955 SAFE_FREE(buffer);
3956 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3957 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3958 buflen = -1;
3959 goto done;
3961 buflen = len;
3962 goto again;
3965 *buf = buffer;
3967 done:
3968 return buflen;
3971 /*********************************************************************
3972 ********************************************************************/
3974 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3975 struct winbindd_tdc_domain **domains )
3977 fstring domain_name, dns_name, sid_string;
3978 uint32 type, attribs, flags;
3979 int num_domains;
3980 int len = 0;
3981 int i;
3982 struct winbindd_tdc_domain *list = NULL;
3984 /* get the number of domains */
3985 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3986 if ( len == -1 ) {
3987 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3988 return 0;
3991 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3992 if ( !list ) {
3993 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3994 return 0;
3997 for ( i=0; i<num_domains; i++ ) {
3998 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3999 domain_name,
4000 dns_name,
4001 sid_string,
4002 &flags,
4003 &attribs,
4004 &type );
4006 if ( len == -1 ) {
4007 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4008 TALLOC_FREE( list );
4009 return 0;
4012 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4013 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4014 domain_name, dns_name, sid_string,
4015 flags, attribs, type));
4017 list[i].domain_name = talloc_strdup( list, domain_name );
4018 list[i].dns_name = talloc_strdup( list, dns_name );
4019 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4020 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4021 domain_name));
4023 list[i].trust_flags = flags;
4024 list[i].trust_attribs = attribs;
4025 list[i].trust_type = type;
4028 *domains = list;
4030 return num_domains;
4033 /*********************************************************************
4034 ********************************************************************/
4036 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4038 TDB_DATA key = make_tdc_key( lp_workgroup() );
4039 TDB_DATA data = { NULL, 0 };
4040 int ret;
4042 if ( !key.dptr )
4043 return false;
4045 /* See if we were asked to delete the cache entry */
4047 if ( !domains ) {
4048 ret = tdb_delete( wcache->tdb, key );
4049 goto done;
4052 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4054 if ( !data.dptr ) {
4055 ret = -1;
4056 goto done;
4059 ret = tdb_store( wcache->tdb, key, data, 0 );
4061 done:
4062 SAFE_FREE( data.dptr );
4063 SAFE_FREE( key.dptr );
4065 return ( ret != -1 );
4068 /*********************************************************************
4069 ********************************************************************/
4071 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4073 TDB_DATA key = make_tdc_key( lp_workgroup() );
4074 TDB_DATA data = { NULL, 0 };
4076 *domains = NULL;
4077 *num_domains = 0;
4079 if ( !key.dptr )
4080 return false;
4082 data = tdb_fetch( wcache->tdb, key );
4084 SAFE_FREE( key.dptr );
4086 if ( !data.dptr )
4087 return false;
4089 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4091 SAFE_FREE( data.dptr );
4093 if ( !*domains )
4094 return false;
4096 return true;
4099 /*********************************************************************
4100 ********************************************************************/
4102 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4104 struct winbindd_tdc_domain *dom_list = NULL;
4105 size_t num_domains = 0;
4106 bool ret = false;
4108 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4109 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4110 domain->name, domain->alt_name,
4111 sid_string_dbg(&domain->sid),
4112 domain->domain_flags,
4113 domain->domain_trust_attribs,
4114 domain->domain_type));
4116 if ( !init_wcache() ) {
4117 return false;
4120 /* fetch the list */
4122 wcache_tdc_fetch_list( &dom_list, &num_domains );
4124 /* add the new domain */
4126 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4127 goto done;
4130 /* pack the domain */
4132 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4133 goto done;
4136 /* Success */
4138 ret = true;
4139 done:
4140 TALLOC_FREE( dom_list );
4142 return ret;
4145 /*********************************************************************
4146 ********************************************************************/
4148 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4150 struct winbindd_tdc_domain *dom_list = NULL;
4151 size_t num_domains = 0;
4152 int i;
4153 struct winbindd_tdc_domain *d = NULL;
4155 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4157 if ( !init_wcache() ) {
4158 return false;
4161 /* fetch the list */
4163 wcache_tdc_fetch_list( &dom_list, &num_domains );
4165 for ( i=0; i<num_domains; i++ ) {
4166 if ( strequal(name, dom_list[i].domain_name) ||
4167 strequal(name, dom_list[i].dns_name) )
4169 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4170 name));
4172 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4173 if ( !d )
4174 break;
4176 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4177 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4178 sid_copy( &d->sid, &dom_list[i].sid );
4179 d->trust_flags = dom_list[i].trust_flags;
4180 d->trust_type = dom_list[i].trust_type;
4181 d->trust_attribs = dom_list[i].trust_attribs;
4183 break;
4187 TALLOC_FREE( dom_list );
4189 return d;
4193 /*********************************************************************
4194 ********************************************************************/
4196 void wcache_tdc_clear( void )
4198 if ( !init_wcache() )
4199 return;
4201 wcache_tdc_store_list( NULL, 0 );
4203 return;
4207 /*********************************************************************
4208 ********************************************************************/
4210 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4211 NTSTATUS status,
4212 const DOM_SID *user_sid,
4213 const char *homedir,
4214 const char *shell,
4215 const char *gecos,
4216 uint32 gid)
4218 struct cache_entry *centry;
4219 fstring tmp;
4221 if ( (centry = centry_start(domain, status)) == NULL )
4222 return;
4224 centry_put_string( centry, homedir );
4225 centry_put_string( centry, shell );
4226 centry_put_string( centry, gecos );
4227 centry_put_uint32( centry, gid );
4229 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4231 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4233 centry_free(centry);
4236 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4237 const DOM_SID *user_sid,
4238 TALLOC_CTX *ctx,
4239 ADS_STRUCT *ads, LDAPMessage *msg,
4240 const char **homedir, const char **shell,
4241 const char **gecos, gid_t *p_gid)
4243 struct winbind_cache *cache = get_cache(domain);
4244 struct cache_entry *centry = NULL;
4245 NTSTATUS nt_status;
4246 fstring tmp;
4248 if (!cache->tdb)
4249 goto do_query;
4251 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4252 sid_to_fstring(tmp, user_sid));
4254 if (!centry)
4255 goto do_query;
4257 *homedir = centry_string( centry, ctx );
4258 *shell = centry_string( centry, ctx );
4259 *gecos = centry_string( centry, ctx );
4260 *p_gid = centry_uint32( centry );
4262 centry_free(centry);
4264 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4265 sid_string_dbg(user_sid)));
4267 return NT_STATUS_OK;
4269 do_query:
4271 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4272 homedir, shell, gecos, p_gid );
4274 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4276 if ( NT_STATUS_IS_OK(nt_status) ) {
4277 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4278 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4279 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4280 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4282 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4283 *homedir, *shell, *gecos, *p_gid );
4286 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4287 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4288 domain->name ));
4289 set_domain_offline( domain );
4292 return nt_status;
4296 /* the cache backend methods are exposed via this structure */
4297 struct winbindd_methods cache_methods = {
4298 true,
4299 query_user_list,
4300 enum_dom_groups,
4301 enum_local_groups,
4302 name_to_sid,
4303 sid_to_name,
4304 rids_to_names,
4305 query_user,
4306 lookup_usergroups,
4307 lookup_useraliases,
4308 lookup_groupmem,
4309 sequence_number,
4310 lockout_policy,
4311 password_policy,
4312 trusted_domains
4315 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4316 uint32_t opnum, const DATA_BLOB *req,
4317 TDB_DATA *pkey)
4319 char *key;
4320 size_t keylen;
4322 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4323 if (key == NULL) {
4324 return false;
4326 keylen = talloc_get_size(key) - 1;
4328 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4329 if (key == NULL) {
4330 return false;
4332 memcpy(key + keylen, req->data, req->length);
4334 pkey->dptr = (uint8_t *)key;
4335 pkey->dsize = talloc_get_size(key);
4336 return true;
4339 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4340 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4342 TDB_DATA key, data;
4343 bool ret = false;
4345 if (wcache->tdb == NULL) {
4346 return false;
4349 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4350 return false;
4352 data = tdb_fetch(wcache->tdb, key);
4353 TALLOC_FREE(key.dptr);
4355 if (data.dptr == NULL) {
4356 return false;
4358 if (data.dsize < 4) {
4359 goto fail;
4362 if (IS_DOMAIN_ONLINE(domain)) {
4363 uint32_t entry_seqnum, dom_seqnum, last_check;
4365 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4366 &last_check)) {
4367 goto fail;
4369 entry_seqnum = IVAL(data.dptr, 0);
4370 if (entry_seqnum != dom_seqnum) {
4371 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4372 (int)entry_seqnum));
4373 goto fail;
4377 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4378 data.dsize - 4);
4379 if (resp->data == NULL) {
4380 DEBUG(10, ("talloc failed\n"));
4381 goto fail;
4383 resp->length = data.dsize - 4;
4385 ret = true;
4386 fail:
4387 SAFE_FREE(data.dptr);
4388 return ret;
4391 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4392 const DATA_BLOB *req, const DATA_BLOB *resp)
4394 TDB_DATA key, data;
4395 uint32_t dom_seqnum, last_check;
4397 if (wcache->tdb == NULL) {
4398 return;
4401 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4402 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4403 domain->name));
4404 return;
4407 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4408 return;
4411 data.dsize = resp->length + 4;
4412 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4413 if (data.dptr == NULL) {
4414 goto done;
4417 SIVAL(data.dptr, 0, dom_seqnum);
4418 memcpy(data.dptr+4, resp->data, resp->length);
4420 tdb_store(wcache->tdb, key, data, 0);
4422 done:
4423 TALLOC_FREE(key.dptr);
4424 return;