s3:winbind: Add a generic cache for NDR based parent-child requests
[Samba/aatanasov.git] / source3 / winbindd / winbindd_cache.c
blobe8f928867b53e5d089c971a2368fb43f0d9c1bc2
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 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
463 TDB_DATA data;
464 fstring key_str;
465 uint8 buf[8];
467 if (!wcache->tdb) {
468 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
469 return NT_STATUS_UNSUCCESSFUL;
472 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
474 SIVAL(buf, 0, domain->sequence_number);
475 SIVAL(buf, 4, domain->last_seq_check);
476 data.dptr = buf;
477 data.dsize = 8;
479 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
480 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
481 return NT_STATUS_UNSUCCESSFUL;
484 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
485 domain->name, domain->sequence_number,
486 (uint32)domain->last_seq_check));
488 return NT_STATUS_OK;
492 refresh the domain sequence number. If force is true
493 then always refresh it, no matter how recently we fetched it
496 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
498 NTSTATUS status;
499 unsigned time_diff;
500 time_t t = time(NULL);
501 unsigned cache_time = lp_winbind_cache_time();
503 if ( IS_DOMAIN_OFFLINE(domain) ) {
504 return;
507 get_cache( domain );
509 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
510 /* trying to reconnect is expensive, don't do it too often */
511 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
512 cache_time *= 8;
514 #endif
516 time_diff = t - domain->last_seq_check;
518 /* see if we have to refetch the domain sequence number */
519 if (!force && (time_diff < cache_time) &&
520 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
521 NT_STATUS_IS_OK(domain->last_status)) {
522 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
523 goto done;
526 /* try to get the sequence number from the tdb cache first */
527 /* this will update the timestamp as well */
529 status = fetch_cache_seqnum( domain, t );
530 if (NT_STATUS_IS_OK(status) &&
531 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
532 NT_STATUS_IS_OK(domain->last_status)) {
533 goto done;
536 /* important! make sure that we know if this is a native
537 mode domain or not. And that we can contact it. */
539 if ( winbindd_can_contact_domain( domain ) ) {
540 status = domain->backend->sequence_number(domain,
541 &domain->sequence_number);
542 } else {
543 /* just use the current time */
544 status = NT_STATUS_OK;
545 domain->sequence_number = time(NULL);
549 /* the above call could have set our domain->backend to NULL when
550 * coming from offline to online mode, make sure to reinitialize the
551 * backend - Guenther */
552 get_cache( domain );
554 if (!NT_STATUS_IS_OK(status)) {
555 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
556 domain->sequence_number = DOM_SEQUENCE_NONE;
559 domain->last_status = status;
560 domain->last_seq_check = time(NULL);
562 /* save the new sequence number in the cache */
563 store_cache_seqnum( domain );
565 done:
566 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
567 domain->name, domain->sequence_number));
569 return;
573 decide if a cache entry has expired
575 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
577 /* If we've been told to be offline - stay in that state... */
578 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
579 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
580 keystr, domain->name ));
581 return false;
584 /* when the domain is offline return the cached entry.
585 * This deals with transient offline states... */
587 if (!domain->online) {
588 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
589 keystr, domain->name ));
590 return false;
593 /* if the server is OK and our cache entry came from when it was down then
594 the entry is invalid */
595 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
596 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
597 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
598 keystr, domain->name ));
599 return true;
602 /* if the server is down or the cache entry is not older than the
603 current sequence number then it is OK */
604 if (wcache_server_down(domain) ||
605 centry->sequence_number == domain->sequence_number) {
606 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
607 keystr, domain->name ));
608 return false;
611 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
612 keystr, domain->name ));
614 /* it's expired */
615 return true;
618 static struct cache_entry *wcache_fetch_raw(char *kstr)
620 TDB_DATA data;
621 struct cache_entry *centry;
622 TDB_DATA key;
624 key = string_tdb_data(kstr);
625 data = tdb_fetch(wcache->tdb, key);
626 if (!data.dptr) {
627 /* a cache miss */
628 return NULL;
631 centry = SMB_XMALLOC_P(struct cache_entry);
632 centry->data = (unsigned char *)data.dptr;
633 centry->len = data.dsize;
634 centry->ofs = 0;
636 if (centry->len < 8) {
637 /* huh? corrupt cache? */
638 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
639 centry_free(centry);
640 return NULL;
643 centry->status = centry_ntstatus(centry);
644 centry->sequence_number = centry_uint32(centry);
646 return centry;
650 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
651 number and return status
653 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
654 struct winbindd_domain *domain,
655 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
656 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
657 struct winbindd_domain *domain,
658 const char *format, ...)
660 va_list ap;
661 char *kstr;
662 struct cache_entry *centry;
664 if (!winbindd_use_cache()) {
665 return NULL;
668 refresh_sequence_number(domain, false);
670 va_start(ap, format);
671 smb_xvasprintf(&kstr, format, ap);
672 va_end(ap);
674 centry = wcache_fetch_raw(kstr);
675 if (centry == NULL) {
676 free(kstr);
677 return NULL;
680 if (centry_expired(domain, kstr, centry)) {
682 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
683 kstr, domain->name ));
685 centry_free(centry);
686 free(kstr);
687 return NULL;
690 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
691 kstr, domain->name ));
693 free(kstr);
694 return centry;
697 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
698 static void wcache_delete(const char *format, ...)
700 va_list ap;
701 char *kstr;
702 TDB_DATA key;
704 va_start(ap, format);
705 smb_xvasprintf(&kstr, format, ap);
706 va_end(ap);
708 key = string_tdb_data(kstr);
710 tdb_delete(wcache->tdb, key);
711 free(kstr);
715 make sure we have at least len bytes available in a centry
717 static void centry_expand(struct cache_entry *centry, uint32 len)
719 if (centry->len - centry->ofs >= len)
720 return;
721 centry->len *= 2;
722 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
723 centry->len);
724 if (!centry->data) {
725 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
726 smb_panic_fn("out of memory in centry_expand");
731 push a uint32 into a centry
733 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
735 centry_expand(centry, 4);
736 SIVAL(centry->data, centry->ofs, v);
737 centry->ofs += 4;
741 push a uint16 into a centry
743 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
745 centry_expand(centry, 2);
746 SIVAL(centry->data, centry->ofs, v);
747 centry->ofs += 2;
751 push a uint8 into a centry
753 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
755 centry_expand(centry, 1);
756 SCVAL(centry->data, centry->ofs, v);
757 centry->ofs += 1;
761 push a string into a centry
763 static void centry_put_string(struct cache_entry *centry, const char *s)
765 int len;
767 if (!s) {
768 /* null strings are marked as len 0xFFFF */
769 centry_put_uint8(centry, 0xFF);
770 return;
773 len = strlen(s);
774 /* can't handle more than 254 char strings. Truncating is probably best */
775 if (len > 254) {
776 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
777 len = 254;
779 centry_put_uint8(centry, len);
780 centry_expand(centry, len);
781 memcpy(centry->data + centry->ofs, s, len);
782 centry->ofs += len;
786 push a 16 byte hash into a centry - treat as 16 byte string.
788 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
790 centry_put_uint8(centry, 16);
791 centry_expand(centry, 16);
792 memcpy(centry->data + centry->ofs, val, 16);
793 centry->ofs += 16;
796 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
798 fstring sid_string;
799 centry_put_string(centry, sid_to_fstring(sid_string, sid));
804 put NTSTATUS into a centry
806 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
808 uint32 status_value = NT_STATUS_V(status);
809 centry_put_uint32(centry, status_value);
814 push a NTTIME into a centry
816 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
818 centry_expand(centry, 8);
819 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
820 centry->ofs += 4;
821 SIVAL(centry->data, centry->ofs, nt >> 32);
822 centry->ofs += 4;
826 push a time_t into a centry - use a 64 bit size.
827 NTTIME here is being used as a convenient 64-bit size.
829 static void centry_put_time(struct cache_entry *centry, time_t t)
831 NTTIME nt = (NTTIME)t;
832 centry_put_nttime(centry, nt);
836 start a centry for output. When finished, call centry_end()
838 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
840 struct cache_entry *centry;
842 if (!wcache->tdb)
843 return NULL;
845 centry = SMB_XMALLOC_P(struct cache_entry);
847 centry->len = 8192; /* reasonable default */
848 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
849 centry->ofs = 0;
850 centry->sequence_number = domain->sequence_number;
851 centry_put_ntstatus(centry, status);
852 centry_put_uint32(centry, centry->sequence_number);
853 return centry;
857 finish a centry and write it to the tdb
859 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
860 static void centry_end(struct cache_entry *centry, const char *format, ...)
862 va_list ap;
863 char *kstr;
864 TDB_DATA key, data;
866 if (!winbindd_use_cache()) {
867 return;
870 va_start(ap, format);
871 smb_xvasprintf(&kstr, format, ap);
872 va_end(ap);
874 key = string_tdb_data(kstr);
875 data.dptr = centry->data;
876 data.dsize = centry->ofs;
878 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
879 free(kstr);
882 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
883 NTSTATUS status, const char *domain_name,
884 const char *name, const DOM_SID *sid,
885 enum lsa_SidType type)
887 struct cache_entry *centry;
888 fstring uname;
890 centry = centry_start(domain, status);
891 if (!centry)
892 return;
893 centry_put_uint32(centry, type);
894 centry_put_sid(centry, sid);
895 fstrcpy(uname, name);
896 strupper_m(uname);
897 centry_end(centry, "NS/%s/%s", domain_name, uname);
898 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
899 uname, sid_string_dbg(sid), nt_errstr(status)));
900 centry_free(centry);
903 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
904 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
906 struct cache_entry *centry;
907 fstring sid_string;
909 centry = centry_start(domain, status);
910 if (!centry)
911 return;
913 if (NT_STATUS_IS_OK(status)) {
914 centry_put_uint32(centry, type);
915 centry_put_string(centry, domain_name);
916 centry_put_string(centry, name);
919 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
920 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
921 name, nt_errstr(status)));
922 centry_free(centry);
926 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
927 struct wbint_userinfo *info)
929 struct cache_entry *centry;
930 fstring sid_string;
932 if (is_null_sid(&info->user_sid)) {
933 return;
936 centry = centry_start(domain, status);
937 if (!centry)
938 return;
939 centry_put_string(centry, info->acct_name);
940 centry_put_string(centry, info->full_name);
941 centry_put_string(centry, info->homedir);
942 centry_put_string(centry, info->shell);
943 centry_put_uint32(centry, info->primary_gid);
944 centry_put_sid(centry, &info->user_sid);
945 centry_put_sid(centry, &info->group_sid);
946 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
947 &info->user_sid));
948 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
949 centry_free(centry);
952 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
953 NTSTATUS status,
954 struct samr_DomInfo12 *lockout_policy)
956 struct cache_entry *centry;
958 centry = centry_start(domain, status);
959 if (!centry)
960 return;
962 centry_put_nttime(centry, lockout_policy->lockout_duration);
963 centry_put_nttime(centry, lockout_policy->lockout_window);
964 centry_put_uint16(centry, lockout_policy->lockout_threshold);
966 centry_end(centry, "LOC_POL/%s", domain->name);
968 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
970 centry_free(centry);
975 static void wcache_save_password_policy(struct winbindd_domain *domain,
976 NTSTATUS status,
977 struct samr_DomInfo1 *policy)
979 struct cache_entry *centry;
981 centry = centry_start(domain, status);
982 if (!centry)
983 return;
985 centry_put_uint16(centry, policy->min_password_length);
986 centry_put_uint16(centry, policy->password_history_length);
987 centry_put_uint32(centry, policy->password_properties);
988 centry_put_nttime(centry, policy->max_password_age);
989 centry_put_nttime(centry, policy->min_password_age);
991 centry_end(centry, "PWD_POL/%s", domain->name);
993 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
995 centry_free(centry);
998 /***************************************************************************
999 ***************************************************************************/
1001 static void wcache_save_username_alias(struct winbindd_domain *domain,
1002 NTSTATUS status,
1003 const char *name, const char *alias)
1005 struct cache_entry *centry;
1006 fstring uname;
1008 if ( (centry = centry_start(domain, status)) == NULL )
1009 return;
1011 centry_put_string( centry, alias );
1013 fstrcpy(uname, name);
1014 strupper_m(uname);
1015 centry_end(centry, "NSS/NA/%s", uname);
1017 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1019 centry_free(centry);
1022 static void wcache_save_alias_username(struct winbindd_domain *domain,
1023 NTSTATUS status,
1024 const char *alias, const char *name)
1026 struct cache_entry *centry;
1027 fstring uname;
1029 if ( (centry = centry_start(domain, status)) == NULL )
1030 return;
1032 centry_put_string( centry, name );
1034 fstrcpy(uname, alias);
1035 strupper_m(uname);
1036 centry_end(centry, "NSS/AN/%s", uname);
1038 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1040 centry_free(centry);
1043 /***************************************************************************
1044 ***************************************************************************/
1046 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1047 struct winbindd_domain *domain,
1048 const char *name, char **alias )
1050 struct winbind_cache *cache = get_cache(domain);
1051 struct cache_entry *centry = NULL;
1052 NTSTATUS status;
1053 char *upper_name;
1055 if ( domain->internal )
1056 return NT_STATUS_NOT_SUPPORTED;
1058 if (!cache->tdb)
1059 goto do_query;
1061 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1062 return NT_STATUS_NO_MEMORY;
1063 strupper_m(upper_name);
1065 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1067 SAFE_FREE( upper_name );
1069 if (!centry)
1070 goto do_query;
1072 status = centry->status;
1074 if (!NT_STATUS_IS_OK(status)) {
1075 centry_free(centry);
1076 return status;
1079 *alias = centry_string( centry, mem_ctx );
1081 centry_free(centry);
1083 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1084 name, *alias ? *alias : "(none)"));
1086 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1088 do_query:
1090 /* If its not in cache and we are offline, then fail */
1092 if ( get_global_winbindd_state_offline() || !domain->online ) {
1093 DEBUG(8,("resolve_username_to_alias: rejecting query "
1094 "in offline mode\n"));
1095 return NT_STATUS_NOT_FOUND;
1098 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1100 if ( NT_STATUS_IS_OK( status ) ) {
1101 wcache_save_username_alias(domain, status, name, *alias);
1104 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1105 wcache_save_username_alias(domain, status, name, "(NULL)");
1108 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1109 nt_errstr(status)));
1111 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1112 set_domain_offline( domain );
1115 return status;
1118 /***************************************************************************
1119 ***************************************************************************/
1121 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1122 struct winbindd_domain *domain,
1123 const char *alias, char **name )
1125 struct winbind_cache *cache = get_cache(domain);
1126 struct cache_entry *centry = NULL;
1127 NTSTATUS status;
1128 char *upper_name;
1130 if ( domain->internal )
1131 return NT_STATUS_NOT_SUPPORTED;
1133 if (!cache->tdb)
1134 goto do_query;
1136 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1137 return NT_STATUS_NO_MEMORY;
1138 strupper_m(upper_name);
1140 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1142 SAFE_FREE( upper_name );
1144 if (!centry)
1145 goto do_query;
1147 status = centry->status;
1149 if (!NT_STATUS_IS_OK(status)) {
1150 centry_free(centry);
1151 return status;
1154 *name = centry_string( centry, mem_ctx );
1156 centry_free(centry);
1158 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1159 alias, *name ? *name : "(none)"));
1161 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1163 do_query:
1165 /* If its not in cache and we are offline, then fail */
1167 if ( get_global_winbindd_state_offline() || !domain->online ) {
1168 DEBUG(8,("resolve_alias_to_username: rejecting query "
1169 "in offline mode\n"));
1170 return NT_STATUS_NOT_FOUND;
1173 /* an alias cannot contain a domain prefix or '@' */
1175 if (strchr(alias, '\\') || strchr(alias, '@')) {
1176 DEBUG(10,("resolve_alias_to_username: skipping fully "
1177 "qualified name %s\n", alias));
1178 return NT_STATUS_OBJECT_NAME_INVALID;
1181 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1183 if ( NT_STATUS_IS_OK( status ) ) {
1184 wcache_save_alias_username( domain, status, alias, *name );
1187 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1188 wcache_save_alias_username(domain, status, alias, "(NULL)");
1191 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1192 nt_errstr(status)));
1194 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1195 set_domain_offline( domain );
1198 return status;
1201 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1203 struct winbind_cache *cache = get_cache(domain);
1204 TDB_DATA data;
1205 fstring key_str, tmp;
1206 uint32 rid;
1208 if (!cache->tdb) {
1209 return NT_STATUS_INTERNAL_DB_ERROR;
1212 if (is_null_sid(sid)) {
1213 return NT_STATUS_INVALID_SID;
1216 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1217 return NT_STATUS_INVALID_SID;
1220 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1222 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1223 if (!data.dptr) {
1224 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1227 SAFE_FREE(data.dptr);
1228 return NT_STATUS_OK;
1231 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1232 as new salted ones. */
1234 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1235 TALLOC_CTX *mem_ctx,
1236 const DOM_SID *sid,
1237 const uint8 **cached_nt_pass,
1238 const uint8 **cached_salt)
1240 struct winbind_cache *cache = get_cache(domain);
1241 struct cache_entry *centry = NULL;
1242 NTSTATUS status;
1243 time_t t;
1244 uint32 rid;
1245 fstring tmp;
1247 if (!cache->tdb) {
1248 return NT_STATUS_INTERNAL_DB_ERROR;
1251 if (is_null_sid(sid)) {
1252 return NT_STATUS_INVALID_SID;
1255 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1256 return NT_STATUS_INVALID_SID;
1259 /* Try and get a salted cred first. If we can't
1260 fall back to an unsalted cred. */
1262 centry = wcache_fetch(cache, domain, "CRED/%s",
1263 sid_to_fstring(tmp, sid));
1264 if (!centry) {
1265 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1266 sid_string_dbg(sid)));
1267 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1270 t = centry_time(centry);
1272 /* In the salted case this isn't actually the nt_hash itself,
1273 but the MD5 of the salt + nt_hash. Let the caller
1274 sort this out. It can tell as we only return the cached_salt
1275 if we are returning a salted cred. */
1277 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1278 if (*cached_nt_pass == NULL) {
1279 fstring sidstr;
1281 sid_to_fstring(sidstr, sid);
1283 /* Bad (old) cred cache. Delete and pretend we
1284 don't have it. */
1285 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1286 sidstr));
1287 wcache_delete("CRED/%s", sidstr);
1288 centry_free(centry);
1289 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1292 /* We only have 17 bytes more data in the salted cred case. */
1293 if (centry->len - centry->ofs == 17) {
1294 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1295 } else {
1296 *cached_salt = NULL;
1299 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1300 if (*cached_salt) {
1301 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1304 status = centry->status;
1306 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1307 sid_string_dbg(sid), nt_errstr(status) ));
1309 centry_free(centry);
1310 return status;
1313 /* Store creds for a SID - only writes out new salted ones. */
1315 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1316 TALLOC_CTX *mem_ctx,
1317 const DOM_SID *sid,
1318 const uint8 nt_pass[NT_HASH_LEN])
1320 struct cache_entry *centry;
1321 fstring sid_string;
1322 uint32 rid;
1323 uint8 cred_salt[NT_HASH_LEN];
1324 uint8 salted_hash[NT_HASH_LEN];
1326 if (is_null_sid(sid)) {
1327 return NT_STATUS_INVALID_SID;
1330 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1331 return NT_STATUS_INVALID_SID;
1334 centry = centry_start(domain, NT_STATUS_OK);
1335 if (!centry) {
1336 return NT_STATUS_INTERNAL_DB_ERROR;
1339 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1341 centry_put_time(centry, time(NULL));
1343 /* Create a salt and then salt the hash. */
1344 generate_random_buffer(cred_salt, NT_HASH_LEN);
1345 E_md5hash(cred_salt, nt_pass, salted_hash);
1347 centry_put_hash16(centry, salted_hash);
1348 centry_put_hash16(centry, cred_salt);
1349 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1351 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1353 centry_free(centry);
1355 return NT_STATUS_OK;
1359 /* Query display info. This is the basic user list fn */
1360 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1361 TALLOC_CTX *mem_ctx,
1362 uint32 *num_entries,
1363 struct wbint_userinfo **info)
1365 struct winbind_cache *cache = get_cache(domain);
1366 struct cache_entry *centry = NULL;
1367 NTSTATUS status;
1368 unsigned int i, retry;
1370 if (!cache->tdb)
1371 goto do_query;
1373 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1374 if (!centry)
1375 goto do_query;
1377 *num_entries = centry_uint32(centry);
1379 if (*num_entries == 0)
1380 goto do_cached;
1382 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1383 if (! (*info)) {
1384 smb_panic_fn("query_user_list out of memory");
1386 for (i=0; i<(*num_entries); i++) {
1387 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1388 (*info)[i].full_name = centry_string(centry, mem_ctx);
1389 (*info)[i].homedir = centry_string(centry, mem_ctx);
1390 (*info)[i].shell = centry_string(centry, mem_ctx);
1391 centry_sid(centry, &(*info)[i].user_sid);
1392 centry_sid(centry, &(*info)[i].group_sid);
1395 do_cached:
1396 status = centry->status;
1398 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1399 domain->name, nt_errstr(status) ));
1401 centry_free(centry);
1402 return status;
1404 do_query:
1405 *num_entries = 0;
1406 *info = NULL;
1408 /* Return status value returned by seq number check */
1410 if (!NT_STATUS_IS_OK(domain->last_status))
1411 return domain->last_status;
1413 /* Put the query_user_list() in a retry loop. There appears to be
1414 * some bug either with Windows 2000 or Samba's handling of large
1415 * rpc replies. This manifests itself as sudden disconnection
1416 * at a random point in the enumeration of a large (60k) user list.
1417 * The retry loop simply tries the operation again. )-: It's not
1418 * pretty but an acceptable workaround until we work out what the
1419 * real problem is. */
1421 retry = 0;
1422 do {
1424 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1425 domain->name ));
1427 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1428 if (!NT_STATUS_IS_OK(status)) {
1429 DEBUG(3, ("query_user_list: returned 0x%08x, "
1430 "retrying\n", NT_STATUS_V(status)));
1432 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1433 DEBUG(3, ("query_user_list: flushing "
1434 "connection cache\n"));
1435 invalidate_cm_connection(&domain->conn);
1438 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1439 (retry++ < 5));
1441 /* and save it */
1442 refresh_sequence_number(domain, false);
1443 centry = centry_start(domain, status);
1444 if (!centry)
1445 goto skip_save;
1446 centry_put_uint32(centry, *num_entries);
1447 for (i=0; i<(*num_entries); i++) {
1448 centry_put_string(centry, (*info)[i].acct_name);
1449 centry_put_string(centry, (*info)[i].full_name);
1450 centry_put_string(centry, (*info)[i].homedir);
1451 centry_put_string(centry, (*info)[i].shell);
1452 centry_put_sid(centry, &(*info)[i].user_sid);
1453 centry_put_sid(centry, &(*info)[i].group_sid);
1454 if (domain->backend && domain->backend->consistent) {
1455 /* when the backend is consistent we can pre-prime some mappings */
1456 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1457 domain->name,
1458 (*info)[i].acct_name,
1459 &(*info)[i].user_sid,
1460 SID_NAME_USER);
1461 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1462 &(*info)[i].user_sid,
1463 domain->name,
1464 (*info)[i].acct_name,
1465 SID_NAME_USER);
1466 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1469 centry_end(centry, "UL/%s", domain->name);
1470 centry_free(centry);
1472 skip_save:
1473 return status;
1476 /* list all domain groups */
1477 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1478 TALLOC_CTX *mem_ctx,
1479 uint32 *num_entries,
1480 struct acct_info **info)
1482 struct winbind_cache *cache = get_cache(domain);
1483 struct cache_entry *centry = NULL;
1484 NTSTATUS status;
1485 unsigned int i;
1487 if (!cache->tdb)
1488 goto do_query;
1490 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1491 if (!centry)
1492 goto do_query;
1494 *num_entries = centry_uint32(centry);
1496 if (*num_entries == 0)
1497 goto do_cached;
1499 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1500 if (! (*info)) {
1501 smb_panic_fn("enum_dom_groups out of memory");
1503 for (i=0; i<(*num_entries); i++) {
1504 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1505 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1506 (*info)[i].rid = centry_uint32(centry);
1509 do_cached:
1510 status = centry->status;
1512 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1513 domain->name, nt_errstr(status) ));
1515 centry_free(centry);
1516 return status;
1518 do_query:
1519 *num_entries = 0;
1520 *info = NULL;
1522 /* Return status value returned by seq number check */
1524 if (!NT_STATUS_IS_OK(domain->last_status))
1525 return domain->last_status;
1527 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1528 domain->name ));
1530 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1532 /* and save it */
1533 refresh_sequence_number(domain, false);
1534 centry = centry_start(domain, status);
1535 if (!centry)
1536 goto skip_save;
1537 centry_put_uint32(centry, *num_entries);
1538 for (i=0; i<(*num_entries); i++) {
1539 centry_put_string(centry, (*info)[i].acct_name);
1540 centry_put_string(centry, (*info)[i].acct_desc);
1541 centry_put_uint32(centry, (*info)[i].rid);
1543 centry_end(centry, "GL/%s/domain", domain->name);
1544 centry_free(centry);
1546 skip_save:
1547 return status;
1550 /* list all domain groups */
1551 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1552 TALLOC_CTX *mem_ctx,
1553 uint32 *num_entries,
1554 struct acct_info **info)
1556 struct winbind_cache *cache = get_cache(domain);
1557 struct cache_entry *centry = NULL;
1558 NTSTATUS status;
1559 unsigned int i;
1561 if (!cache->tdb)
1562 goto do_query;
1564 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1565 if (!centry)
1566 goto do_query;
1568 *num_entries = centry_uint32(centry);
1570 if (*num_entries == 0)
1571 goto do_cached;
1573 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1574 if (! (*info)) {
1575 smb_panic_fn("enum_dom_groups out of memory");
1577 for (i=0; i<(*num_entries); i++) {
1578 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1579 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1580 (*info)[i].rid = centry_uint32(centry);
1583 do_cached:
1585 /* If we are returning cached data and the domain controller
1586 is down then we don't know whether the data is up to date
1587 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1588 indicate this. */
1590 if (wcache_server_down(domain)) {
1591 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1592 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1593 } else
1594 status = centry->status;
1596 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1597 domain->name, nt_errstr(status) ));
1599 centry_free(centry);
1600 return status;
1602 do_query:
1603 *num_entries = 0;
1604 *info = NULL;
1606 /* Return status value returned by seq number check */
1608 if (!NT_STATUS_IS_OK(domain->last_status))
1609 return domain->last_status;
1611 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1612 domain->name ));
1614 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1616 /* and save it */
1617 refresh_sequence_number(domain, false);
1618 centry = centry_start(domain, status);
1619 if (!centry)
1620 goto skip_save;
1621 centry_put_uint32(centry, *num_entries);
1622 for (i=0; i<(*num_entries); i++) {
1623 centry_put_string(centry, (*info)[i].acct_name);
1624 centry_put_string(centry, (*info)[i].acct_desc);
1625 centry_put_uint32(centry, (*info)[i].rid);
1627 centry_end(centry, "GL/%s/local", domain->name);
1628 centry_free(centry);
1630 skip_save:
1631 return status;
1634 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1635 const char *domain_name,
1636 const char *name,
1637 struct dom_sid *sid,
1638 enum lsa_SidType *type)
1640 struct winbind_cache *cache = get_cache(domain);
1641 struct cache_entry *centry;
1642 NTSTATUS status;
1643 char *uname;
1645 if (cache->tdb == NULL) {
1646 return NT_STATUS_NOT_FOUND;
1649 uname = talloc_strdup_upper(talloc_tos(), name);
1650 if (uname == NULL) {
1651 return NT_STATUS_NO_MEMORY;
1654 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1655 TALLOC_FREE(uname);
1656 if (centry == NULL) {
1657 return NT_STATUS_NOT_FOUND;
1660 status = centry->status;
1661 if (NT_STATUS_IS_OK(status)) {
1662 *type = (enum lsa_SidType)centry_uint32(centry);
1663 centry_sid(centry, sid);
1666 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1667 "%s\n", domain->name, nt_errstr(status) ));
1669 centry_free(centry);
1670 return status;
1673 /* convert a single name to a sid in a domain */
1674 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1675 TALLOC_CTX *mem_ctx,
1676 const char *domain_name,
1677 const char *name,
1678 uint32_t flags,
1679 DOM_SID *sid,
1680 enum lsa_SidType *type)
1682 NTSTATUS status;
1684 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1685 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1686 return status;
1689 ZERO_STRUCTP(sid);
1691 /* If the seq number check indicated that there is a problem
1692 * with this DC, then return that status... except for
1693 * access_denied. This is special because the dc may be in
1694 * "restrict anonymous = 1" mode, in which case it will deny
1695 * most unauthenticated operations, but *will* allow the LSA
1696 * name-to-sid that we try as a fallback. */
1698 if (!(NT_STATUS_IS_OK(domain->last_status)
1699 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1700 return domain->last_status;
1702 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1703 domain->name ));
1705 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1706 name, flags, sid, type);
1708 /* and save it */
1709 refresh_sequence_number(domain, false);
1711 if (domain->online &&
1712 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1713 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1715 /* Only save the reverse mapping if this was not a UPN */
1716 if (!strchr(name, '@')) {
1717 strupper_m(CONST_DISCARD(char *,domain_name));
1718 strlower_m(CONST_DISCARD(char *,name));
1719 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1723 return status;
1726 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1727 const struct dom_sid *sid,
1728 TALLOC_CTX *mem_ctx,
1729 char **domain_name,
1730 char **name,
1731 enum lsa_SidType *type)
1733 struct winbind_cache *cache = get_cache(domain);
1734 struct cache_entry *centry;
1735 char *sid_string;
1736 NTSTATUS status;
1738 if (cache->tdb == NULL) {
1739 return NT_STATUS_NOT_FOUND;
1742 sid_string = sid_string_tos(sid);
1743 if (sid_string == NULL) {
1744 return NT_STATUS_NO_MEMORY;
1747 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1748 TALLOC_FREE(sid_string);
1749 if (centry == NULL) {
1750 return NT_STATUS_NOT_FOUND;
1753 if (NT_STATUS_IS_OK(centry->status)) {
1754 *type = (enum lsa_SidType)centry_uint32(centry);
1755 *domain_name = centry_string(centry, mem_ctx);
1756 *name = centry_string(centry, mem_ctx);
1759 status = centry->status;
1760 centry_free(centry);
1762 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1763 "%s\n", domain->name, nt_errstr(status) ));
1765 return status;
1768 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1769 given */
1770 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1771 TALLOC_CTX *mem_ctx,
1772 const DOM_SID *sid,
1773 char **domain_name,
1774 char **name,
1775 enum lsa_SidType *type)
1777 NTSTATUS status;
1779 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1780 type);
1781 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1782 return status;
1785 *name = NULL;
1786 *domain_name = NULL;
1788 /* If the seq number check indicated that there is a problem
1789 * with this DC, then return that status... except for
1790 * access_denied. This is special because the dc may be in
1791 * "restrict anonymous = 1" mode, in which case it will deny
1792 * most unauthenticated operations, but *will* allow the LSA
1793 * sid-to-name that we try as a fallback. */
1795 if (!(NT_STATUS_IS_OK(domain->last_status)
1796 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1797 return domain->last_status;
1799 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1800 domain->name ));
1802 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1804 /* and save it */
1805 refresh_sequence_number(domain, false);
1806 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1808 /* We can't save the name to sid mapping here, as with sid history a
1809 * later name2sid would give the wrong sid. */
1811 return status;
1814 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1815 TALLOC_CTX *mem_ctx,
1816 const DOM_SID *domain_sid,
1817 uint32 *rids,
1818 size_t num_rids,
1819 char **domain_name,
1820 char ***names,
1821 enum lsa_SidType **types)
1823 struct winbind_cache *cache = get_cache(domain);
1824 size_t i;
1825 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1826 bool have_mapped;
1827 bool have_unmapped;
1829 *domain_name = NULL;
1830 *names = NULL;
1831 *types = NULL;
1833 if (!cache->tdb) {
1834 goto do_query;
1837 if (num_rids == 0) {
1838 return NT_STATUS_OK;
1841 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1842 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1844 if ((*names == NULL) || (*types == NULL)) {
1845 result = NT_STATUS_NO_MEMORY;
1846 goto error;
1849 have_mapped = have_unmapped = false;
1851 for (i=0; i<num_rids; i++) {
1852 DOM_SID sid;
1853 struct cache_entry *centry;
1854 fstring tmp;
1856 if (!sid_compose(&sid, domain_sid, rids[i])) {
1857 result = NT_STATUS_INTERNAL_ERROR;
1858 goto error;
1861 centry = wcache_fetch(cache, domain, "SN/%s",
1862 sid_to_fstring(tmp, &sid));
1863 if (!centry) {
1864 goto do_query;
1867 (*types)[i] = SID_NAME_UNKNOWN;
1868 (*names)[i] = talloc_strdup(*names, "");
1870 if (NT_STATUS_IS_OK(centry->status)) {
1871 char *dom;
1872 have_mapped = true;
1873 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1875 dom = centry_string(centry, mem_ctx);
1876 if (*domain_name == NULL) {
1877 *domain_name = dom;
1878 } else {
1879 talloc_free(dom);
1882 (*names)[i] = centry_string(centry, *names);
1884 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1885 have_unmapped = true;
1887 } else {
1888 /* something's definitely wrong */
1889 result = centry->status;
1890 goto error;
1893 centry_free(centry);
1896 if (!have_mapped) {
1897 return NT_STATUS_NONE_MAPPED;
1899 if (!have_unmapped) {
1900 return NT_STATUS_OK;
1902 return STATUS_SOME_UNMAPPED;
1904 do_query:
1906 TALLOC_FREE(*names);
1907 TALLOC_FREE(*types);
1909 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1910 rids, num_rids, domain_name,
1911 names, types);
1914 None of the queried rids has been found so save all negative entries
1916 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1917 for (i = 0; i < num_rids; i++) {
1918 DOM_SID sid;
1919 const char *name = "";
1920 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1921 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1923 if (!sid_compose(&sid, domain_sid, rids[i])) {
1924 return NT_STATUS_INTERNAL_ERROR;
1927 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1928 name, type);
1931 return result;
1935 Some or all of the queried rids have been found.
1937 if (!NT_STATUS_IS_OK(result) &&
1938 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1939 return result;
1942 refresh_sequence_number(domain, false);
1944 for (i=0; i<num_rids; i++) {
1945 DOM_SID sid;
1946 NTSTATUS status;
1948 if (!sid_compose(&sid, domain_sid, rids[i])) {
1949 result = NT_STATUS_INTERNAL_ERROR;
1950 goto error;
1953 status = (*types)[i] == SID_NAME_UNKNOWN ?
1954 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1956 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1957 (*names)[i], (*types)[i]);
1960 return result;
1962 error:
1963 TALLOC_FREE(*names);
1964 TALLOC_FREE(*types);
1965 return result;
1968 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
1969 TALLOC_CTX *mem_ctx,
1970 const struct dom_sid *user_sid,
1971 struct wbint_userinfo *info)
1973 struct winbind_cache *cache = get_cache(domain);
1974 struct cache_entry *centry = NULL;
1975 NTSTATUS status;
1976 char *sid_string;
1978 if (cache->tdb == NULL) {
1979 return NT_STATUS_NOT_FOUND;
1982 sid_string = sid_string_tos(user_sid);
1983 if (sid_string == NULL) {
1984 return NT_STATUS_NO_MEMORY;
1987 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
1988 TALLOC_FREE(sid_string);
1989 if (centry == NULL) {
1990 return NT_STATUS_NOT_FOUND;
1994 * If we have an access denied cache entry and a cached info3
1995 * in the samlogon cache then do a query. This will force the
1996 * rpc back end to return the info3 data.
1999 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2000 netsamlogon_cache_have(user_sid)) {
2001 DEBUG(10, ("query_user: cached access denied and have cached "
2002 "info3\n"));
2003 domain->last_status = NT_STATUS_OK;
2004 centry_free(centry);
2005 return NT_STATUS_NOT_FOUND;
2008 /* if status is not ok then this is a negative hit
2009 and the rest of the data doesn't matter */
2010 status = centry->status;
2011 if (NT_STATUS_IS_OK(status)) {
2012 info->acct_name = centry_string(centry, mem_ctx);
2013 info->full_name = centry_string(centry, mem_ctx);
2014 info->homedir = centry_string(centry, mem_ctx);
2015 info->shell = centry_string(centry, mem_ctx);
2016 info->primary_gid = centry_uint32(centry);
2017 centry_sid(centry, &info->user_sid);
2018 centry_sid(centry, &info->group_sid);
2021 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2022 "%s\n", domain->name, nt_errstr(status) ));
2024 centry_free(centry);
2025 return status;
2028 /* Lookup user information from a rid */
2029 static NTSTATUS query_user(struct winbindd_domain *domain,
2030 TALLOC_CTX *mem_ctx,
2031 const DOM_SID *user_sid,
2032 struct wbint_userinfo *info)
2034 NTSTATUS status;
2036 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2037 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2038 return status;
2041 ZERO_STRUCTP(info);
2043 /* Return status value returned by seq number check */
2045 if (!NT_STATUS_IS_OK(domain->last_status))
2046 return domain->last_status;
2048 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2049 domain->name ));
2051 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2053 /* and save it */
2054 refresh_sequence_number(domain, false);
2055 wcache_save_user(domain, status, info);
2057 return status;
2060 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2061 TALLOC_CTX *mem_ctx,
2062 const struct dom_sid *user_sid,
2063 uint32_t *pnum_sids,
2064 struct dom_sid **psids)
2066 struct winbind_cache *cache = get_cache(domain);
2067 struct cache_entry *centry = NULL;
2068 NTSTATUS status;
2069 uint32_t i, num_sids;
2070 struct dom_sid *sids;
2071 fstring sid_string;
2073 if (cache->tdb == NULL) {
2074 return NT_STATUS_NOT_FOUND;
2077 centry = wcache_fetch(cache, domain, "UG/%s",
2078 sid_to_fstring(sid_string, user_sid));
2079 if (centry == NULL) {
2080 return NT_STATUS_NOT_FOUND;
2083 /* If we have an access denied cache entry and a cached info3 in the
2084 samlogon cache then do a query. This will force the rpc back end
2085 to return the info3 data. */
2087 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2088 && netsamlogon_cache_have(user_sid)) {
2089 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2090 "cached info3\n"));
2091 domain->last_status = NT_STATUS_OK;
2092 centry_free(centry);
2093 return NT_STATUS_NOT_FOUND;
2096 num_sids = centry_uint32(centry);
2097 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2098 if (sids == NULL) {
2099 return NT_STATUS_NO_MEMORY;
2102 for (i=0; i<num_sids; i++) {
2103 centry_sid(centry, &sids[i]);
2106 status = centry->status;
2108 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2109 "status: %s\n", domain->name, nt_errstr(status)));
2111 centry_free(centry);
2113 *pnum_sids = num_sids;
2114 *psids = sids;
2115 return status;
2118 /* Lookup groups a user is a member of. */
2119 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2120 TALLOC_CTX *mem_ctx,
2121 const DOM_SID *user_sid,
2122 uint32 *num_groups, DOM_SID **user_gids)
2124 struct cache_entry *centry = NULL;
2125 NTSTATUS status;
2126 unsigned int i;
2127 fstring sid_string;
2129 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2130 num_groups, user_gids);
2131 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2132 return status;
2135 (*num_groups) = 0;
2136 (*user_gids) = NULL;
2138 /* Return status value returned by seq number check */
2140 if (!NT_STATUS_IS_OK(domain->last_status))
2141 return domain->last_status;
2143 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2144 domain->name ));
2146 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2148 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2149 goto skip_save;
2151 /* and save it */
2152 refresh_sequence_number(domain, false);
2153 centry = centry_start(domain, status);
2154 if (!centry)
2155 goto skip_save;
2157 centry_put_uint32(centry, *num_groups);
2158 for (i=0; i<(*num_groups); i++) {
2159 centry_put_sid(centry, &(*user_gids)[i]);
2162 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2163 centry_free(centry);
2165 skip_save:
2166 return status;
2169 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2170 const struct dom_sid *sids)
2172 uint32_t i;
2173 char *sidlist;
2175 sidlist = talloc_strdup(mem_ctx, "");
2176 if (sidlist == NULL) {
2177 return NULL;
2179 for (i=0; i<num_sids; i++) {
2180 fstring tmp;
2181 sidlist = talloc_asprintf_append_buffer(
2182 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2183 if (sidlist == NULL) {
2184 return NULL;
2187 return sidlist;
2190 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2191 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2192 const struct dom_sid *sids,
2193 uint32_t *pnum_aliases, uint32_t **paliases)
2195 struct winbind_cache *cache = get_cache(domain);
2196 struct cache_entry *centry = NULL;
2197 uint32_t num_aliases;
2198 uint32_t *aliases;
2199 NTSTATUS status;
2200 char *sidlist;
2201 int i;
2203 if (cache->tdb == NULL) {
2204 return NT_STATUS_NOT_FOUND;
2207 if (num_sids == 0) {
2208 *pnum_aliases = 0;
2209 *paliases = NULL;
2210 return NT_STATUS_OK;
2213 /* We need to cache indexed by the whole list of SIDs, the aliases
2214 * resulting might come from any of the SIDs. */
2216 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2217 if (sidlist == NULL) {
2218 return NT_STATUS_NO_MEMORY;
2221 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2222 TALLOC_FREE(sidlist);
2223 if (centry == NULL) {
2224 return NT_STATUS_NOT_FOUND;
2227 num_aliases = centry_uint32(centry);
2228 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2229 if (aliases == NULL) {
2230 centry_free(centry);
2231 return NT_STATUS_NO_MEMORY;
2234 for (i=0; i<num_aliases; i++) {
2235 aliases[i] = centry_uint32(centry);
2238 status = centry->status;
2240 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2241 "status %s\n", domain->name, nt_errstr(status)));
2243 centry_free(centry);
2245 *pnum_aliases = num_aliases;
2246 *paliases = aliases;
2248 return status;
2251 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2252 TALLOC_CTX *mem_ctx,
2253 uint32 num_sids, const DOM_SID *sids,
2254 uint32 *num_aliases, uint32 **alias_rids)
2256 struct cache_entry *centry = NULL;
2257 NTSTATUS status;
2258 char *sidlist;
2259 int i;
2261 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2262 num_aliases, alias_rids);
2263 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2264 return status;
2267 (*num_aliases) = 0;
2268 (*alias_rids) = NULL;
2270 if (!NT_STATUS_IS_OK(domain->last_status))
2271 return domain->last_status;
2273 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2274 "for domain %s\n", domain->name ));
2276 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2277 if (sidlist == NULL) {
2278 return NT_STATUS_NO_MEMORY;
2281 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2282 num_sids, sids,
2283 num_aliases, alias_rids);
2285 /* and save it */
2286 refresh_sequence_number(domain, false);
2287 centry = centry_start(domain, status);
2288 if (!centry)
2289 goto skip_save;
2290 centry_put_uint32(centry, *num_aliases);
2291 for (i=0; i<(*num_aliases); i++)
2292 centry_put_uint32(centry, (*alias_rids)[i]);
2293 centry_end(centry, "UA%s", sidlist);
2294 centry_free(centry);
2296 skip_save:
2297 return status;
2300 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2301 TALLOC_CTX *mem_ctx,
2302 const struct dom_sid *group_sid,
2303 uint32_t *num_names,
2304 struct dom_sid **sid_mem, char ***names,
2305 uint32_t **name_types)
2307 struct winbind_cache *cache = get_cache(domain);
2308 struct cache_entry *centry = NULL;
2309 NTSTATUS status;
2310 unsigned int i;
2311 char *sid_string;
2313 if (cache->tdb == NULL) {
2314 return NT_STATUS_NOT_FOUND;
2317 sid_string = sid_string_tos(group_sid);
2318 if (sid_string == NULL) {
2319 return NT_STATUS_NO_MEMORY;
2322 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2323 TALLOC_FREE(sid_string);
2324 if (centry == NULL) {
2325 return NT_STATUS_NOT_FOUND;
2328 *sid_mem = NULL;
2329 *names = NULL;
2330 *name_types = NULL;
2332 *num_names = centry_uint32(centry);
2333 if (*num_names == 0) {
2334 centry_free(centry);
2335 return NT_STATUS_OK;
2338 *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2339 *names = talloc_array(mem_ctx, char *, *num_names);
2340 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2342 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2343 TALLOC_FREE(*sid_mem);
2344 TALLOC_FREE(*names);
2345 TALLOC_FREE(*name_types);
2346 centry_free(centry);
2347 return NT_STATUS_NO_MEMORY;
2350 for (i=0; i<(*num_names); i++) {
2351 centry_sid(centry, &(*sid_mem)[i]);
2352 (*names)[i] = centry_string(centry, mem_ctx);
2353 (*name_types)[i] = centry_uint32(centry);
2356 status = centry->status;
2358 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2359 "status: %s\n", domain->name, nt_errstr(status)));
2361 centry_free(centry);
2362 return status;
2365 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2366 TALLOC_CTX *mem_ctx,
2367 const DOM_SID *group_sid, uint32 *num_names,
2368 DOM_SID **sid_mem, char ***names,
2369 uint32 **name_types)
2371 struct cache_entry *centry = NULL;
2372 NTSTATUS status;
2373 unsigned int i;
2374 fstring sid_string;
2376 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2377 sid_mem, names, name_types);
2378 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2379 return status;
2382 (*num_names) = 0;
2383 (*sid_mem) = NULL;
2384 (*names) = NULL;
2385 (*name_types) = NULL;
2387 /* Return status value returned by seq number check */
2389 if (!NT_STATUS_IS_OK(domain->last_status))
2390 return domain->last_status;
2392 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2393 domain->name ));
2395 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2396 sid_mem, names, name_types);
2398 /* and save it */
2399 refresh_sequence_number(domain, false);
2400 centry = centry_start(domain, status);
2401 if (!centry)
2402 goto skip_save;
2403 centry_put_uint32(centry, *num_names);
2404 for (i=0; i<(*num_names); i++) {
2405 centry_put_sid(centry, &(*sid_mem)[i]);
2406 centry_put_string(centry, (*names)[i]);
2407 centry_put_uint32(centry, (*name_types)[i]);
2409 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2410 centry_free(centry);
2412 skip_save:
2413 return status;
2416 /* find the sequence number for a domain */
2417 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2419 refresh_sequence_number(domain, false);
2421 *seq = domain->sequence_number;
2423 return NT_STATUS_OK;
2426 /* enumerate trusted domains
2427 * (we need to have the list of trustdoms in the cache when we go offline) -
2428 * Guenther */
2429 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2430 TALLOC_CTX *mem_ctx,
2431 uint32 *num_domains,
2432 char ***names,
2433 char ***alt_names,
2434 DOM_SID **dom_sids)
2436 struct winbind_cache *cache = get_cache(domain);
2437 struct cache_entry *centry = NULL;
2438 NTSTATUS status;
2439 int i;
2441 if (!cache->tdb)
2442 goto do_query;
2444 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2446 if (!centry) {
2447 goto do_query;
2450 *num_domains = centry_uint32(centry);
2452 if (*num_domains) {
2453 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2454 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2455 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2457 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2458 smb_panic_fn("trusted_domains out of memory");
2460 } else {
2461 (*names) = NULL;
2462 (*alt_names) = NULL;
2463 (*dom_sids) = NULL;
2466 for (i=0; i<(*num_domains); i++) {
2467 (*names)[i] = centry_string(centry, mem_ctx);
2468 (*alt_names)[i] = centry_string(centry, mem_ctx);
2469 if (!centry_sid(centry, &(*dom_sids)[i])) {
2470 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2474 status = centry->status;
2476 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2477 domain->name, *num_domains, nt_errstr(status) ));
2479 centry_free(centry);
2480 return status;
2482 do_query:
2483 (*num_domains) = 0;
2484 (*dom_sids) = NULL;
2485 (*names) = NULL;
2486 (*alt_names) = NULL;
2488 /* Return status value returned by seq number check */
2490 if (!NT_STATUS_IS_OK(domain->last_status))
2491 return domain->last_status;
2493 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2494 domain->name ));
2496 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2497 names, alt_names, dom_sids);
2499 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2500 * so that the generic centry handling still applies correctly -
2501 * Guenther*/
2503 if (!NT_STATUS_IS_ERR(status)) {
2504 status = NT_STATUS_OK;
2508 #if 0 /* Disabled as we want the trust dom list to be managed by
2509 the main parent and always to make the query. --jerry */
2511 /* and save it */
2512 refresh_sequence_number(domain, false);
2514 centry = centry_start(domain, status);
2515 if (!centry)
2516 goto skip_save;
2518 centry_put_uint32(centry, *num_domains);
2520 for (i=0; i<(*num_domains); i++) {
2521 centry_put_string(centry, (*names)[i]);
2522 centry_put_string(centry, (*alt_names)[i]);
2523 centry_put_sid(centry, &(*dom_sids)[i]);
2526 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2528 centry_free(centry);
2530 skip_save:
2531 #endif
2533 return status;
2536 /* get lockout policy */
2537 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2538 TALLOC_CTX *mem_ctx,
2539 struct samr_DomInfo12 *policy)
2541 struct winbind_cache *cache = get_cache(domain);
2542 struct cache_entry *centry = NULL;
2543 NTSTATUS status;
2545 if (!cache->tdb)
2546 goto do_query;
2548 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2550 if (!centry)
2551 goto do_query;
2553 policy->lockout_duration = centry_nttime(centry);
2554 policy->lockout_window = centry_nttime(centry);
2555 policy->lockout_threshold = centry_uint16(centry);
2557 status = centry->status;
2559 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2560 domain->name, nt_errstr(status) ));
2562 centry_free(centry);
2563 return status;
2565 do_query:
2566 ZERO_STRUCTP(policy);
2568 /* Return status value returned by seq number check */
2570 if (!NT_STATUS_IS_OK(domain->last_status))
2571 return domain->last_status;
2573 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2574 domain->name ));
2576 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2578 /* and save it */
2579 refresh_sequence_number(domain, false);
2580 wcache_save_lockout_policy(domain, status, policy);
2582 return status;
2585 /* get password policy */
2586 static NTSTATUS password_policy(struct winbindd_domain *domain,
2587 TALLOC_CTX *mem_ctx,
2588 struct samr_DomInfo1 *policy)
2590 struct winbind_cache *cache = get_cache(domain);
2591 struct cache_entry *centry = NULL;
2592 NTSTATUS status;
2594 if (!cache->tdb)
2595 goto do_query;
2597 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2599 if (!centry)
2600 goto do_query;
2602 policy->min_password_length = centry_uint16(centry);
2603 policy->password_history_length = centry_uint16(centry);
2604 policy->password_properties = centry_uint32(centry);
2605 policy->max_password_age = centry_nttime(centry);
2606 policy->min_password_age = centry_nttime(centry);
2608 status = centry->status;
2610 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2611 domain->name, nt_errstr(status) ));
2613 centry_free(centry);
2614 return status;
2616 do_query:
2617 ZERO_STRUCTP(policy);
2619 /* Return status value returned by seq number check */
2621 if (!NT_STATUS_IS_OK(domain->last_status))
2622 return domain->last_status;
2624 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2625 domain->name ));
2627 status = domain->backend->password_policy(domain, mem_ctx, policy);
2629 /* and save it */
2630 refresh_sequence_number(domain, false);
2631 if (NT_STATUS_IS_OK(status)) {
2632 wcache_save_password_policy(domain, status, policy);
2635 return status;
2639 /* Invalidate cached user and group lists coherently */
2641 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2642 void *state)
2644 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2645 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2646 tdb_delete(the_tdb, kbuf);
2648 return 0;
2651 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2653 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2654 struct netr_SamInfo3 *info3)
2656 DOM_SID sid;
2657 fstring key_str, sid_string;
2658 struct winbind_cache *cache;
2660 /* dont clear cached U/SID and UG/SID entries when we want to logon
2661 * offline - gd */
2663 if (lp_winbind_offline_logon()) {
2664 return;
2667 if (!domain)
2668 return;
2670 cache = get_cache(domain);
2672 if (!cache->tdb) {
2673 return;
2676 sid_copy(&sid, info3->base.domain_sid);
2677 sid_append_rid(&sid, info3->base.rid);
2679 /* Clear U/SID cache entry */
2680 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2681 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2682 tdb_delete(cache->tdb, string_tdb_data(key_str));
2684 /* Clear UG/SID cache entry */
2685 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2686 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2687 tdb_delete(cache->tdb, string_tdb_data(key_str));
2689 /* Samba/winbindd never needs this. */
2690 netsamlogon_clear_cached_user(info3);
2693 bool wcache_invalidate_cache(void)
2695 struct winbindd_domain *domain;
2697 for (domain = domain_list(); domain; domain = domain->next) {
2698 struct winbind_cache *cache = get_cache(domain);
2700 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2701 "entries for %s\n", domain->name));
2702 if (cache) {
2703 if (cache->tdb) {
2704 tdb_traverse(cache->tdb, traverse_fn, NULL);
2705 } else {
2706 return false;
2710 return true;
2713 bool init_wcache(void)
2715 if (wcache == NULL) {
2716 wcache = SMB_XMALLOC_P(struct winbind_cache);
2717 ZERO_STRUCTP(wcache);
2720 if (wcache->tdb != NULL)
2721 return true;
2723 /* when working offline we must not clear the cache on restart */
2724 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2725 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2726 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2727 O_RDWR|O_CREAT, 0600);
2729 if (wcache->tdb == NULL) {
2730 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2731 return false;
2734 return true;
2737 /************************************************************************
2738 This is called by the parent to initialize the cache file.
2739 We don't need sophisticated locking here as we know we're the
2740 only opener.
2741 ************************************************************************/
2743 bool initialize_winbindd_cache(void)
2745 bool cache_bad = true;
2746 uint32 vers;
2748 if (!init_wcache()) {
2749 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2750 return false;
2753 /* Check version number. */
2754 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2755 vers == WINBINDD_CACHE_VERSION) {
2756 cache_bad = false;
2759 if (cache_bad) {
2760 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2761 "and re-creating with version number %d\n",
2762 WINBINDD_CACHE_VERSION ));
2764 tdb_close(wcache->tdb);
2765 wcache->tdb = NULL;
2767 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2768 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2769 cache_path("winbindd_cache.tdb"),
2770 strerror(errno) ));
2771 return false;
2773 if (!init_wcache()) {
2774 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2775 "init_wcache failed.\n"));
2776 return false;
2779 /* Write the version. */
2780 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2781 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2782 tdb_errorstr(wcache->tdb) ));
2783 return false;
2787 tdb_close(wcache->tdb);
2788 wcache->tdb = NULL;
2789 return true;
2792 void close_winbindd_cache(void)
2794 if (!wcache) {
2795 return;
2797 if (wcache->tdb) {
2798 tdb_close(wcache->tdb);
2799 wcache->tdb = NULL;
2803 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2804 char **domain_name, char **name,
2805 enum lsa_SidType *type)
2807 struct winbindd_domain *domain;
2808 NTSTATUS status;
2810 domain = find_lookup_domain_from_sid(sid);
2811 if (domain == NULL) {
2812 return false;
2814 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
2815 type);
2816 return NT_STATUS_IS_OK(status);
2819 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2820 const char *domain_name,
2821 const char *name,
2822 DOM_SID *sid,
2823 enum lsa_SidType *type)
2825 struct winbindd_domain *domain;
2826 NTSTATUS status;
2827 bool original_online_state;
2829 domain = find_lookup_domain_from_name(domain_name);
2830 if (domain == NULL) {
2831 return false;
2834 /* If we are doing a cached logon, temporarily set the domain
2835 offline so the cache won't expire the entry */
2837 original_online_state = domain->online;
2838 domain->online = false;
2839 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
2840 domain->online = original_online_state;
2842 return NT_STATUS_IS_OK(status);
2845 void cache_name2sid(struct winbindd_domain *domain,
2846 const char *domain_name, const char *name,
2847 enum lsa_SidType type, const DOM_SID *sid)
2849 refresh_sequence_number(domain, false);
2850 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2851 sid, type);
2855 * The original idea that this cache only contains centries has
2856 * been blurred - now other stuff gets put in here. Ensure we
2857 * ignore these things on cleanup.
2860 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2861 TDB_DATA dbuf, void *state)
2863 struct cache_entry *centry;
2865 if (is_non_centry_key(kbuf)) {
2866 return 0;
2869 centry = wcache_fetch_raw((char *)kbuf.dptr);
2870 if (!centry) {
2871 return 0;
2874 if (!NT_STATUS_IS_OK(centry->status)) {
2875 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2876 tdb_delete(the_tdb, kbuf);
2879 centry_free(centry);
2880 return 0;
2883 /* flush the cache */
2884 void wcache_flush_cache(void)
2886 if (!wcache)
2887 return;
2888 if (wcache->tdb) {
2889 tdb_close(wcache->tdb);
2890 wcache->tdb = NULL;
2892 if (!winbindd_use_cache()) {
2893 return;
2896 /* when working offline we must not clear the cache on restart */
2897 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2898 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2899 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2900 O_RDWR|O_CREAT, 0600);
2902 if (!wcache->tdb) {
2903 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2904 return;
2907 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2909 DEBUG(10,("wcache_flush_cache success\n"));
2912 /* Count cached creds */
2914 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2915 void *state)
2917 int *cred_count = (int*)state;
2919 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2920 (*cred_count)++;
2922 return 0;
2925 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2927 struct winbind_cache *cache = get_cache(domain);
2929 *count = 0;
2931 if (!cache->tdb) {
2932 return NT_STATUS_INTERNAL_DB_ERROR;
2935 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2937 return NT_STATUS_OK;
2940 struct cred_list {
2941 struct cred_list *prev, *next;
2942 TDB_DATA key;
2943 fstring name;
2944 time_t created;
2946 static struct cred_list *wcache_cred_list;
2948 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2949 void *state)
2951 struct cred_list *cred;
2953 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2955 cred = SMB_MALLOC_P(struct cred_list);
2956 if (cred == NULL) {
2957 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2958 return -1;
2961 ZERO_STRUCTP(cred);
2963 /* save a copy of the key */
2965 fstrcpy(cred->name, (const char *)kbuf.dptr);
2966 DLIST_ADD(wcache_cred_list, cred);
2969 return 0;
2972 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2974 struct winbind_cache *cache = get_cache(domain);
2975 NTSTATUS status;
2976 int ret;
2977 struct cred_list *cred, *oldest = NULL;
2979 if (!cache->tdb) {
2980 return NT_STATUS_INTERNAL_DB_ERROR;
2983 /* we possibly already have an entry */
2984 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2986 fstring key_str, tmp;
2988 DEBUG(11,("we already have an entry, deleting that\n"));
2990 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2992 tdb_delete(cache->tdb, string_tdb_data(key_str));
2994 return NT_STATUS_OK;
2997 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2998 if (ret == 0) {
2999 return NT_STATUS_OK;
3000 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3001 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3004 ZERO_STRUCTP(oldest);
3006 for (cred = wcache_cred_list; cred; cred = cred->next) {
3008 TDB_DATA data;
3009 time_t t;
3011 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3012 if (!data.dptr) {
3013 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3014 cred->name));
3015 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3016 goto done;
3019 t = IVAL(data.dptr, 0);
3020 SAFE_FREE(data.dptr);
3022 if (!oldest) {
3023 oldest = SMB_MALLOC_P(struct cred_list);
3024 if (oldest == NULL) {
3025 status = NT_STATUS_NO_MEMORY;
3026 goto done;
3029 fstrcpy(oldest->name, cred->name);
3030 oldest->created = t;
3031 continue;
3034 if (t < oldest->created) {
3035 fstrcpy(oldest->name, cred->name);
3036 oldest->created = t;
3040 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3041 status = NT_STATUS_OK;
3042 } else {
3043 status = NT_STATUS_UNSUCCESSFUL;
3045 done:
3046 SAFE_FREE(wcache_cred_list);
3047 SAFE_FREE(oldest);
3049 return status;
3052 /* Change the global online/offline state. */
3053 bool set_global_winbindd_state_offline(void)
3055 TDB_DATA data;
3057 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3059 /* Only go offline if someone has created
3060 the key "WINBINDD_OFFLINE" in the cache tdb. */
3062 if (wcache == NULL || wcache->tdb == NULL) {
3063 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3064 return false;
3067 if (!lp_winbind_offline_logon()) {
3068 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3069 return false;
3072 if (global_winbindd_offline_state) {
3073 /* Already offline. */
3074 return true;
3077 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3079 if (!data.dptr || data.dsize != 4) {
3080 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3081 SAFE_FREE(data.dptr);
3082 return false;
3083 } else {
3084 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3085 global_winbindd_offline_state = true;
3086 SAFE_FREE(data.dptr);
3087 return true;
3091 void set_global_winbindd_state_online(void)
3093 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3095 if (!lp_winbind_offline_logon()) {
3096 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3097 return;
3100 if (!global_winbindd_offline_state) {
3101 /* Already online. */
3102 return;
3104 global_winbindd_offline_state = false;
3106 if (!wcache->tdb) {
3107 return;
3110 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3111 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3114 bool get_global_winbindd_state_offline(void)
3116 return global_winbindd_offline_state;
3119 /***********************************************************************
3120 Validate functions for all possible cache tdb keys.
3121 ***********************************************************************/
3123 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3124 struct tdb_validation_status *state)
3126 struct cache_entry *centry;
3128 centry = SMB_XMALLOC_P(struct cache_entry);
3129 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3130 if (!centry->data) {
3131 SAFE_FREE(centry);
3132 return NULL;
3134 centry->len = data.dsize;
3135 centry->ofs = 0;
3137 if (centry->len < 8) {
3138 /* huh? corrupt cache? */
3139 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3140 centry_free(centry);
3141 state->bad_entry = true;
3142 state->success = false;
3143 return NULL;
3146 centry->status = NT_STATUS(centry_uint32(centry));
3147 centry->sequence_number = centry_uint32(centry);
3148 return centry;
3151 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3152 struct tdb_validation_status *state)
3154 if (dbuf.dsize != 8) {
3155 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3156 keystr, (unsigned int)dbuf.dsize ));
3157 state->bad_entry = true;
3158 return 1;
3160 return 0;
3163 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3164 struct tdb_validation_status *state)
3166 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3167 if (!centry) {
3168 return 1;
3171 (void)centry_uint32(centry);
3172 if (NT_STATUS_IS_OK(centry->status)) {
3173 DOM_SID sid;
3174 (void)centry_sid(centry, &sid);
3177 centry_free(centry);
3179 if (!(state->success)) {
3180 return 1;
3182 DEBUG(10,("validate_ns: %s ok\n", keystr));
3183 return 0;
3186 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3187 struct tdb_validation_status *state)
3189 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3190 if (!centry) {
3191 return 1;
3194 if (NT_STATUS_IS_OK(centry->status)) {
3195 (void)centry_uint32(centry);
3196 (void)centry_string(centry, mem_ctx);
3197 (void)centry_string(centry, mem_ctx);
3200 centry_free(centry);
3202 if (!(state->success)) {
3203 return 1;
3205 DEBUG(10,("validate_sn: %s ok\n", keystr));
3206 return 0;
3209 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3210 struct tdb_validation_status *state)
3212 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3213 DOM_SID sid;
3215 if (!centry) {
3216 return 1;
3219 (void)centry_string(centry, mem_ctx);
3220 (void)centry_string(centry, mem_ctx);
3221 (void)centry_string(centry, mem_ctx);
3222 (void)centry_string(centry, mem_ctx);
3223 (void)centry_uint32(centry);
3224 (void)centry_sid(centry, &sid);
3225 (void)centry_sid(centry, &sid);
3227 centry_free(centry);
3229 if (!(state->success)) {
3230 return 1;
3232 DEBUG(10,("validate_u: %s ok\n", keystr));
3233 return 0;
3236 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3237 struct tdb_validation_status *state)
3239 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3241 if (!centry) {
3242 return 1;
3245 (void)centry_nttime(centry);
3246 (void)centry_nttime(centry);
3247 (void)centry_uint16(centry);
3249 centry_free(centry);
3251 if (!(state->success)) {
3252 return 1;
3254 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3255 return 0;
3258 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3259 struct tdb_validation_status *state)
3261 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3263 if (!centry) {
3264 return 1;
3267 (void)centry_uint16(centry);
3268 (void)centry_uint16(centry);
3269 (void)centry_uint32(centry);
3270 (void)centry_nttime(centry);
3271 (void)centry_nttime(centry);
3273 centry_free(centry);
3275 if (!(state->success)) {
3276 return 1;
3278 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3279 return 0;
3282 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3283 struct tdb_validation_status *state)
3285 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3287 if (!centry) {
3288 return 1;
3291 (void)centry_time(centry);
3292 (void)centry_hash16(centry, mem_ctx);
3294 /* We only have 17 bytes more data in the salted cred case. */
3295 if (centry->len - centry->ofs == 17) {
3296 (void)centry_hash16(centry, mem_ctx);
3299 centry_free(centry);
3301 if (!(state->success)) {
3302 return 1;
3304 DEBUG(10,("validate_cred: %s ok\n", keystr));
3305 return 0;
3308 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3309 struct tdb_validation_status *state)
3311 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3312 int32 num_entries, i;
3314 if (!centry) {
3315 return 1;
3318 num_entries = (int32)centry_uint32(centry);
3320 for (i=0; i< num_entries; i++) {
3321 DOM_SID sid;
3322 (void)centry_string(centry, mem_ctx);
3323 (void)centry_string(centry, mem_ctx);
3324 (void)centry_string(centry, mem_ctx);
3325 (void)centry_string(centry, mem_ctx);
3326 (void)centry_sid(centry, &sid);
3327 (void)centry_sid(centry, &sid);
3330 centry_free(centry);
3332 if (!(state->success)) {
3333 return 1;
3335 DEBUG(10,("validate_ul: %s ok\n", keystr));
3336 return 0;
3339 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3340 struct tdb_validation_status *state)
3342 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3343 int32 num_entries, i;
3345 if (!centry) {
3346 return 1;
3349 num_entries = centry_uint32(centry);
3351 for (i=0; i< num_entries; i++) {
3352 (void)centry_string(centry, mem_ctx);
3353 (void)centry_string(centry, mem_ctx);
3354 (void)centry_uint32(centry);
3357 centry_free(centry);
3359 if (!(state->success)) {
3360 return 1;
3362 DEBUG(10,("validate_gl: %s ok\n", keystr));
3363 return 0;
3366 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3367 struct tdb_validation_status *state)
3369 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3370 int32 num_groups, i;
3372 if (!centry) {
3373 return 1;
3376 num_groups = centry_uint32(centry);
3378 for (i=0; i< num_groups; i++) {
3379 DOM_SID sid;
3380 centry_sid(centry, &sid);
3383 centry_free(centry);
3385 if (!(state->success)) {
3386 return 1;
3388 DEBUG(10,("validate_ug: %s ok\n", keystr));
3389 return 0;
3392 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3393 struct tdb_validation_status *state)
3395 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3396 int32 num_aliases, i;
3398 if (!centry) {
3399 return 1;
3402 num_aliases = centry_uint32(centry);
3404 for (i=0; i < num_aliases; i++) {
3405 (void)centry_uint32(centry);
3408 centry_free(centry);
3410 if (!(state->success)) {
3411 return 1;
3413 DEBUG(10,("validate_ua: %s ok\n", keystr));
3414 return 0;
3417 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3418 struct tdb_validation_status *state)
3420 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3421 int32 num_names, i;
3423 if (!centry) {
3424 return 1;
3427 num_names = centry_uint32(centry);
3429 for (i=0; i< num_names; i++) {
3430 DOM_SID sid;
3431 centry_sid(centry, &sid);
3432 (void)centry_string(centry, mem_ctx);
3433 (void)centry_uint32(centry);
3436 centry_free(centry);
3438 if (!(state->success)) {
3439 return 1;
3441 DEBUG(10,("validate_gm: %s ok\n", keystr));
3442 return 0;
3445 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3446 struct tdb_validation_status *state)
3448 /* Can't say anything about this other than must be nonzero. */
3449 if (dbuf.dsize == 0) {
3450 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3451 keystr));
3452 state->bad_entry = true;
3453 state->success = false;
3454 return 1;
3457 DEBUG(10,("validate_dr: %s ok\n", keystr));
3458 return 0;
3461 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3462 struct tdb_validation_status *state)
3464 /* Can't say anything about this other than must be nonzero. */
3465 if (dbuf.dsize == 0) {
3466 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3467 keystr));
3468 state->bad_entry = true;
3469 state->success = false;
3470 return 1;
3473 DEBUG(10,("validate_de: %s ok\n", keystr));
3474 return 0;
3477 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3478 TDB_DATA dbuf, struct tdb_validation_status *state)
3480 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3482 if (!centry) {
3483 return 1;
3486 (void)centry_string(centry, mem_ctx);
3487 (void)centry_string(centry, mem_ctx);
3488 (void)centry_string(centry, mem_ctx);
3489 (void)centry_uint32(centry);
3491 centry_free(centry);
3493 if (!(state->success)) {
3494 return 1;
3496 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3497 return 0;
3500 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3501 TDB_DATA dbuf,
3502 struct tdb_validation_status *state)
3504 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3506 if (!centry) {
3507 return 1;
3510 (void)centry_string( centry, mem_ctx );
3512 centry_free(centry);
3514 if (!(state->success)) {
3515 return 1;
3517 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3518 return 0;
3521 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3522 TDB_DATA dbuf,
3523 struct tdb_validation_status *state)
3525 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3527 if (!centry) {
3528 return 1;
3531 (void)centry_string( centry, mem_ctx );
3533 centry_free(centry);
3535 if (!(state->success)) {
3536 return 1;
3538 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3539 return 0;
3542 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3543 struct tdb_validation_status *state)
3545 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3546 int32 num_domains, i;
3548 if (!centry) {
3549 return 1;
3552 num_domains = centry_uint32(centry);
3554 for (i=0; i< num_domains; i++) {
3555 DOM_SID sid;
3556 (void)centry_string(centry, mem_ctx);
3557 (void)centry_string(centry, mem_ctx);
3558 (void)centry_sid(centry, &sid);
3561 centry_free(centry);
3563 if (!(state->success)) {
3564 return 1;
3566 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3567 return 0;
3570 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3571 TDB_DATA dbuf,
3572 struct tdb_validation_status *state)
3574 if (dbuf.dsize == 0) {
3575 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3576 "key %s (len ==0) ?\n", keystr));
3577 state->bad_entry = true;
3578 state->success = false;
3579 return 1;
3582 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3583 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3584 return 0;
3587 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3588 struct tdb_validation_status *state)
3590 if (dbuf.dsize != 4) {
3591 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3592 keystr, (unsigned int)dbuf.dsize ));
3593 state->bad_entry = true;
3594 state->success = false;
3595 return 1;
3597 DEBUG(10,("validate_offline: %s ok\n", keystr));
3598 return 0;
3601 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3602 struct tdb_validation_status *state)
3604 if (dbuf.dsize != 4) {
3605 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3606 "key %s (len %u != 4) ?\n",
3607 keystr, (unsigned int)dbuf.dsize));
3608 state->bad_entry = true;
3609 state->success = false;
3610 return 1;
3613 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3614 return 0;
3617 /***********************************************************************
3618 A list of all possible cache tdb keys with associated validation
3619 functions.
3620 ***********************************************************************/
3622 struct key_val_struct {
3623 const char *keyname;
3624 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3625 } key_val[] = {
3626 {"SEQNUM/", validate_seqnum},
3627 {"NS/", validate_ns},
3628 {"SN/", validate_sn},
3629 {"U/", validate_u},
3630 {"LOC_POL/", validate_loc_pol},
3631 {"PWD_POL/", validate_pwd_pol},
3632 {"CRED/", validate_cred},
3633 {"UL/", validate_ul},
3634 {"GL/", validate_gl},
3635 {"UG/", validate_ug},
3636 {"UA", validate_ua},
3637 {"GM/", validate_gm},
3638 {"DR/", validate_dr},
3639 {"DE/", validate_de},
3640 {"NSS/PWINFO/", validate_pwinfo},
3641 {"TRUSTDOMS/", validate_trustdoms},
3642 {"TRUSTDOMCACHE/", validate_trustdomcache},
3643 {"NSS/NA/", validate_nss_na},
3644 {"NSS/AN/", validate_nss_an},
3645 {"WINBINDD_OFFLINE", validate_offline},
3646 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3647 {NULL, NULL}
3650 /***********************************************************************
3651 Function to look at every entry in the tdb and validate it as far as
3652 possible.
3653 ***********************************************************************/
3655 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3657 int i;
3658 unsigned int max_key_len = 1024;
3659 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3661 /* Paranoia check. */
3662 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3663 max_key_len = 1024 * 1024;
3665 if (kbuf.dsize > max_key_len) {
3666 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3667 "(%u) > (%u)\n\n",
3668 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3669 return 1;
3672 for (i = 0; key_val[i].keyname; i++) {
3673 size_t namelen = strlen(key_val[i].keyname);
3674 if (kbuf.dsize >= namelen && (
3675 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3676 TALLOC_CTX *mem_ctx;
3677 char *keystr;
3678 int ret;
3680 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3681 if (!keystr) {
3682 return 1;
3684 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3685 keystr[kbuf.dsize] = '\0';
3687 mem_ctx = talloc_init("validate_ctx");
3688 if (!mem_ctx) {
3689 SAFE_FREE(keystr);
3690 return 1;
3693 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3694 v_state);
3696 SAFE_FREE(keystr);
3697 talloc_destroy(mem_ctx);
3698 return ret;
3702 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3703 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3704 DEBUG(0,("data :\n"));
3705 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3706 v_state->unknown_key = true;
3707 v_state->success = false;
3708 return 1; /* terminate. */
3711 static void validate_panic(const char *const why)
3713 DEBUG(0,("validating cache: would panic %s\n", why ));
3714 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3715 exit(47);
3718 /***********************************************************************
3719 Try and validate every entry in the winbindd cache. If we fail here,
3720 delete the cache tdb and return non-zero.
3721 ***********************************************************************/
3723 int winbindd_validate_cache(void)
3725 int ret = -1;
3726 const char *tdb_path = cache_path("winbindd_cache.tdb");
3727 TDB_CONTEXT *tdb = NULL;
3729 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3730 smb_panic_fn = validate_panic;
3733 tdb = tdb_open_log(tdb_path,
3734 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3735 ( lp_winbind_offline_logon()
3736 ? TDB_DEFAULT
3737 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3738 O_RDWR|O_CREAT,
3739 0600);
3740 if (!tdb) {
3741 DEBUG(0, ("winbindd_validate_cache: "
3742 "error opening/initializing tdb\n"));
3743 goto done;
3745 tdb_close(tdb);
3747 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3749 if (ret != 0) {
3750 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3751 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3752 unlink(tdb_path);
3755 done:
3756 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3757 smb_panic_fn = smb_panic;
3758 return ret;
3761 /***********************************************************************
3762 Try and validate every entry in the winbindd cache.
3763 ***********************************************************************/
3765 int winbindd_validate_cache_nobackup(void)
3767 int ret = -1;
3768 const char *tdb_path = cache_path("winbindd_cache.tdb");
3770 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3771 smb_panic_fn = validate_panic;
3774 if (wcache == NULL || wcache->tdb == NULL) {
3775 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3776 } else {
3777 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3780 if (ret != 0) {
3781 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3782 "successful.\n"));
3785 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3786 "function\n"));
3787 smb_panic_fn = smb_panic;
3788 return ret;
3791 bool winbindd_cache_validate_and_initialize(void)
3793 close_winbindd_cache();
3795 if (lp_winbind_offline_logon()) {
3796 if (winbindd_validate_cache() < 0) {
3797 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3798 "could be restored.\n"));
3802 return initialize_winbindd_cache();
3805 /*********************************************************************
3806 ********************************************************************/
3808 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3809 struct winbindd_tdc_domain **domains,
3810 size_t *num_domains )
3812 struct winbindd_tdc_domain *list = NULL;
3813 size_t idx;
3814 int i;
3815 bool set_only = false;
3817 /* don't allow duplicates */
3819 idx = *num_domains;
3820 list = *domains;
3822 for ( i=0; i< (*num_domains); i++ ) {
3823 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3824 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3825 new_dom->name));
3826 idx = i;
3827 set_only = true;
3829 break;
3833 if ( !set_only ) {
3834 if ( !*domains ) {
3835 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3836 idx = 0;
3837 } else {
3838 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3839 struct winbindd_tdc_domain,
3840 (*num_domains)+1);
3841 idx = *num_domains;
3844 ZERO_STRUCT( list[idx] );
3847 if ( !list )
3848 return false;
3850 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3851 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3853 if ( !is_null_sid( &new_dom->sid ) ) {
3854 sid_copy( &list[idx].sid, &new_dom->sid );
3855 } else {
3856 sid_copy(&list[idx].sid, &global_sid_NULL);
3859 if ( new_dom->domain_flags != 0x0 )
3860 list[idx].trust_flags = new_dom->domain_flags;
3862 if ( new_dom->domain_type != 0x0 )
3863 list[idx].trust_type = new_dom->domain_type;
3865 if ( new_dom->domain_trust_attribs != 0x0 )
3866 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3868 if ( !set_only ) {
3869 *domains = list;
3870 *num_domains = idx + 1;
3873 return true;
3876 /*********************************************************************
3877 ********************************************************************/
3879 static TDB_DATA make_tdc_key( const char *domain_name )
3881 char *keystr = NULL;
3882 TDB_DATA key = { NULL, 0 };
3884 if ( !domain_name ) {
3885 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3886 return key;
3889 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3890 return key;
3892 key = string_term_tdb_data(keystr);
3894 return key;
3897 /*********************************************************************
3898 ********************************************************************/
3900 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3901 size_t num_domains,
3902 unsigned char **buf )
3904 unsigned char *buffer = NULL;
3905 int len = 0;
3906 int buflen = 0;
3907 int i = 0;
3909 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3910 (int)num_domains));
3912 buflen = 0;
3914 again:
3915 len = 0;
3917 /* Store the number of array items first */
3918 len += tdb_pack( buffer+len, buflen-len, "d",
3919 num_domains );
3921 /* now pack each domain trust record */
3922 for ( i=0; i<num_domains; i++ ) {
3924 fstring tmp;
3926 if ( buflen > 0 ) {
3927 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3928 domains[i].domain_name,
3929 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3932 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3933 domains[i].domain_name,
3934 domains[i].dns_name,
3935 sid_to_fstring(tmp, &domains[i].sid),
3936 domains[i].trust_flags,
3937 domains[i].trust_attribs,
3938 domains[i].trust_type );
3941 if ( buflen < len ) {
3942 SAFE_FREE(buffer);
3943 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3944 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3945 buflen = -1;
3946 goto done;
3948 buflen = len;
3949 goto again;
3952 *buf = buffer;
3954 done:
3955 return buflen;
3958 /*********************************************************************
3959 ********************************************************************/
3961 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3962 struct winbindd_tdc_domain **domains )
3964 fstring domain_name, dns_name, sid_string;
3965 uint32 type, attribs, flags;
3966 int num_domains;
3967 int len = 0;
3968 int i;
3969 struct winbindd_tdc_domain *list = NULL;
3971 /* get the number of domains */
3972 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3973 if ( len == -1 ) {
3974 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3975 return 0;
3978 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3979 if ( !list ) {
3980 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3981 return 0;
3984 for ( i=0; i<num_domains; i++ ) {
3985 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3986 domain_name,
3987 dns_name,
3988 sid_string,
3989 &flags,
3990 &attribs,
3991 &type );
3993 if ( len == -1 ) {
3994 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3995 TALLOC_FREE( list );
3996 return 0;
3999 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4000 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4001 domain_name, dns_name, sid_string,
4002 flags, attribs, type));
4004 list[i].domain_name = talloc_strdup( list, domain_name );
4005 list[i].dns_name = talloc_strdup( list, dns_name );
4006 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4007 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4008 domain_name));
4010 list[i].trust_flags = flags;
4011 list[i].trust_attribs = attribs;
4012 list[i].trust_type = type;
4015 *domains = list;
4017 return num_domains;
4020 /*********************************************************************
4021 ********************************************************************/
4023 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4025 TDB_DATA key = make_tdc_key( lp_workgroup() );
4026 TDB_DATA data = { NULL, 0 };
4027 int ret;
4029 if ( !key.dptr )
4030 return false;
4032 /* See if we were asked to delete the cache entry */
4034 if ( !domains ) {
4035 ret = tdb_delete( wcache->tdb, key );
4036 goto done;
4039 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4041 if ( !data.dptr ) {
4042 ret = -1;
4043 goto done;
4046 ret = tdb_store( wcache->tdb, key, data, 0 );
4048 done:
4049 SAFE_FREE( data.dptr );
4050 SAFE_FREE( key.dptr );
4052 return ( ret != -1 );
4055 /*********************************************************************
4056 ********************************************************************/
4058 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4060 TDB_DATA key = make_tdc_key( lp_workgroup() );
4061 TDB_DATA data = { NULL, 0 };
4063 *domains = NULL;
4064 *num_domains = 0;
4066 if ( !key.dptr )
4067 return false;
4069 data = tdb_fetch( wcache->tdb, key );
4071 SAFE_FREE( key.dptr );
4073 if ( !data.dptr )
4074 return false;
4076 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4078 SAFE_FREE( data.dptr );
4080 if ( !*domains )
4081 return false;
4083 return true;
4086 /*********************************************************************
4087 ********************************************************************/
4089 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4091 struct winbindd_tdc_domain *dom_list = NULL;
4092 size_t num_domains = 0;
4093 bool ret = false;
4095 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4096 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4097 domain->name, domain->alt_name,
4098 sid_string_dbg(&domain->sid),
4099 domain->domain_flags,
4100 domain->domain_trust_attribs,
4101 domain->domain_type));
4103 if ( !init_wcache() ) {
4104 return false;
4107 /* fetch the list */
4109 wcache_tdc_fetch_list( &dom_list, &num_domains );
4111 /* add the new domain */
4113 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4114 goto done;
4117 /* pack the domain */
4119 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4120 goto done;
4123 /* Success */
4125 ret = true;
4126 done:
4127 TALLOC_FREE( dom_list );
4129 return ret;
4132 /*********************************************************************
4133 ********************************************************************/
4135 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4137 struct winbindd_tdc_domain *dom_list = NULL;
4138 size_t num_domains = 0;
4139 int i;
4140 struct winbindd_tdc_domain *d = NULL;
4142 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4144 if ( !init_wcache() ) {
4145 return false;
4148 /* fetch the list */
4150 wcache_tdc_fetch_list( &dom_list, &num_domains );
4152 for ( i=0; i<num_domains; i++ ) {
4153 if ( strequal(name, dom_list[i].domain_name) ||
4154 strequal(name, dom_list[i].dns_name) )
4156 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4157 name));
4159 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4160 if ( !d )
4161 break;
4163 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4164 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4165 sid_copy( &d->sid, &dom_list[i].sid );
4166 d->trust_flags = dom_list[i].trust_flags;
4167 d->trust_type = dom_list[i].trust_type;
4168 d->trust_attribs = dom_list[i].trust_attribs;
4170 break;
4174 TALLOC_FREE( dom_list );
4176 return d;
4180 /*********************************************************************
4181 ********************************************************************/
4183 void wcache_tdc_clear( void )
4185 if ( !init_wcache() )
4186 return;
4188 wcache_tdc_store_list( NULL, 0 );
4190 return;
4194 /*********************************************************************
4195 ********************************************************************/
4197 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4198 NTSTATUS status,
4199 const DOM_SID *user_sid,
4200 const char *homedir,
4201 const char *shell,
4202 const char *gecos,
4203 uint32 gid)
4205 struct cache_entry *centry;
4206 fstring tmp;
4208 if ( (centry = centry_start(domain, status)) == NULL )
4209 return;
4211 centry_put_string( centry, homedir );
4212 centry_put_string( centry, shell );
4213 centry_put_string( centry, gecos );
4214 centry_put_uint32( centry, gid );
4216 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4218 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4220 centry_free(centry);
4223 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4224 const DOM_SID *user_sid,
4225 TALLOC_CTX *ctx,
4226 ADS_STRUCT *ads, LDAPMessage *msg,
4227 const char **homedir, const char **shell,
4228 const char **gecos, gid_t *p_gid)
4230 struct winbind_cache *cache = get_cache(domain);
4231 struct cache_entry *centry = NULL;
4232 NTSTATUS nt_status;
4233 fstring tmp;
4235 if (!cache->tdb)
4236 goto do_query;
4238 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4239 sid_to_fstring(tmp, user_sid));
4241 if (!centry)
4242 goto do_query;
4244 *homedir = centry_string( centry, ctx );
4245 *shell = centry_string( centry, ctx );
4246 *gecos = centry_string( centry, ctx );
4247 *p_gid = centry_uint32( centry );
4249 centry_free(centry);
4251 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4252 sid_string_dbg(user_sid)));
4254 return NT_STATUS_OK;
4256 do_query:
4258 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4259 homedir, shell, gecos, p_gid );
4261 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4263 if ( NT_STATUS_IS_OK(nt_status) ) {
4264 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4265 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4266 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4267 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4269 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4270 *homedir, *shell, *gecos, *p_gid );
4273 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4274 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4275 domain->name ));
4276 set_domain_offline( domain );
4279 return nt_status;
4283 /* the cache backend methods are exposed via this structure */
4284 struct winbindd_methods cache_methods = {
4285 true,
4286 query_user_list,
4287 enum_dom_groups,
4288 enum_local_groups,
4289 name_to_sid,
4290 sid_to_name,
4291 rids_to_names,
4292 query_user,
4293 lookup_usergroups,
4294 lookup_useraliases,
4295 lookup_groupmem,
4296 sequence_number,
4297 lockout_policy,
4298 password_policy,
4299 trusted_domains
4302 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4303 uint32_t opnum, const DATA_BLOB *req,
4304 TDB_DATA *pkey)
4306 char *key;
4307 size_t keylen;
4309 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4310 if (key == NULL) {
4311 return false;
4313 keylen = talloc_get_size(key) - 1;
4315 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4316 if (key == NULL) {
4317 return false;
4319 memcpy(key + keylen, req->data, req->length);
4321 pkey->dptr = (uint8_t *)key;
4322 pkey->dsize = talloc_get_size(key);
4323 return true;
4326 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4327 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4329 TDB_DATA key, data;
4330 bool ret = false;
4332 if (wcache->tdb == NULL) {
4333 return false;
4336 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4337 return false;
4339 data = tdb_fetch(wcache->tdb, key);
4340 TALLOC_FREE(key.dptr);
4342 if (data.dptr == NULL) {
4343 return false;
4345 if (data.dsize < 4) {
4346 goto fail;
4349 if (IS_DOMAIN_ONLINE(domain)) {
4350 uint32_t entry_seqnum, dom_seqnum, last_check;
4352 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4353 &last_check)) {
4354 goto fail;
4356 entry_seqnum = IVAL(data.dptr, 0);
4357 if (entry_seqnum != dom_seqnum) {
4358 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4359 (int)entry_seqnum));
4360 goto fail;
4364 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4365 data.dsize - 4);
4366 if (resp->data == NULL) {
4367 DEBUG(10, ("talloc failed\n"));
4368 goto fail;
4370 resp->length = data.dsize - 4;
4372 ret = true;
4373 fail:
4374 SAFE_FREE(data.dptr);
4375 return ret;
4378 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4379 const DATA_BLOB *req, const DATA_BLOB *resp)
4381 TDB_DATA key, data;
4382 uint32_t dom_seqnum, last_check;
4384 if (wcache->tdb == NULL) {
4385 return;
4388 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4389 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4390 domain->name));
4391 return;
4394 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4395 return;
4398 data.dsize = resp->length + 4;
4399 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4400 if (data.dptr == NULL) {
4401 goto done;
4404 SIVAL(data.dptr, 0, dom_seqnum);
4405 memcpy(data.dptr+4, resp->data, resp->length);
4407 tdb_store(wcache->tdb, key, data, 0);
4409 done:
4410 TALLOC_FREE(key.dptr);
4411 return;