Fix bug #7669.
[Samba.git] / source / winbindd / winbindd_cache.c
blob5d2b1f587107aafce97d8dacd509eb29fc73586b
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "winbindd.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods;
36 #ifdef HAVE_ADS
37 extern struct winbindd_methods ads_methods;
38 #endif
39 extern struct winbindd_methods builtin_passdb_methods;
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
47 static const char *non_centry_keys[] = {
48 "SEQNUM/",
49 "DR/",
50 "DE/",
51 "WINBINDD_OFFLINE",
52 WINBINDD_CACHE_VERSION_KEYSTR,
53 NULL
56 /************************************************************************
57 Is this key a non-centry type ?
58 ************************************************************************/
60 static bool is_non_centry_key(TDB_DATA kbuf)
62 int i;
64 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
65 return false;
67 for (i = 0; non_centry_keys[i] != NULL; i++) {
68 size_t namelen = strlen(non_centry_keys[i]);
69 if (kbuf.dsize < namelen) {
70 continue;
72 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
73 return true;
76 return false;
79 /* Global online/offline state - False when online. winbindd starts up online
80 and sets this to true if the first query fails and there's an entry in
81 the cache tdb telling us to stay offline. */
83 static bool global_winbindd_offline_state;
85 struct winbind_cache {
86 TDB_CONTEXT *tdb;
89 struct cache_entry {
90 NTSTATUS status;
91 uint32 sequence_number;
92 uint8 *data;
93 uint32 len, ofs;
96 void (*smb_panic_fn)(const char *const why) = smb_panic;
98 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100 static struct winbind_cache *wcache;
102 void winbindd_check_cache_size(time_t t)
104 static time_t last_check_time;
105 struct stat st;
107 if (last_check_time == (time_t)0)
108 last_check_time = t;
110 if (t - last_check_time < 60 && t - last_check_time > 0)
111 return;
113 if (wcache == NULL || wcache->tdb == NULL) {
114 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
115 return;
118 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
119 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
120 return;
123 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
124 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
125 (unsigned long)st.st_size,
126 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
127 wcache_flush_cache();
131 /* get the winbind_cache structure */
132 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
134 struct winbind_cache *ret = wcache;
136 /* We have to know what type of domain we are dealing with first. */
138 if (domain->internal) {
139 domain->backend = &builtin_passdb_methods;
140 domain->initialized = True;
142 if ( !domain->initialized ) {
143 init_dc_connection( domain );
147 OK. listen up becasue I'm only going to say this once.
148 We have the following scenarios to consider
149 (a) trusted AD domains on a Samba DC,
150 (b) trusted AD domains and we are joined to a non-kerberos domain
151 (c) trusted AD domains and we are joined to a kerberos (AD) domain
153 For (a) we can always contact the trusted domain using krb5
154 since we have the domain trust account password
156 For (b) we can only use RPC since we have no way of
157 getting a krb5 ticket in our own domain
159 For (c) we can always use krb5 since we have a kerberos trust
161 --jerry
164 if (!domain->backend) {
165 #ifdef HAVE_ADS
166 struct winbindd_domain *our_domain = domain;
168 /* find our domain first so we can figure out if we
169 are joined to a kerberized domain */
171 if ( !domain->primary )
172 our_domain = find_our_domain();
174 if ((our_domain->active_directory || IS_DC)
175 && domain->active_directory
176 && !lp_winbind_rpc_only()) {
177 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
178 domain->backend = &ads_methods;
179 } else {
180 #endif /* HAVE_ADS */
181 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
182 domain->backend = &reconnect_methods;
183 #ifdef HAVE_ADS
185 #endif /* HAVE_ADS */
188 if (ret)
189 return ret;
191 ret = SMB_XMALLOC_P(struct winbind_cache);
192 ZERO_STRUCTP(ret);
194 wcache = ret;
195 wcache_flush_cache();
197 return ret;
201 free a centry structure
203 static void centry_free(struct cache_entry *centry)
205 if (!centry)
206 return;
207 SAFE_FREE(centry->data);
208 free(centry);
211 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
213 if (centry->len - centry->ofs < nbytes) {
214 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
215 (unsigned int)nbytes,
216 centry->len - centry->ofs));
217 return false;
219 return true;
223 pull a uint32 from a cache entry
225 static uint32 centry_uint32(struct cache_entry *centry)
227 uint32 ret;
229 if (!centry_check_bytes(centry, 4)) {
230 smb_panic_fn("centry_uint32");
232 ret = IVAL(centry->data, centry->ofs);
233 centry->ofs += 4;
234 return ret;
238 pull a uint16 from a cache entry
240 static uint16 centry_uint16(struct cache_entry *centry)
242 uint16 ret;
243 if (!centry_check_bytes(centry, 2)) {
244 smb_panic_fn("centry_uint16");
246 ret = CVAL(centry->data, centry->ofs);
247 centry->ofs += 2;
248 return ret;
252 pull a uint8 from a cache entry
254 static uint8 centry_uint8(struct cache_entry *centry)
256 uint8 ret;
257 if (!centry_check_bytes(centry, 1)) {
258 smb_panic_fn("centry_uint8");
260 ret = CVAL(centry->data, centry->ofs);
261 centry->ofs += 1;
262 return ret;
266 pull a NTTIME from a cache entry
268 static NTTIME centry_nttime(struct cache_entry *centry)
270 NTTIME ret;
271 if (!centry_check_bytes(centry, 8)) {
272 smb_panic_fn("centry_nttime");
274 ret = IVAL(centry->data, centry->ofs);
275 centry->ofs += 4;
276 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
277 centry->ofs += 4;
278 return ret;
282 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
284 static time_t centry_time(struct cache_entry *centry)
286 return (time_t)centry_nttime(centry);
289 /* pull a string from a cache entry, using the supplied
290 talloc context
292 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
294 uint32 len;
295 char *ret;
297 len = centry_uint8(centry);
299 if (len == 0xFF) {
300 /* a deliberate NULL string */
301 return NULL;
304 if (!centry_check_bytes(centry, (size_t)len)) {
305 smb_panic_fn("centry_string");
308 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
309 if (!ret) {
310 smb_panic_fn("centry_string out of memory\n");
312 memcpy(ret,centry->data + centry->ofs, len);
313 ret[len] = 0;
314 centry->ofs += len;
315 return ret;
318 /* pull a hash16 from a cache entry, using the supplied
319 talloc context
321 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
323 uint32 len;
324 char *ret;
326 len = centry_uint8(centry);
328 if (len != 16) {
329 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
330 len ));
331 return NULL;
334 if (!centry_check_bytes(centry, 16)) {
335 return NULL;
338 ret = TALLOC_ARRAY(mem_ctx, char, 16);
339 if (!ret) {
340 smb_panic_fn("centry_hash out of memory\n");
342 memcpy(ret,centry->data + centry->ofs, 16);
343 centry->ofs += 16;
344 return ret;
347 /* pull a sid from a cache entry, using the supplied
348 talloc context
350 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
352 char *sid_string;
353 sid_string = centry_string(centry, mem_ctx);
354 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
355 return false;
357 return true;
362 pull a NTSTATUS from a cache entry
364 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
366 NTSTATUS status;
368 status = NT_STATUS(centry_uint32(centry));
369 return status;
373 /* the server is considered down if it can't give us a sequence number */
374 static bool wcache_server_down(struct winbindd_domain *domain)
376 bool ret;
378 if (!wcache->tdb)
379 return false;
381 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
383 if (ret)
384 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
385 domain->name ));
386 return ret;
389 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
391 TDB_DATA data;
392 fstring key;
393 uint32 time_diff;
395 if (!wcache->tdb) {
396 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
397 return NT_STATUS_UNSUCCESSFUL;
400 fstr_sprintf( key, "SEQNUM/%s", domain->name );
402 data = tdb_fetch_bystring( wcache->tdb, key );
403 if ( !data.dptr || data.dsize!=8 ) {
404 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
405 return NT_STATUS_UNSUCCESSFUL;
408 domain->sequence_number = IVAL(data.dptr, 0);
409 domain->last_seq_check = IVAL(data.dptr, 4);
411 SAFE_FREE(data.dptr);
413 /* have we expired? */
415 time_diff = now - domain->last_seq_check;
416 if ( time_diff > lp_winbind_cache_time() ) {
417 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
418 domain->name, domain->sequence_number,
419 (uint32)domain->last_seq_check));
420 return NT_STATUS_UNSUCCESSFUL;
423 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
424 domain->name, domain->sequence_number,
425 (uint32)domain->last_seq_check));
427 return NT_STATUS_OK;
430 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
432 TDB_DATA data;
433 fstring key_str;
434 uint8 buf[8];
436 if (!wcache->tdb) {
437 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
438 return NT_STATUS_UNSUCCESSFUL;
441 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
443 SIVAL(buf, 0, domain->sequence_number);
444 SIVAL(buf, 4, domain->last_seq_check);
445 data.dptr = buf;
446 data.dsize = 8;
448 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
449 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
450 return NT_STATUS_UNSUCCESSFUL;
453 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
454 domain->name, domain->sequence_number,
455 (uint32)domain->last_seq_check));
457 return NT_STATUS_OK;
461 refresh the domain sequence number. If force is true
462 then always refresh it, no matter how recently we fetched it
465 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
467 NTSTATUS status;
468 unsigned time_diff;
469 time_t t = time(NULL);
470 unsigned cache_time = lp_winbind_cache_time();
472 if ( IS_DOMAIN_OFFLINE(domain) ) {
473 return;
476 get_cache( domain );
478 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
479 /* trying to reconnect is expensive, don't do it too often */
480 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
481 cache_time *= 8;
483 #endif
485 time_diff = t - domain->last_seq_check;
487 /* see if we have to refetch the domain sequence number */
488 if (!force && (time_diff < cache_time) &&
489 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
490 NT_STATUS_IS_OK(domain->last_status)) {
491 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
492 goto done;
495 /* try to get the sequence number from the tdb cache first */
496 /* this will update the timestamp as well */
498 status = fetch_cache_seqnum( domain, t );
499 if (NT_STATUS_IS_OK(status) &&
500 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
501 NT_STATUS_IS_OK(domain->last_status)) {
502 goto done;
505 /* important! make sure that we know if this is a native
506 mode domain or not. And that we can contact it. */
508 if ( winbindd_can_contact_domain( domain ) ) {
509 status = domain->backend->sequence_number(domain,
510 &domain->sequence_number);
511 } else {
512 /* just use the current time */
513 status = NT_STATUS_OK;
514 domain->sequence_number = time(NULL);
518 /* the above call could have set our domain->backend to NULL when
519 * coming from offline to online mode, make sure to reinitialize the
520 * backend - Guenther */
521 get_cache( domain );
523 if (!NT_STATUS_IS_OK(status)) {
524 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
525 domain->sequence_number = DOM_SEQUENCE_NONE;
528 domain->last_status = status;
529 domain->last_seq_check = time(NULL);
531 /* save the new sequence number in the cache */
532 store_cache_seqnum( domain );
534 done:
535 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
536 domain->name, domain->sequence_number));
538 return;
542 decide if a cache entry has expired
544 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
546 /* If we've been told to be offline - stay in that state... */
547 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
548 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
549 keystr, domain->name ));
550 return false;
553 /* when the domain is offline return the cached entry.
554 * This deals with transient offline states... */
556 if (!domain->online) {
557 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
558 keystr, domain->name ));
559 return false;
562 /* if the server is OK and our cache entry came from when it was down then
563 the entry is invalid */
564 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
565 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
566 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
567 keystr, domain->name ));
568 return true;
571 /* if the server is down or the cache entry is not older than the
572 current sequence number then it is OK */
573 if (wcache_server_down(domain) ||
574 centry->sequence_number == domain->sequence_number) {
575 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
576 keystr, domain->name ));
577 return false;
580 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
581 keystr, domain->name ));
583 /* it's expired */
584 return true;
587 static struct cache_entry *wcache_fetch_raw(char *kstr)
589 TDB_DATA data;
590 struct cache_entry *centry;
591 TDB_DATA key;
593 key = string_tdb_data(kstr);
594 data = tdb_fetch(wcache->tdb, key);
595 if (!data.dptr) {
596 /* a cache miss */
597 return NULL;
600 centry = SMB_XMALLOC_P(struct cache_entry);
601 centry->data = (unsigned char *)data.dptr;
602 centry->len = data.dsize;
603 centry->ofs = 0;
605 if (centry->len < 8) {
606 /* huh? corrupt cache? */
607 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
608 centry_free(centry);
609 return NULL;
612 centry->status = centry_ntstatus(centry);
613 centry->sequence_number = centry_uint32(centry);
615 return centry;
619 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
620 number and return status
622 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
623 struct winbindd_domain *domain,
624 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
625 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
626 struct winbindd_domain *domain,
627 const char *format, ...)
629 va_list ap;
630 char *kstr;
631 struct cache_entry *centry;
633 if (!winbindd_use_cache()) {
634 return NULL;
637 refresh_sequence_number(domain, false);
639 va_start(ap, format);
640 smb_xvasprintf(&kstr, format, ap);
641 va_end(ap);
643 centry = wcache_fetch_raw(kstr);
644 if (centry == NULL) {
645 free(kstr);
646 return NULL;
649 if (centry_expired(domain, kstr, centry)) {
651 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
652 kstr, domain->name ));
654 centry_free(centry);
655 free(kstr);
656 return NULL;
659 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
660 kstr, domain->name ));
662 free(kstr);
663 return centry;
666 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
667 static void wcache_delete(const char *format, ...)
669 va_list ap;
670 char *kstr;
671 TDB_DATA key;
673 va_start(ap, format);
674 smb_xvasprintf(&kstr, format, ap);
675 va_end(ap);
677 key = string_tdb_data(kstr);
679 tdb_delete(wcache->tdb, key);
680 free(kstr);
684 make sure we have at least len bytes available in a centry
686 static void centry_expand(struct cache_entry *centry, uint32 len)
688 if (centry->len - centry->ofs >= len)
689 return;
690 centry->len *= 2;
691 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
692 centry->len);
693 if (!centry->data) {
694 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
695 smb_panic_fn("out of memory in centry_expand");
700 push a uint32 into a centry
702 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
704 centry_expand(centry, 4);
705 SIVAL(centry->data, centry->ofs, v);
706 centry->ofs += 4;
710 push a uint16 into a centry
712 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
714 centry_expand(centry, 2);
715 SIVAL(centry->data, centry->ofs, v);
716 centry->ofs += 2;
720 push a uint8 into a centry
722 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
724 centry_expand(centry, 1);
725 SCVAL(centry->data, centry->ofs, v);
726 centry->ofs += 1;
730 push a string into a centry
732 static void centry_put_string(struct cache_entry *centry, const char *s)
734 int len;
736 if (!s) {
737 /* null strings are marked as len 0xFFFF */
738 centry_put_uint8(centry, 0xFF);
739 return;
742 len = strlen(s);
743 /* can't handle more than 254 char strings. Truncating is probably best */
744 if (len > 254) {
745 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
746 len = 254;
748 centry_put_uint8(centry, len);
749 centry_expand(centry, len);
750 memcpy(centry->data + centry->ofs, s, len);
751 centry->ofs += len;
755 push a 16 byte hash into a centry - treat as 16 byte string.
757 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
759 centry_put_uint8(centry, 16);
760 centry_expand(centry, 16);
761 memcpy(centry->data + centry->ofs, val, 16);
762 centry->ofs += 16;
765 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
767 fstring sid_string;
768 centry_put_string(centry, sid_to_fstring(sid_string, sid));
773 put NTSTATUS into a centry
775 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
777 uint32 status_value = NT_STATUS_V(status);
778 centry_put_uint32(centry, status_value);
783 push a NTTIME into a centry
785 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
787 centry_expand(centry, 8);
788 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
789 centry->ofs += 4;
790 SIVAL(centry->data, centry->ofs, nt >> 32);
791 centry->ofs += 4;
795 push a time_t into a centry - use a 64 bit size.
796 NTTIME here is being used as a convenient 64-bit size.
798 static void centry_put_time(struct cache_entry *centry, time_t t)
800 NTTIME nt = (NTTIME)t;
801 centry_put_nttime(centry, nt);
805 start a centry for output. When finished, call centry_end()
807 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
809 struct cache_entry *centry;
811 if (!wcache->tdb)
812 return NULL;
814 centry = SMB_XMALLOC_P(struct cache_entry);
816 centry->len = 8192; /* reasonable default */
817 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
818 centry->ofs = 0;
819 centry->sequence_number = domain->sequence_number;
820 centry_put_ntstatus(centry, status);
821 centry_put_uint32(centry, centry->sequence_number);
822 return centry;
826 finish a centry and write it to the tdb
828 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
829 static void centry_end(struct cache_entry *centry, const char *format, ...)
831 va_list ap;
832 char *kstr;
833 TDB_DATA key, data;
835 if (!winbindd_use_cache()) {
836 return;
839 va_start(ap, format);
840 smb_xvasprintf(&kstr, format, ap);
841 va_end(ap);
843 key = string_tdb_data(kstr);
844 data.dptr = centry->data;
845 data.dsize = centry->ofs;
847 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
848 free(kstr);
851 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
852 NTSTATUS status, const char *domain_name,
853 const char *name, const DOM_SID *sid,
854 enum lsa_SidType type)
856 struct cache_entry *centry;
857 fstring uname;
859 centry = centry_start(domain, status);
860 if (!centry)
861 return;
862 centry_put_uint32(centry, type);
863 centry_put_sid(centry, sid);
864 fstrcpy(uname, name);
865 strupper_m(uname);
866 centry_end(centry, "NS/%s/%s", domain_name, uname);
867 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
868 uname, sid_string_dbg(sid), nt_errstr(status)));
869 centry_free(centry);
872 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
873 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
875 struct cache_entry *centry;
876 fstring sid_string;
878 centry = centry_start(domain, status);
879 if (!centry)
880 return;
882 if (NT_STATUS_IS_OK(status)) {
883 centry_put_uint32(centry, type);
884 centry_put_string(centry, domain_name);
885 centry_put_string(centry, name);
888 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
889 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
890 name, nt_errstr(status)));
891 centry_free(centry);
895 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
897 struct cache_entry *centry;
898 fstring sid_string;
900 if (is_null_sid(&info->user_sid)) {
901 return;
904 centry = centry_start(domain, status);
905 if (!centry)
906 return;
907 centry_put_string(centry, info->acct_name);
908 centry_put_string(centry, info->full_name);
909 centry_put_string(centry, info->homedir);
910 centry_put_string(centry, info->shell);
911 centry_put_uint32(centry, info->primary_gid);
912 centry_put_sid(centry, &info->user_sid);
913 centry_put_sid(centry, &info->group_sid);
914 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
915 &info->user_sid));
916 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
917 centry_free(centry);
920 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
921 NTSTATUS status,
922 struct samr_DomInfo12 *lockout_policy)
924 struct cache_entry *centry;
926 centry = centry_start(domain, status);
927 if (!centry)
928 return;
930 centry_put_nttime(centry, lockout_policy->lockout_duration);
931 centry_put_nttime(centry, lockout_policy->lockout_window);
932 centry_put_uint16(centry, lockout_policy->lockout_threshold);
934 centry_end(centry, "LOC_POL/%s", domain->name);
936 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
938 centry_free(centry);
943 static void wcache_save_password_policy(struct winbindd_domain *domain,
944 NTSTATUS status,
945 struct samr_DomInfo1 *policy)
947 struct cache_entry *centry;
949 centry = centry_start(domain, status);
950 if (!centry)
951 return;
953 centry_put_uint16(centry, policy->min_password_length);
954 centry_put_uint16(centry, policy->password_history_length);
955 centry_put_uint32(centry, policy->password_properties);
956 centry_put_nttime(centry, policy->max_password_age);
957 centry_put_nttime(centry, policy->min_password_age);
959 centry_end(centry, "PWD_POL/%s", domain->name);
961 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
963 centry_free(centry);
966 /***************************************************************************
967 ***************************************************************************/
969 static void wcache_save_username_alias(struct winbindd_domain *domain,
970 NTSTATUS status,
971 const char *name, const char *alias)
973 struct cache_entry *centry;
974 fstring uname;
976 if ( (centry = centry_start(domain, status)) == NULL )
977 return;
979 centry_put_string( centry, alias );
981 fstrcpy(uname, name);
982 strupper_m(uname);
983 centry_end(centry, "NSS/NA/%s", uname);
985 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
987 centry_free(centry);
990 static void wcache_save_alias_username(struct winbindd_domain *domain,
991 NTSTATUS status,
992 const char *alias, const char *name)
994 struct cache_entry *centry;
995 fstring uname;
997 if ( (centry = centry_start(domain, status)) == NULL )
998 return;
1000 centry_put_string( centry, name );
1002 fstrcpy(uname, alias);
1003 strupper_m(uname);
1004 centry_end(centry, "NSS/AN/%s", uname);
1006 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1008 centry_free(centry);
1011 /***************************************************************************
1012 ***************************************************************************/
1014 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1015 struct winbindd_domain *domain,
1016 const char *name, char **alias )
1018 struct winbind_cache *cache = get_cache(domain);
1019 struct cache_entry *centry = NULL;
1020 NTSTATUS status;
1021 char *upper_name;
1023 if ( domain->internal )
1024 return NT_STATUS_NOT_SUPPORTED;
1026 if (!cache->tdb)
1027 goto do_query;
1029 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1030 return NT_STATUS_NO_MEMORY;
1031 strupper_m(upper_name);
1033 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1035 SAFE_FREE( upper_name );
1037 if (!centry)
1038 goto do_query;
1040 status = centry->status;
1042 if (!NT_STATUS_IS_OK(status)) {
1043 centry_free(centry);
1044 return status;
1047 *alias = centry_string( centry, mem_ctx );
1049 centry_free(centry);
1051 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1052 name, *alias ? *alias : "(none)"));
1054 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1056 do_query:
1058 /* If its not in cache and we are offline, then fail */
1060 if ( get_global_winbindd_state_offline() || !domain->online ) {
1061 DEBUG(8,("resolve_username_to_alias: rejecting query "
1062 "in offline mode\n"));
1063 return NT_STATUS_NOT_FOUND;
1066 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1068 if ( NT_STATUS_IS_OK( status ) ) {
1069 wcache_save_username_alias(domain, status, name, *alias);
1072 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1073 wcache_save_username_alias(domain, status, name, "(NULL)");
1076 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1077 nt_errstr(status)));
1079 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1080 set_domain_offline( domain );
1083 return status;
1086 /***************************************************************************
1087 ***************************************************************************/
1089 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1090 struct winbindd_domain *domain,
1091 const char *alias, char **name )
1093 struct winbind_cache *cache = get_cache(domain);
1094 struct cache_entry *centry = NULL;
1095 NTSTATUS status;
1096 char *upper_name;
1098 if ( domain->internal )
1099 return NT_STATUS_NOT_SUPPORTED;
1101 if (!cache->tdb)
1102 goto do_query;
1104 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1105 return NT_STATUS_NO_MEMORY;
1106 strupper_m(upper_name);
1108 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1110 SAFE_FREE( upper_name );
1112 if (!centry)
1113 goto do_query;
1115 status = centry->status;
1117 if (!NT_STATUS_IS_OK(status)) {
1118 centry_free(centry);
1119 return status;
1122 *name = centry_string( centry, mem_ctx );
1124 centry_free(centry);
1126 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1127 alias, *name ? *name : "(none)"));
1129 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1131 do_query:
1133 /* If its not in cache and we are offline, then fail */
1135 if ( get_global_winbindd_state_offline() || !domain->online ) {
1136 DEBUG(8,("resolve_alias_to_username: rejecting query "
1137 "in offline mode\n"));
1138 return NT_STATUS_NOT_FOUND;
1141 /* an alias cannot contain a domain prefix or '@' */
1143 if (strchr(alias, '\\') || strchr(alias, '@')) {
1144 DEBUG(10,("resolve_alias_to_username: skipping fully "
1145 "qualified name %s\n", alias));
1146 return NT_STATUS_OBJECT_NAME_INVALID;
1149 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1151 if ( NT_STATUS_IS_OK( status ) ) {
1152 wcache_save_alias_username( domain, status, alias, *name );
1155 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1156 wcache_save_alias_username(domain, status, alias, "(NULL)");
1159 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1160 nt_errstr(status)));
1162 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1163 set_domain_offline( domain );
1166 return status;
1169 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1171 struct winbind_cache *cache = get_cache(domain);
1172 TDB_DATA data;
1173 fstring key_str, tmp;
1174 uint32 rid;
1176 if (!cache->tdb) {
1177 return NT_STATUS_INTERNAL_DB_ERROR;
1180 if (is_null_sid(sid)) {
1181 return NT_STATUS_INVALID_SID;
1184 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1185 return NT_STATUS_INVALID_SID;
1188 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1190 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1191 if (!data.dptr) {
1192 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1195 SAFE_FREE(data.dptr);
1196 return NT_STATUS_OK;
1199 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1200 as new salted ones. */
1202 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1203 TALLOC_CTX *mem_ctx,
1204 const DOM_SID *sid,
1205 const uint8 **cached_nt_pass,
1206 const uint8 **cached_salt)
1208 struct winbind_cache *cache = get_cache(domain);
1209 struct cache_entry *centry = NULL;
1210 NTSTATUS status;
1211 time_t t;
1212 uint32 rid;
1213 fstring tmp;
1215 if (!cache->tdb) {
1216 return NT_STATUS_INTERNAL_DB_ERROR;
1219 if (is_null_sid(sid)) {
1220 return NT_STATUS_INVALID_SID;
1223 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1224 return NT_STATUS_INVALID_SID;
1227 /* Try and get a salted cred first. If we can't
1228 fall back to an unsalted cred. */
1230 centry = wcache_fetch(cache, domain, "CRED/%s",
1231 sid_to_fstring(tmp, sid));
1232 if (!centry) {
1233 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1234 sid_string_dbg(sid)));
1235 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1238 t = centry_time(centry);
1240 /* In the salted case this isn't actually the nt_hash itself,
1241 but the MD5 of the salt + nt_hash. Let the caller
1242 sort this out. It can tell as we only return the cached_salt
1243 if we are returning a salted cred. */
1245 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1246 if (*cached_nt_pass == NULL) {
1247 fstring sidstr;
1249 sid_to_fstring(sidstr, sid);
1251 /* Bad (old) cred cache. Delete and pretend we
1252 don't have it. */
1253 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1254 sidstr));
1255 wcache_delete("CRED/%s", sidstr);
1256 centry_free(centry);
1257 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1260 /* We only have 17 bytes more data in the salted cred case. */
1261 if (centry->len - centry->ofs == 17) {
1262 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1263 } else {
1264 *cached_salt = NULL;
1267 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1268 if (*cached_salt) {
1269 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1272 status = centry->status;
1274 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1275 sid_string_dbg(sid), nt_errstr(status) ));
1277 centry_free(centry);
1278 return status;
1281 /* Store creds for a SID - only writes out new salted ones. */
1283 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1284 TALLOC_CTX *mem_ctx,
1285 const DOM_SID *sid,
1286 const uint8 nt_pass[NT_HASH_LEN])
1288 struct cache_entry *centry;
1289 fstring sid_string;
1290 uint32 rid;
1291 uint8 cred_salt[NT_HASH_LEN];
1292 uint8 salted_hash[NT_HASH_LEN];
1294 if (is_null_sid(sid)) {
1295 return NT_STATUS_INVALID_SID;
1298 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1299 return NT_STATUS_INVALID_SID;
1302 centry = centry_start(domain, NT_STATUS_OK);
1303 if (!centry) {
1304 return NT_STATUS_INTERNAL_DB_ERROR;
1307 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1309 centry_put_time(centry, time(NULL));
1311 /* Create a salt and then salt the hash. */
1312 generate_random_buffer(cred_salt, NT_HASH_LEN);
1313 E_md5hash(cred_salt, nt_pass, salted_hash);
1315 centry_put_hash16(centry, salted_hash);
1316 centry_put_hash16(centry, cred_salt);
1317 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1319 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1321 centry_free(centry);
1323 return NT_STATUS_OK;
1327 /* Query display info. This is the basic user list fn */
1328 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1329 TALLOC_CTX *mem_ctx,
1330 uint32 *num_entries,
1331 WINBIND_USERINFO **info)
1333 struct winbind_cache *cache = get_cache(domain);
1334 struct cache_entry *centry = NULL;
1335 NTSTATUS status;
1336 unsigned int i, retry;
1338 if (!cache->tdb)
1339 goto do_query;
1341 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1342 if (!centry)
1343 goto do_query;
1345 *num_entries = centry_uint32(centry);
1347 if (*num_entries == 0)
1348 goto do_cached;
1350 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1351 if (! (*info)) {
1352 smb_panic_fn("query_user_list out of memory");
1354 for (i=0; i<(*num_entries); i++) {
1355 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1356 (*info)[i].full_name = centry_string(centry, mem_ctx);
1357 (*info)[i].homedir = centry_string(centry, mem_ctx);
1358 (*info)[i].shell = centry_string(centry, mem_ctx);
1359 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1360 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1363 do_cached:
1364 status = centry->status;
1366 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1367 domain->name, nt_errstr(status) ));
1369 centry_free(centry);
1370 return status;
1372 do_query:
1373 *num_entries = 0;
1374 *info = NULL;
1376 /* Return status value returned by seq number check */
1378 if (!NT_STATUS_IS_OK(domain->last_status))
1379 return domain->last_status;
1381 /* Put the query_user_list() in a retry loop. There appears to be
1382 * some bug either with Windows 2000 or Samba's handling of large
1383 * rpc replies. This manifests itself as sudden disconnection
1384 * at a random point in the enumeration of a large (60k) user list.
1385 * The retry loop simply tries the operation again. )-: It's not
1386 * pretty but an acceptable workaround until we work out what the
1387 * real problem is. */
1389 retry = 0;
1390 do {
1392 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1393 domain->name ));
1395 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1396 if (!NT_STATUS_IS_OK(status)) {
1397 DEBUG(3, ("query_user_list: returned 0x%08x, "
1398 "retrying\n", NT_STATUS_V(status)));
1400 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1401 DEBUG(3, ("query_user_list: flushing "
1402 "connection cache\n"));
1403 invalidate_cm_connection(&domain->conn);
1406 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1407 (retry++ < 5));
1409 /* and save it */
1410 refresh_sequence_number(domain, false);
1411 centry = centry_start(domain, status);
1412 if (!centry)
1413 goto skip_save;
1414 centry_put_uint32(centry, *num_entries);
1415 for (i=0; i<(*num_entries); i++) {
1416 centry_put_string(centry, (*info)[i].acct_name);
1417 centry_put_string(centry, (*info)[i].full_name);
1418 centry_put_string(centry, (*info)[i].homedir);
1419 centry_put_string(centry, (*info)[i].shell);
1420 centry_put_sid(centry, &(*info)[i].user_sid);
1421 centry_put_sid(centry, &(*info)[i].group_sid);
1422 if (domain->backend && domain->backend->consistent) {
1423 /* when the backend is consistent we can pre-prime some mappings */
1424 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1425 domain->name,
1426 (*info)[i].acct_name,
1427 &(*info)[i].user_sid,
1428 SID_NAME_USER);
1429 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1430 &(*info)[i].user_sid,
1431 domain->name,
1432 (*info)[i].acct_name,
1433 SID_NAME_USER);
1434 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1437 centry_end(centry, "UL/%s", domain->name);
1438 centry_free(centry);
1440 skip_save:
1441 return status;
1444 /* list all domain groups */
1445 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1446 TALLOC_CTX *mem_ctx,
1447 uint32 *num_entries,
1448 struct acct_info **info)
1450 struct winbind_cache *cache = get_cache(domain);
1451 struct cache_entry *centry = NULL;
1452 NTSTATUS status;
1453 unsigned int i;
1455 if (!cache->tdb)
1456 goto do_query;
1458 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1459 if (!centry)
1460 goto do_query;
1462 *num_entries = centry_uint32(centry);
1464 if (*num_entries == 0)
1465 goto do_cached;
1467 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1468 if (! (*info)) {
1469 smb_panic_fn("enum_dom_groups out of memory");
1471 for (i=0; i<(*num_entries); i++) {
1472 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1473 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1474 (*info)[i].rid = centry_uint32(centry);
1477 do_cached:
1478 status = centry->status;
1480 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1481 domain->name, nt_errstr(status) ));
1483 centry_free(centry);
1484 return status;
1486 do_query:
1487 *num_entries = 0;
1488 *info = NULL;
1490 /* Return status value returned by seq number check */
1492 if (!NT_STATUS_IS_OK(domain->last_status))
1493 return domain->last_status;
1495 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1496 domain->name ));
1498 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1500 /* and save it */
1501 refresh_sequence_number(domain, false);
1502 centry = centry_start(domain, status);
1503 if (!centry)
1504 goto skip_save;
1505 centry_put_uint32(centry, *num_entries);
1506 for (i=0; i<(*num_entries); i++) {
1507 centry_put_string(centry, (*info)[i].acct_name);
1508 centry_put_string(centry, (*info)[i].acct_desc);
1509 centry_put_uint32(centry, (*info)[i].rid);
1511 centry_end(centry, "GL/%s/domain", domain->name);
1512 centry_free(centry);
1514 skip_save:
1515 return status;
1518 /* list all domain groups */
1519 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1520 TALLOC_CTX *mem_ctx,
1521 uint32 *num_entries,
1522 struct acct_info **info)
1524 struct winbind_cache *cache = get_cache(domain);
1525 struct cache_entry *centry = NULL;
1526 NTSTATUS status;
1527 unsigned int i;
1529 if (!cache->tdb)
1530 goto do_query;
1532 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1533 if (!centry)
1534 goto do_query;
1536 *num_entries = centry_uint32(centry);
1538 if (*num_entries == 0)
1539 goto do_cached;
1541 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1542 if (! (*info)) {
1543 smb_panic_fn("enum_dom_groups out of memory");
1545 for (i=0; i<(*num_entries); i++) {
1546 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1547 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1548 (*info)[i].rid = centry_uint32(centry);
1551 do_cached:
1553 /* If we are returning cached data and the domain controller
1554 is down then we don't know whether the data is up to date
1555 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1556 indicate this. */
1558 if (wcache_server_down(domain)) {
1559 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1560 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1561 } else
1562 status = centry->status;
1564 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1565 domain->name, nt_errstr(status) ));
1567 centry_free(centry);
1568 return status;
1570 do_query:
1571 *num_entries = 0;
1572 *info = NULL;
1574 /* Return status value returned by seq number check */
1576 if (!NT_STATUS_IS_OK(domain->last_status))
1577 return domain->last_status;
1579 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1580 domain->name ));
1582 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1584 /* and save it */
1585 refresh_sequence_number(domain, false);
1586 centry = centry_start(domain, status);
1587 if (!centry)
1588 goto skip_save;
1589 centry_put_uint32(centry, *num_entries);
1590 for (i=0; i<(*num_entries); i++) {
1591 centry_put_string(centry, (*info)[i].acct_name);
1592 centry_put_string(centry, (*info)[i].acct_desc);
1593 centry_put_uint32(centry, (*info)[i].rid);
1595 centry_end(centry, "GL/%s/local", domain->name);
1596 centry_free(centry);
1598 skip_save:
1599 return status;
1602 /* convert a single name to a sid in a domain */
1603 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1604 TALLOC_CTX *mem_ctx,
1605 enum winbindd_cmd orig_cmd,
1606 const char *domain_name,
1607 const char *name,
1608 DOM_SID *sid,
1609 enum lsa_SidType *type)
1611 struct winbind_cache *cache = get_cache(domain);
1612 struct cache_entry *centry = NULL;
1613 NTSTATUS status;
1614 fstring uname;
1616 if (!cache->tdb)
1617 goto do_query;
1619 fstrcpy(uname, name);
1620 strupper_m(uname);
1621 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1622 if (!centry)
1623 goto do_query;
1625 status = centry->status;
1626 if (NT_STATUS_IS_OK(status)) {
1627 *type = (enum lsa_SidType)centry_uint32(centry);
1628 centry_sid(centry, mem_ctx, sid);
1631 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1632 domain->name, nt_errstr(status) ));
1634 centry_free(centry);
1635 return status;
1637 do_query:
1638 ZERO_STRUCTP(sid);
1640 /* If the seq number check indicated that there is a problem
1641 * with this DC, then return that status... except for
1642 * access_denied. This is special because the dc may be in
1643 * "restrict anonymous = 1" mode, in which case it will deny
1644 * most unauthenticated operations, but *will* allow the LSA
1645 * name-to-sid that we try as a fallback. */
1647 if (!(NT_STATUS_IS_OK(domain->last_status)
1648 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1649 return domain->last_status;
1651 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1652 domain->name ));
1654 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1655 domain_name, name, sid, type);
1657 /* and save it */
1658 refresh_sequence_number(domain, false);
1660 if (domain->online &&
1661 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1662 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1664 /* Only save the reverse mapping if this was not a UPN */
1665 if (!strchr(name, '@')) {
1666 strupper_m(CONST_DISCARD(char *,domain_name));
1667 strlower_m(CONST_DISCARD(char *,name));
1668 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1672 return status;
1675 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1676 given */
1677 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1678 TALLOC_CTX *mem_ctx,
1679 const DOM_SID *sid,
1680 char **domain_name,
1681 char **name,
1682 enum lsa_SidType *type)
1684 struct winbind_cache *cache = get_cache(domain);
1685 struct cache_entry *centry = NULL;
1686 NTSTATUS status;
1687 fstring sid_string;
1689 if (!cache->tdb)
1690 goto do_query;
1692 centry = wcache_fetch(cache, domain, "SN/%s",
1693 sid_to_fstring(sid_string, sid));
1694 if (!centry)
1695 goto do_query;
1697 status = centry->status;
1698 if (NT_STATUS_IS_OK(status)) {
1699 *type = (enum lsa_SidType)centry_uint32(centry);
1700 *domain_name = centry_string(centry, mem_ctx);
1701 *name = centry_string(centry, mem_ctx);
1704 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1705 domain->name, nt_errstr(status) ));
1707 centry_free(centry);
1708 return status;
1710 do_query:
1711 *name = NULL;
1712 *domain_name = NULL;
1714 /* If the seq number check indicated that there is a problem
1715 * with this DC, then return that status... except for
1716 * access_denied. This is special because the dc may be in
1717 * "restrict anonymous = 1" mode, in which case it will deny
1718 * most unauthenticated operations, but *will* allow the LSA
1719 * sid-to-name that we try as a fallback. */
1721 if (!(NT_STATUS_IS_OK(domain->last_status)
1722 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1723 return domain->last_status;
1725 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1726 domain->name ));
1728 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1730 /* and save it */
1731 refresh_sequence_number(domain, false);
1732 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1734 /* We can't save the name to sid mapping here, as with sid history a
1735 * later name2sid would give the wrong sid. */
1737 return status;
1740 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1741 TALLOC_CTX *mem_ctx,
1742 const DOM_SID *domain_sid,
1743 uint32 *rids,
1744 size_t num_rids,
1745 char **domain_name,
1746 char ***names,
1747 enum lsa_SidType **types)
1749 struct winbind_cache *cache = get_cache(domain);
1750 size_t i;
1751 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1752 bool have_mapped;
1753 bool have_unmapped;
1755 *domain_name = NULL;
1756 *names = NULL;
1757 *types = NULL;
1759 if (!cache->tdb) {
1760 goto do_query;
1763 if (num_rids == 0) {
1764 return NT_STATUS_OK;
1767 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1768 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1770 if ((*names == NULL) || (*types == NULL)) {
1771 result = NT_STATUS_NO_MEMORY;
1772 goto error;
1775 have_mapped = have_unmapped = false;
1777 for (i=0; i<num_rids; i++) {
1778 DOM_SID sid;
1779 struct cache_entry *centry;
1780 fstring tmp;
1782 if (!sid_compose(&sid, domain_sid, rids[i])) {
1783 result = NT_STATUS_INTERNAL_ERROR;
1784 goto error;
1787 centry = wcache_fetch(cache, domain, "SN/%s",
1788 sid_to_fstring(tmp, &sid));
1789 if (!centry) {
1790 goto do_query;
1793 (*types)[i] = SID_NAME_UNKNOWN;
1794 (*names)[i] = talloc_strdup(*names, "");
1796 if (NT_STATUS_IS_OK(centry->status)) {
1797 char *dom;
1798 have_mapped = true;
1799 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1801 dom = centry_string(centry, mem_ctx);
1802 if (*domain_name == NULL) {
1803 *domain_name = dom;
1804 } else {
1805 talloc_free(dom);
1808 (*names)[i] = centry_string(centry, *names);
1810 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1811 have_unmapped = true;
1813 } else {
1814 /* something's definitely wrong */
1815 result = centry->status;
1816 goto error;
1819 centry_free(centry);
1822 if (!have_mapped) {
1823 return NT_STATUS_NONE_MAPPED;
1825 if (!have_unmapped) {
1826 return NT_STATUS_OK;
1828 return STATUS_SOME_UNMAPPED;
1830 do_query:
1832 TALLOC_FREE(*names);
1833 TALLOC_FREE(*types);
1835 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1836 rids, num_rids, domain_name,
1837 names, types);
1840 None of the queried rids has been found so save all negative entries
1842 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1843 for (i = 0; i < num_rids; i++) {
1844 DOM_SID sid;
1845 const char *name = "";
1846 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1847 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1849 if (!sid_compose(&sid, domain_sid, rids[i])) {
1850 return NT_STATUS_INTERNAL_ERROR;
1853 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1854 name, type);
1857 return result;
1861 Some or all of the queried rids have been found.
1863 if (!NT_STATUS_IS_OK(result) &&
1864 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1865 return result;
1868 refresh_sequence_number(domain, false);
1870 for (i=0; i<num_rids; i++) {
1871 DOM_SID sid;
1872 NTSTATUS status;
1874 if (!sid_compose(&sid, domain_sid, rids[i])) {
1875 result = NT_STATUS_INTERNAL_ERROR;
1876 goto error;
1879 status = (*types)[i] == SID_NAME_UNKNOWN ?
1880 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1882 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1883 (*names)[i], (*types)[i]);
1886 return result;
1888 error:
1890 TALLOC_FREE(*names);
1891 TALLOC_FREE(*types);
1892 return result;
1895 /* Lookup user information from a rid */
1896 static NTSTATUS query_user(struct winbindd_domain *domain,
1897 TALLOC_CTX *mem_ctx,
1898 const DOM_SID *user_sid,
1899 WINBIND_USERINFO *info)
1901 struct winbind_cache *cache = get_cache(domain);
1902 struct cache_entry *centry = NULL;
1903 NTSTATUS status;
1904 fstring tmp;
1906 if (!cache->tdb)
1907 goto do_query;
1909 centry = wcache_fetch(cache, domain, "U/%s",
1910 sid_to_fstring(tmp, user_sid));
1912 /* If we have an access denied cache entry and a cached info3 in the
1913 samlogon cache then do a query. This will force the rpc back end
1914 to return the info3 data. */
1916 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1917 netsamlogon_cache_have(user_sid)) {
1918 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1919 domain->last_status = NT_STATUS_OK;
1920 centry_free(centry);
1921 goto do_query;
1924 if (!centry)
1925 goto do_query;
1927 /* if status is not ok then this is a negative hit
1928 and the rest of the data doesn't matter */
1929 status = centry->status;
1930 if (NT_STATUS_IS_OK(status)) {
1931 info->acct_name = centry_string(centry, mem_ctx);
1932 info->full_name = centry_string(centry, mem_ctx);
1933 info->homedir = centry_string(centry, mem_ctx);
1934 info->shell = centry_string(centry, mem_ctx);
1935 info->primary_gid = centry_uint32(centry);
1936 centry_sid(centry, mem_ctx, &info->user_sid);
1937 centry_sid(centry, mem_ctx, &info->group_sid);
1940 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1941 domain->name, nt_errstr(status) ));
1943 centry_free(centry);
1944 return status;
1946 do_query:
1947 ZERO_STRUCTP(info);
1949 /* Return status value returned by seq number check */
1951 if (!NT_STATUS_IS_OK(domain->last_status))
1952 return domain->last_status;
1954 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1955 domain->name ));
1957 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1959 /* and save it */
1960 refresh_sequence_number(domain, false);
1961 wcache_save_user(domain, status, info);
1963 return status;
1967 /* Lookup groups a user is a member of. */
1968 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1969 TALLOC_CTX *mem_ctx,
1970 const DOM_SID *user_sid,
1971 uint32 *num_groups, DOM_SID **user_gids)
1973 struct winbind_cache *cache = get_cache(domain);
1974 struct cache_entry *centry = NULL;
1975 NTSTATUS status;
1976 unsigned int i;
1977 fstring sid_string;
1979 if (!cache->tdb)
1980 goto do_query;
1982 centry = wcache_fetch(cache, domain, "UG/%s",
1983 sid_to_fstring(sid_string, user_sid));
1985 /* If we have an access denied cache entry and a cached info3 in the
1986 samlogon cache then do a query. This will force the rpc back end
1987 to return the info3 data. */
1989 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1990 netsamlogon_cache_have(user_sid)) {
1991 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1992 domain->last_status = NT_STATUS_OK;
1993 centry_free(centry);
1994 goto do_query;
1997 if (!centry)
1998 goto do_query;
2000 *num_groups = centry_uint32(centry);
2002 if (*num_groups == 0)
2003 goto do_cached;
2005 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2006 if (! (*user_gids)) {
2007 smb_panic_fn("lookup_usergroups out of memory");
2009 for (i=0; i<(*num_groups); i++) {
2010 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
2013 do_cached:
2014 status = centry->status;
2016 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2017 domain->name, nt_errstr(status) ));
2019 centry_free(centry);
2020 return status;
2022 do_query:
2023 (*num_groups) = 0;
2024 (*user_gids) = NULL;
2026 /* Return status value returned by seq number check */
2028 if (!NT_STATUS_IS_OK(domain->last_status))
2029 return domain->last_status;
2031 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2032 domain->name ));
2034 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2036 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2037 goto skip_save;
2039 /* and save it */
2040 refresh_sequence_number(domain, false);
2041 centry = centry_start(domain, status);
2042 if (!centry)
2043 goto skip_save;
2045 centry_put_uint32(centry, *num_groups);
2046 for (i=0; i<(*num_groups); i++) {
2047 centry_put_sid(centry, &(*user_gids)[i]);
2050 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2051 centry_free(centry);
2053 skip_save:
2054 return status;
2057 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2058 TALLOC_CTX *mem_ctx,
2059 uint32 num_sids, const DOM_SID *sids,
2060 uint32 *num_aliases, uint32 **alias_rids)
2062 struct winbind_cache *cache = get_cache(domain);
2063 struct cache_entry *centry = NULL;
2064 NTSTATUS status;
2065 char *sidlist = talloc_strdup(mem_ctx, "");
2066 int i;
2068 if (!cache->tdb)
2069 goto do_query;
2071 if (num_sids == 0) {
2072 *num_aliases = 0;
2073 *alias_rids = NULL;
2074 return NT_STATUS_OK;
2077 /* We need to cache indexed by the whole list of SIDs, the aliases
2078 * resulting might come from any of the SIDs. */
2080 for (i=0; i<num_sids; i++) {
2081 fstring tmp;
2082 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2083 sid_to_fstring(tmp, &sids[i]));
2084 if (sidlist == NULL)
2085 return NT_STATUS_NO_MEMORY;
2088 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2090 if (!centry)
2091 goto do_query;
2093 *num_aliases = centry_uint32(centry);
2094 *alias_rids = NULL;
2096 if (*num_aliases) {
2097 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2099 if ((*alias_rids) == NULL) {
2100 centry_free(centry);
2101 return NT_STATUS_NO_MEMORY;
2103 } else {
2104 (*alias_rids) = NULL;
2107 for (i=0; i<(*num_aliases); i++)
2108 (*alias_rids)[i] = centry_uint32(centry);
2110 status = centry->status;
2112 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2113 "status %s\n", domain->name, nt_errstr(status)));
2115 centry_free(centry);
2116 return status;
2118 do_query:
2119 (*num_aliases) = 0;
2120 (*alias_rids) = NULL;
2122 if (!NT_STATUS_IS_OK(domain->last_status))
2123 return domain->last_status;
2125 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2126 "for domain %s\n", domain->name ));
2128 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2129 num_sids, sids,
2130 num_aliases, alias_rids);
2132 /* and save it */
2133 refresh_sequence_number(domain, false);
2134 centry = centry_start(domain, status);
2135 if (!centry)
2136 goto skip_save;
2137 centry_put_uint32(centry, *num_aliases);
2138 for (i=0; i<(*num_aliases); i++)
2139 centry_put_uint32(centry, (*alias_rids)[i]);
2140 centry_end(centry, "UA%s", sidlist);
2141 centry_free(centry);
2143 skip_save:
2144 return status;
2148 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2149 TALLOC_CTX *mem_ctx,
2150 const DOM_SID *group_sid, uint32 *num_names,
2151 DOM_SID **sid_mem, char ***names,
2152 uint32 **name_types)
2154 struct winbind_cache *cache = get_cache(domain);
2155 struct cache_entry *centry = NULL;
2156 NTSTATUS status;
2157 unsigned int i;
2158 fstring sid_string;
2160 if (!cache->tdb)
2161 goto do_query;
2163 centry = wcache_fetch(cache, domain, "GM/%s",
2164 sid_to_fstring(sid_string, group_sid));
2165 if (!centry)
2166 goto do_query;
2168 *num_names = centry_uint32(centry);
2170 if (*num_names == 0)
2171 goto do_cached;
2173 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2174 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2175 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2177 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2178 smb_panic_fn("lookup_groupmem out of memory");
2181 for (i=0; i<(*num_names); i++) {
2182 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
2183 (*names)[i] = centry_string(centry, mem_ctx);
2184 (*name_types)[i] = centry_uint32(centry);
2187 do_cached:
2188 status = centry->status;
2190 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2191 domain->name, nt_errstr(status)));
2193 centry_free(centry);
2194 return status;
2196 do_query:
2197 (*num_names) = 0;
2198 (*sid_mem) = NULL;
2199 (*names) = NULL;
2200 (*name_types) = NULL;
2202 /* Return status value returned by seq number check */
2204 if (!NT_STATUS_IS_OK(domain->last_status))
2205 return domain->last_status;
2207 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2208 domain->name ));
2210 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2211 sid_mem, names, name_types);
2213 /* and save it */
2214 refresh_sequence_number(domain, false);
2215 centry = centry_start(domain, status);
2216 if (!centry)
2217 goto skip_save;
2218 centry_put_uint32(centry, *num_names);
2219 for (i=0; i<(*num_names); i++) {
2220 centry_put_sid(centry, &(*sid_mem)[i]);
2221 centry_put_string(centry, (*names)[i]);
2222 centry_put_uint32(centry, (*name_types)[i]);
2224 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2225 centry_free(centry);
2227 skip_save:
2228 return status;
2231 /* find the sequence number for a domain */
2232 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2234 refresh_sequence_number(domain, false);
2236 *seq = domain->sequence_number;
2238 return NT_STATUS_OK;
2241 /* enumerate trusted domains
2242 * (we need to have the list of trustdoms in the cache when we go offline) -
2243 * Guenther */
2244 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2245 TALLOC_CTX *mem_ctx,
2246 uint32 *num_domains,
2247 char ***names,
2248 char ***alt_names,
2249 DOM_SID **dom_sids)
2251 struct winbind_cache *cache = get_cache(domain);
2252 struct cache_entry *centry = NULL;
2253 NTSTATUS status;
2254 int i;
2256 if (!cache->tdb)
2257 goto do_query;
2259 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2261 if (!centry) {
2262 goto do_query;
2265 *num_domains = centry_uint32(centry);
2267 if (*num_domains) {
2268 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2269 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2270 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2272 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2273 smb_panic_fn("trusted_domains out of memory");
2275 } else {
2276 (*names) = NULL;
2277 (*alt_names) = NULL;
2278 (*dom_sids) = NULL;
2281 for (i=0; i<(*num_domains); i++) {
2282 (*names)[i] = centry_string(centry, mem_ctx);
2283 (*alt_names)[i] = centry_string(centry, mem_ctx);
2284 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2285 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2289 status = centry->status;
2291 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2292 domain->name, *num_domains, nt_errstr(status) ));
2294 centry_free(centry);
2295 return status;
2297 do_query:
2298 (*num_domains) = 0;
2299 (*dom_sids) = NULL;
2300 (*names) = NULL;
2301 (*alt_names) = NULL;
2303 /* Return status value returned by seq number check */
2305 if (!NT_STATUS_IS_OK(domain->last_status))
2306 return domain->last_status;
2308 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2309 domain->name ));
2311 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2312 names, alt_names, dom_sids);
2314 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2315 * so that the generic centry handling still applies correctly -
2316 * Guenther*/
2318 if (!NT_STATUS_IS_ERR(status)) {
2319 status = NT_STATUS_OK;
2323 #if 0 /* Disabled as we want the trust dom list to be managed by
2324 the main parent and always to make the query. --jerry */
2326 /* and save it */
2327 refresh_sequence_number(domain, false);
2329 centry = centry_start(domain, status);
2330 if (!centry)
2331 goto skip_save;
2333 centry_put_uint32(centry, *num_domains);
2335 for (i=0; i<(*num_domains); i++) {
2336 centry_put_string(centry, (*names)[i]);
2337 centry_put_string(centry, (*alt_names)[i]);
2338 centry_put_sid(centry, &(*dom_sids)[i]);
2341 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2343 centry_free(centry);
2345 skip_save:
2346 #endif
2348 return status;
2351 /* get lockout policy */
2352 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2353 TALLOC_CTX *mem_ctx,
2354 struct samr_DomInfo12 *policy)
2356 struct winbind_cache *cache = get_cache(domain);
2357 struct cache_entry *centry = NULL;
2358 NTSTATUS status;
2360 if (!cache->tdb)
2361 goto do_query;
2363 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2365 if (!centry)
2366 goto do_query;
2368 policy->lockout_duration = centry_nttime(centry);
2369 policy->lockout_window = centry_nttime(centry);
2370 policy->lockout_threshold = centry_uint16(centry);
2372 status = centry->status;
2374 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2375 domain->name, nt_errstr(status) ));
2377 centry_free(centry);
2378 return status;
2380 do_query:
2381 ZERO_STRUCTP(policy);
2383 /* Return status value returned by seq number check */
2385 if (!NT_STATUS_IS_OK(domain->last_status))
2386 return domain->last_status;
2388 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2389 domain->name ));
2391 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2393 /* and save it */
2394 refresh_sequence_number(domain, false);
2395 wcache_save_lockout_policy(domain, status, policy);
2397 return status;
2400 /* get password policy */
2401 static NTSTATUS password_policy(struct winbindd_domain *domain,
2402 TALLOC_CTX *mem_ctx,
2403 struct samr_DomInfo1 *policy)
2405 struct winbind_cache *cache = get_cache(domain);
2406 struct cache_entry *centry = NULL;
2407 NTSTATUS status;
2409 if (!cache->tdb)
2410 goto do_query;
2412 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2414 if (!centry)
2415 goto do_query;
2417 policy->min_password_length = centry_uint16(centry);
2418 policy->password_history_length = centry_uint16(centry);
2419 policy->password_properties = centry_uint32(centry);
2420 policy->max_password_age = centry_nttime(centry);
2421 policy->min_password_age = centry_nttime(centry);
2423 status = centry->status;
2425 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2426 domain->name, nt_errstr(status) ));
2428 centry_free(centry);
2429 return status;
2431 do_query:
2432 ZERO_STRUCTP(policy);
2434 /* Return status value returned by seq number check */
2436 if (!NT_STATUS_IS_OK(domain->last_status))
2437 return domain->last_status;
2439 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2440 domain->name ));
2442 status = domain->backend->password_policy(domain, mem_ctx, policy);
2444 /* and save it */
2445 refresh_sequence_number(domain, false);
2446 if (NT_STATUS_IS_OK(status)) {
2447 wcache_save_password_policy(domain, status, policy);
2450 return status;
2454 /* Invalidate cached user and group lists coherently */
2456 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2457 void *state)
2459 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2460 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2461 tdb_delete(the_tdb, kbuf);
2463 return 0;
2466 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2468 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2469 struct netr_SamInfo3 *info3)
2471 DOM_SID sid;
2472 fstring key_str, sid_string;
2473 struct winbind_cache *cache;
2475 /* dont clear cached U/SID and UG/SID entries when we want to logon
2476 * offline - gd */
2478 if (lp_winbind_offline_logon()) {
2479 return;
2482 if (!domain)
2483 return;
2485 cache = get_cache(domain);
2487 if (!cache->tdb) {
2488 return;
2491 sid_copy(&sid, info3->base.domain_sid);
2492 sid_append_rid(&sid, info3->base.rid);
2494 /* Clear U/SID cache entry */
2495 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2496 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2497 tdb_delete(cache->tdb, string_tdb_data(key_str));
2499 /* Clear UG/SID cache entry */
2500 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2501 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2502 tdb_delete(cache->tdb, string_tdb_data(key_str));
2504 /* Samba/winbindd never needs this. */
2505 netsamlogon_clear_cached_user(info3);
2508 bool wcache_invalidate_cache(void)
2510 struct winbindd_domain *domain;
2512 for (domain = domain_list(); domain; domain = domain->next) {
2513 struct winbind_cache *cache = get_cache(domain);
2515 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2516 "entries for %s\n", domain->name));
2517 if (cache) {
2518 if (cache->tdb) {
2519 tdb_traverse(cache->tdb, traverse_fn, NULL);
2520 } else {
2521 return false;
2525 return true;
2528 bool init_wcache(void)
2530 if (wcache == NULL) {
2531 wcache = SMB_XMALLOC_P(struct winbind_cache);
2532 ZERO_STRUCTP(wcache);
2535 if (wcache->tdb != NULL)
2536 return true;
2538 /* when working offline we must not clear the cache on restart */
2539 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2540 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2541 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2542 O_RDWR|O_CREAT, 0600);
2544 if (wcache->tdb == NULL) {
2545 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2546 return false;
2549 return true;
2552 /************************************************************************
2553 This is called by the parent to initialize the cache file.
2554 We don't need sophisticated locking here as we know we're the
2555 only opener.
2556 ************************************************************************/
2558 bool initialize_winbindd_cache(void)
2560 bool cache_bad = true;
2561 uint32 vers;
2563 if (!init_wcache()) {
2564 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2565 return false;
2568 /* Check version number. */
2569 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2570 vers == WINBINDD_CACHE_VERSION) {
2571 cache_bad = false;
2574 if (cache_bad) {
2575 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2576 "and re-creating with version number %d\n",
2577 WINBINDD_CACHE_VERSION ));
2579 tdb_close(wcache->tdb);
2580 wcache->tdb = NULL;
2582 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2583 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2584 lock_path("winbindd_cache.tdb"),
2585 strerror(errno) ));
2586 return false;
2588 if (!init_wcache()) {
2589 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2590 "init_wcache failed.\n"));
2591 return false;
2594 /* Write the version. */
2595 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2596 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2597 tdb_errorstr(wcache->tdb) ));
2598 return false;
2602 tdb_close(wcache->tdb);
2603 wcache->tdb = NULL;
2604 return true;
2607 void close_winbindd_cache(void)
2609 if (!wcache) {
2610 return;
2612 if (wcache->tdb) {
2613 tdb_close(wcache->tdb);
2614 wcache->tdb = NULL;
2618 void cache_store_response(pid_t pid, struct winbindd_response *response)
2620 fstring key_str;
2622 if (!init_wcache())
2623 return;
2625 DEBUG(10, ("Storing response for pid %d, len %d\n",
2626 (int)pid, response->length));
2628 fstr_sprintf(key_str, "DR/%d", (int)pid);
2629 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2630 make_tdb_data((uint8 *)response, sizeof(*response)),
2631 TDB_REPLACE) == -1)
2632 return;
2634 if (response->length == sizeof(*response))
2635 return;
2637 /* There's extra data */
2639 DEBUG(10, ("Storing extra data: len=%d\n",
2640 (int)(response->length - sizeof(*response))));
2642 fstr_sprintf(key_str, "DE/%d", (int)pid);
2643 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2644 make_tdb_data((uint8 *)response->extra_data.data,
2645 response->length - sizeof(*response)),
2646 TDB_REPLACE) == 0)
2647 return;
2649 /* We could not store the extra data, make sure the tdb does not
2650 * contain a main record with wrong dangling extra data */
2652 fstr_sprintf(key_str, "DR/%d", (int)pid);
2653 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2655 return;
2658 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2660 TDB_DATA data;
2661 fstring key_str;
2663 if (!init_wcache())
2664 return false;
2666 DEBUG(10, ("Retrieving response for pid %d\n", (int)pid));
2668 fstr_sprintf(key_str, "DR/%d", (int)pid);
2669 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2671 if (data.dptr == NULL)
2672 return false;
2674 if (data.dsize != sizeof(*response))
2675 return false;
2677 memcpy(response, data.dptr, data.dsize);
2678 SAFE_FREE(data.dptr);
2680 if (response->length == sizeof(*response)) {
2681 response->extra_data.data = NULL;
2682 return true;
2685 /* There's extra data */
2687 DEBUG(10, ("Retrieving extra data length=%d\n",
2688 (int)(response->length - sizeof(*response))));
2690 fstr_sprintf(key_str, "DE/%d", (int)pid);
2691 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2693 if (data.dptr == NULL) {
2694 DEBUG(0, ("Did not find extra data\n"));
2695 return false;
2698 if (data.dsize != (response->length - sizeof(*response))) {
2699 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2700 SAFE_FREE(data.dptr);
2701 return false;
2704 dump_data(11, (uint8 *)data.dptr, data.dsize);
2706 response->extra_data.data = data.dptr;
2707 return true;
2710 void cache_cleanup_response(pid_t pid)
2712 fstring key_str;
2714 if (!init_wcache())
2715 return;
2717 fstr_sprintf(key_str, "DR/%d", (int)pid);
2718 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2720 fstr_sprintf(key_str, "DE/%d", (int)pid);
2721 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2723 return;
2727 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2728 char **domain_name, char **name,
2729 enum lsa_SidType *type)
2731 struct winbindd_domain *domain;
2732 struct winbind_cache *cache;
2733 struct cache_entry *centry = NULL;
2734 NTSTATUS status;
2735 fstring tmp;
2737 domain = find_lookup_domain_from_sid(sid);
2738 if (domain == NULL) {
2739 return false;
2742 cache = get_cache(domain);
2744 if (cache->tdb == NULL) {
2745 return false;
2748 centry = wcache_fetch(cache, domain, "SN/%s",
2749 sid_to_fstring(tmp, sid));
2750 if (centry == NULL) {
2751 return false;
2754 if (NT_STATUS_IS_OK(centry->status)) {
2755 *type = (enum lsa_SidType)centry_uint32(centry);
2756 *domain_name = centry_string(centry, mem_ctx);
2757 *name = centry_string(centry, mem_ctx);
2760 status = centry->status;
2761 centry_free(centry);
2762 return NT_STATUS_IS_OK(status);
2765 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2766 const char *domain_name,
2767 const char *name,
2768 DOM_SID *sid,
2769 enum lsa_SidType *type)
2771 struct winbindd_domain *domain;
2772 struct winbind_cache *cache;
2773 struct cache_entry *centry = NULL;
2774 NTSTATUS status;
2775 fstring uname;
2776 bool original_online_state;
2778 domain = find_lookup_domain_from_name(domain_name);
2779 if (domain == NULL) {
2780 return false;
2783 cache = get_cache(domain);
2785 if (cache->tdb == NULL) {
2786 return false;
2789 fstrcpy(uname, name);
2790 strupper_m(uname);
2792 /* If we are doing a cached logon, temporarily set the domain
2793 offline so the cache won't expire the entry */
2795 original_online_state = domain->online;
2796 domain->online = false;
2797 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2798 domain->online = original_online_state;
2800 if (centry == NULL) {
2801 return false;
2804 if (NT_STATUS_IS_OK(centry->status)) {
2805 *type = (enum lsa_SidType)centry_uint32(centry);
2806 centry_sid(centry, mem_ctx, sid);
2809 status = centry->status;
2810 centry_free(centry);
2812 return NT_STATUS_IS_OK(status);
2815 void cache_name2sid(struct winbindd_domain *domain,
2816 const char *domain_name, const char *name,
2817 enum lsa_SidType type, const DOM_SID *sid)
2819 refresh_sequence_number(domain, false);
2820 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2821 sid, type);
2825 * The original idea that this cache only contains centries has
2826 * been blurred - now other stuff gets put in here. Ensure we
2827 * ignore these things on cleanup.
2830 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2831 TDB_DATA dbuf, void *state)
2833 struct cache_entry *centry;
2835 if (is_non_centry_key(kbuf)) {
2836 return 0;
2839 centry = wcache_fetch_raw((char *)kbuf.dptr);
2840 if (!centry) {
2841 return 0;
2844 if (!NT_STATUS_IS_OK(centry->status)) {
2845 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2846 tdb_delete(the_tdb, kbuf);
2849 centry_free(centry);
2850 return 0;
2853 /* flush the cache */
2854 void wcache_flush_cache(void)
2856 if (!wcache)
2857 return;
2858 if (wcache->tdb) {
2859 tdb_close(wcache->tdb);
2860 wcache->tdb = NULL;
2862 if (!winbindd_use_cache()) {
2863 return;
2866 /* when working offline we must not clear the cache on restart */
2867 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2868 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2869 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2870 O_RDWR|O_CREAT, 0600);
2872 if (!wcache->tdb) {
2873 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2874 return;
2877 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2879 DEBUG(10,("wcache_flush_cache success\n"));
2882 /* Count cached creds */
2884 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2885 void *state)
2887 int *cred_count = (int*)state;
2889 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2890 (*cred_count)++;
2892 return 0;
2895 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2897 struct winbind_cache *cache = get_cache(domain);
2899 *count = 0;
2901 if (!cache->tdb) {
2902 return NT_STATUS_INTERNAL_DB_ERROR;
2905 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2907 return NT_STATUS_OK;
2910 struct cred_list {
2911 struct cred_list *prev, *next;
2912 TDB_DATA key;
2913 fstring name;
2914 time_t created;
2916 static struct cred_list *wcache_cred_list;
2918 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2919 void *state)
2921 struct cred_list *cred;
2923 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2925 cred = SMB_MALLOC_P(struct cred_list);
2926 if (cred == NULL) {
2927 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2928 return -1;
2931 ZERO_STRUCTP(cred);
2933 /* save a copy of the key */
2935 fstrcpy(cred->name, (const char *)kbuf.dptr);
2936 DLIST_ADD(wcache_cred_list, cred);
2939 return 0;
2942 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2944 struct winbind_cache *cache = get_cache(domain);
2945 NTSTATUS status;
2946 int ret;
2947 struct cred_list *cred, *oldest = NULL;
2949 if (!cache->tdb) {
2950 return NT_STATUS_INTERNAL_DB_ERROR;
2953 /* we possibly already have an entry */
2954 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2956 fstring key_str, tmp;
2958 DEBUG(11,("we already have an entry, deleting that\n"));
2960 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2962 tdb_delete(cache->tdb, string_tdb_data(key_str));
2964 return NT_STATUS_OK;
2967 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2968 if (ret == 0) {
2969 return NT_STATUS_OK;
2970 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2971 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2974 ZERO_STRUCTP(oldest);
2976 for (cred = wcache_cred_list; cred; cred = cred->next) {
2978 TDB_DATA data;
2979 time_t t;
2981 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2982 if (!data.dptr) {
2983 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2984 cred->name));
2985 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2986 goto done;
2989 t = IVAL(data.dptr, 0);
2990 SAFE_FREE(data.dptr);
2992 if (!oldest) {
2993 oldest = SMB_MALLOC_P(struct cred_list);
2994 if (oldest == NULL) {
2995 status = NT_STATUS_NO_MEMORY;
2996 goto done;
2999 fstrcpy(oldest->name, cred->name);
3000 oldest->created = t;
3001 continue;
3004 if (t < oldest->created) {
3005 fstrcpy(oldest->name, cred->name);
3006 oldest->created = t;
3010 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3011 status = NT_STATUS_OK;
3012 } else {
3013 status = NT_STATUS_UNSUCCESSFUL;
3015 done:
3016 SAFE_FREE(wcache_cred_list);
3017 SAFE_FREE(oldest);
3019 return status;
3022 /* Change the global online/offline state. */
3023 bool set_global_winbindd_state_offline(void)
3025 TDB_DATA data;
3027 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3029 /* Only go offline if someone has created
3030 the key "WINBINDD_OFFLINE" in the cache tdb. */
3032 if (wcache == NULL || wcache->tdb == NULL) {
3033 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3034 return false;
3037 if (!lp_winbind_offline_logon()) {
3038 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3039 return false;
3042 if (global_winbindd_offline_state) {
3043 /* Already offline. */
3044 return true;
3047 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3049 if (!data.dptr || data.dsize != 4) {
3050 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3051 SAFE_FREE(data.dptr);
3052 return false;
3053 } else {
3054 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3055 global_winbindd_offline_state = true;
3056 SAFE_FREE(data.dptr);
3057 return true;
3061 void set_global_winbindd_state_online(void)
3063 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3065 if (!lp_winbind_offline_logon()) {
3066 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3067 return;
3070 if (!global_winbindd_offline_state) {
3071 /* Already online. */
3072 return;
3074 global_winbindd_offline_state = false;
3076 if (!wcache->tdb) {
3077 return;
3080 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3081 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3084 bool get_global_winbindd_state_offline(void)
3086 return global_winbindd_offline_state;
3089 /***********************************************************************
3090 Validate functions for all possible cache tdb keys.
3091 ***********************************************************************/
3093 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3094 struct tdb_validation_status *state)
3096 struct cache_entry *centry;
3098 centry = SMB_XMALLOC_P(struct cache_entry);
3099 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3100 if (!centry->data) {
3101 SAFE_FREE(centry);
3102 return NULL;
3104 centry->len = data.dsize;
3105 centry->ofs = 0;
3107 if (centry->len < 8) {
3108 /* huh? corrupt cache? */
3109 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3110 centry_free(centry);
3111 state->bad_entry = true;
3112 state->success = false;
3113 return NULL;
3116 centry->status = NT_STATUS(centry_uint32(centry));
3117 centry->sequence_number = centry_uint32(centry);
3118 return centry;
3121 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3122 struct tdb_validation_status *state)
3124 if (dbuf.dsize != 8) {
3125 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3126 keystr, (unsigned int)dbuf.dsize ));
3127 state->bad_entry = true;
3128 return 1;
3130 return 0;
3133 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3134 struct tdb_validation_status *state)
3136 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3137 if (!centry) {
3138 return 1;
3141 (void)centry_uint32(centry);
3142 if (NT_STATUS_IS_OK(centry->status)) {
3143 DOM_SID sid;
3144 (void)centry_sid(centry, mem_ctx, &sid);
3147 centry_free(centry);
3149 if (!(state->success)) {
3150 return 1;
3152 DEBUG(10,("validate_ns: %s ok\n", keystr));
3153 return 0;
3156 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3157 struct tdb_validation_status *state)
3159 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3160 if (!centry) {
3161 return 1;
3164 if (NT_STATUS_IS_OK(centry->status)) {
3165 (void)centry_uint32(centry);
3166 (void)centry_string(centry, mem_ctx);
3167 (void)centry_string(centry, mem_ctx);
3170 centry_free(centry);
3172 if (!(state->success)) {
3173 return 1;
3175 DEBUG(10,("validate_sn: %s ok\n", keystr));
3176 return 0;
3179 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3180 struct tdb_validation_status *state)
3182 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3183 DOM_SID sid;
3185 if (!centry) {
3186 return 1;
3189 (void)centry_string(centry, mem_ctx);
3190 (void)centry_string(centry, mem_ctx);
3191 (void)centry_string(centry, mem_ctx);
3192 (void)centry_string(centry, mem_ctx);
3193 (void)centry_uint32(centry);
3194 (void)centry_sid(centry, mem_ctx, &sid);
3195 (void)centry_sid(centry, mem_ctx, &sid);
3197 centry_free(centry);
3199 if (!(state->success)) {
3200 return 1;
3202 DEBUG(10,("validate_u: %s ok\n", keystr));
3203 return 0;
3206 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3207 struct tdb_validation_status *state)
3209 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3211 if (!centry) {
3212 return 1;
3215 (void)centry_nttime(centry);
3216 (void)centry_nttime(centry);
3217 (void)centry_uint16(centry);
3219 centry_free(centry);
3221 if (!(state->success)) {
3222 return 1;
3224 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3225 return 0;
3228 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3229 struct tdb_validation_status *state)
3231 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3233 if (!centry) {
3234 return 1;
3237 (void)centry_uint16(centry);
3238 (void)centry_uint16(centry);
3239 (void)centry_uint32(centry);
3240 (void)centry_nttime(centry);
3241 (void)centry_nttime(centry);
3243 centry_free(centry);
3245 if (!(state->success)) {
3246 return 1;
3248 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3249 return 0;
3252 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3253 struct tdb_validation_status *state)
3255 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3257 if (!centry) {
3258 return 1;
3261 (void)centry_time(centry);
3262 (void)centry_hash16(centry, mem_ctx);
3264 /* We only have 17 bytes more data in the salted cred case. */
3265 if (centry->len - centry->ofs == 17) {
3266 (void)centry_hash16(centry, mem_ctx);
3269 centry_free(centry);
3271 if (!(state->success)) {
3272 return 1;
3274 DEBUG(10,("validate_cred: %s ok\n", keystr));
3275 return 0;
3278 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3279 struct tdb_validation_status *state)
3281 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3282 int32 num_entries, i;
3284 if (!centry) {
3285 return 1;
3288 num_entries = (int32)centry_uint32(centry);
3290 for (i=0; i< num_entries; i++) {
3291 DOM_SID sid;
3292 (void)centry_string(centry, mem_ctx);
3293 (void)centry_string(centry, mem_ctx);
3294 (void)centry_string(centry, mem_ctx);
3295 (void)centry_string(centry, mem_ctx);
3296 (void)centry_sid(centry, mem_ctx, &sid);
3297 (void)centry_sid(centry, mem_ctx, &sid);
3300 centry_free(centry);
3302 if (!(state->success)) {
3303 return 1;
3305 DEBUG(10,("validate_ul: %s ok\n", keystr));
3306 return 0;
3309 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3310 struct tdb_validation_status *state)
3312 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3313 int32 num_entries, i;
3315 if (!centry) {
3316 return 1;
3319 num_entries = centry_uint32(centry);
3321 for (i=0; i< num_entries; i++) {
3322 (void)centry_string(centry, mem_ctx);
3323 (void)centry_string(centry, mem_ctx);
3324 (void)centry_uint32(centry);
3327 centry_free(centry);
3329 if (!(state->success)) {
3330 return 1;
3332 DEBUG(10,("validate_gl: %s ok\n", keystr));
3333 return 0;
3336 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3337 struct tdb_validation_status *state)
3339 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3340 int32 num_groups, i;
3342 if (!centry) {
3343 return 1;
3346 num_groups = centry_uint32(centry);
3348 for (i=0; i< num_groups; i++) {
3349 DOM_SID sid;
3350 centry_sid(centry, mem_ctx, &sid);
3353 centry_free(centry);
3355 if (!(state->success)) {
3356 return 1;
3358 DEBUG(10,("validate_ug: %s ok\n", keystr));
3359 return 0;
3362 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3363 struct tdb_validation_status *state)
3365 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3366 int32 num_aliases, i;
3368 if (!centry) {
3369 return 1;
3372 num_aliases = centry_uint32(centry);
3374 for (i=0; i < num_aliases; i++) {
3375 (void)centry_uint32(centry);
3378 centry_free(centry);
3380 if (!(state->success)) {
3381 return 1;
3383 DEBUG(10,("validate_ua: %s ok\n", keystr));
3384 return 0;
3387 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3388 struct tdb_validation_status *state)
3390 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3391 int32 num_names, i;
3393 if (!centry) {
3394 return 1;
3397 num_names = centry_uint32(centry);
3399 for (i=0; i< num_names; i++) {
3400 DOM_SID sid;
3401 centry_sid(centry, mem_ctx, &sid);
3402 (void)centry_string(centry, mem_ctx);
3403 (void)centry_uint32(centry);
3406 centry_free(centry);
3408 if (!(state->success)) {
3409 return 1;
3411 DEBUG(10,("validate_gm: %s ok\n", keystr));
3412 return 0;
3415 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3416 struct tdb_validation_status *state)
3418 /* Can't say anything about this other than must be nonzero. */
3419 if (dbuf.dsize == 0) {
3420 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3421 keystr));
3422 state->bad_entry = true;
3423 state->success = false;
3424 return 1;
3427 DEBUG(10,("validate_dr: %s ok\n", keystr));
3428 return 0;
3431 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3432 struct tdb_validation_status *state)
3434 /* Can't say anything about this other than must be nonzero. */
3435 if (dbuf.dsize == 0) {
3436 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3437 keystr));
3438 state->bad_entry = true;
3439 state->success = false;
3440 return 1;
3443 DEBUG(10,("validate_de: %s ok\n", keystr));
3444 return 0;
3447 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3448 TDB_DATA dbuf, struct tdb_validation_status *state)
3450 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3452 if (!centry) {
3453 return 1;
3456 (void)centry_string(centry, mem_ctx);
3457 (void)centry_string(centry, mem_ctx);
3458 (void)centry_string(centry, mem_ctx);
3459 (void)centry_uint32(centry);
3461 centry_free(centry);
3463 if (!(state->success)) {
3464 return 1;
3466 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3467 return 0;
3470 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3471 TDB_DATA dbuf,
3472 struct tdb_validation_status *state)
3474 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3476 if (!centry) {
3477 return 1;
3480 (void)centry_string( centry, mem_ctx );
3482 centry_free(centry);
3484 if (!(state->success)) {
3485 return 1;
3487 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3488 return 0;
3491 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3492 TDB_DATA dbuf,
3493 struct tdb_validation_status *state)
3495 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3497 if (!centry) {
3498 return 1;
3501 (void)centry_string( centry, mem_ctx );
3503 centry_free(centry);
3505 if (!(state->success)) {
3506 return 1;
3508 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3509 return 0;
3512 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3513 struct tdb_validation_status *state)
3515 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3516 int32 num_domains, i;
3518 if (!centry) {
3519 return 1;
3522 num_domains = centry_uint32(centry);
3524 for (i=0; i< num_domains; i++) {
3525 DOM_SID sid;
3526 (void)centry_string(centry, mem_ctx);
3527 (void)centry_string(centry, mem_ctx);
3528 (void)centry_sid(centry, mem_ctx, &sid);
3531 centry_free(centry);
3533 if (!(state->success)) {
3534 return 1;
3536 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3537 return 0;
3540 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3541 TDB_DATA dbuf,
3542 struct tdb_validation_status *state)
3544 if (dbuf.dsize == 0) {
3545 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3546 "key %s (len ==0) ?\n", keystr));
3547 state->bad_entry = true;
3548 state->success = false;
3549 return 1;
3552 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3553 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3554 return 0;
3557 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3558 struct tdb_validation_status *state)
3560 if (dbuf.dsize != 4) {
3561 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3562 keystr, (unsigned int)dbuf.dsize ));
3563 state->bad_entry = true;
3564 state->success = false;
3565 return 1;
3567 DEBUG(10,("validate_offline: %s ok\n", keystr));
3568 return 0;
3571 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3572 struct tdb_validation_status *state)
3574 if (dbuf.dsize != 4) {
3575 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3576 "key %s (len %u != 4) ?\n",
3577 keystr, (unsigned int)dbuf.dsize));
3578 state->bad_entry = true;
3579 state->success = false;
3580 return 1;
3583 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3584 return 0;
3587 /***********************************************************************
3588 A list of all possible cache tdb keys with associated validation
3589 functions.
3590 ***********************************************************************/
3592 struct key_val_struct {
3593 const char *keyname;
3594 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3595 } key_val[] = {
3596 {"SEQNUM/", validate_seqnum},
3597 {"NS/", validate_ns},
3598 {"SN/", validate_sn},
3599 {"U/", validate_u},
3600 {"LOC_POL/", validate_loc_pol},
3601 {"PWD_POL/", validate_pwd_pol},
3602 {"CRED/", validate_cred},
3603 {"UL/", validate_ul},
3604 {"GL/", validate_gl},
3605 {"UG/", validate_ug},
3606 {"UA", validate_ua},
3607 {"GM/", validate_gm},
3608 {"DR/", validate_dr},
3609 {"DE/", validate_de},
3610 {"NSS/PWINFO/", validate_pwinfo},
3611 {"TRUSTDOMS/", validate_trustdoms},
3612 {"TRUSTDOMCACHE/", validate_trustdomcache},
3613 {"NSS/NA/", validate_nss_na},
3614 {"NSS/AN/", validate_nss_an},
3615 {"WINBINDD_OFFLINE", validate_offline},
3616 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3617 {NULL, NULL}
3620 /***********************************************************************
3621 Function to look at every entry in the tdb and validate it as far as
3622 possible.
3623 ***********************************************************************/
3625 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3627 int i;
3628 unsigned int max_key_len = 1024;
3629 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3631 /* Paranoia check. */
3632 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3633 max_key_len = 1024 * 1024;
3635 if (kbuf.dsize > max_key_len) {
3636 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3637 "(%u) > (%u)\n\n",
3638 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3639 return 1;
3642 for (i = 0; key_val[i].keyname; i++) {
3643 size_t namelen = strlen(key_val[i].keyname);
3644 if (kbuf.dsize >= namelen && (
3645 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3646 TALLOC_CTX *mem_ctx;
3647 char *keystr;
3648 int ret;
3650 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3651 if (!keystr) {
3652 return 1;
3654 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3655 keystr[kbuf.dsize] = '\0';
3657 mem_ctx = talloc_init("validate_ctx");
3658 if (!mem_ctx) {
3659 SAFE_FREE(keystr);
3660 return 1;
3663 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3664 v_state);
3666 SAFE_FREE(keystr);
3667 talloc_destroy(mem_ctx);
3668 return ret;
3672 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3673 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3674 DEBUG(0,("data :\n"));
3675 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3676 v_state->unknown_key = true;
3677 v_state->success = false;
3678 return 1; /* terminate. */
3681 static void validate_panic(const char *const why)
3683 DEBUG(0,("validating cache: would panic %s\n", why ));
3684 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3685 exit(47);
3688 /***********************************************************************
3689 Try and validate every entry in the winbindd cache. If we fail here,
3690 delete the cache tdb and return non-zero.
3691 ***********************************************************************/
3693 int winbindd_validate_cache(void)
3695 int ret = -1;
3696 const char *tdb_path = lock_path("winbindd_cache.tdb");
3697 TDB_CONTEXT *tdb = NULL;
3699 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3700 smb_panic_fn = validate_panic;
3703 tdb = tdb_open_log(tdb_path,
3704 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3705 ( lp_winbind_offline_logon()
3706 ? TDB_DEFAULT
3707 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3708 O_RDWR|O_CREAT,
3709 0600);
3710 if (!tdb) {
3711 DEBUG(0, ("winbindd_validate_cache: "
3712 "error opening/initializing tdb\n"));
3713 goto done;
3715 tdb_close(tdb);
3717 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3719 if (ret != 0) {
3720 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3721 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3722 unlink(tdb_path);
3725 done:
3726 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3727 smb_panic_fn = smb_panic;
3728 return ret;
3731 /***********************************************************************
3732 Try and validate every entry in the winbindd cache.
3733 ***********************************************************************/
3735 int winbindd_validate_cache_nobackup(void)
3737 int ret = -1;
3738 const char *tdb_path = lock_path("winbindd_cache.tdb");
3740 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3741 smb_panic_fn = validate_panic;
3744 if (wcache == NULL || wcache->tdb == NULL) {
3745 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3746 } else {
3747 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3750 if (ret != 0) {
3751 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3752 "successful.\n"));
3755 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3756 "function\n"));
3757 smb_panic_fn = smb_panic;
3758 return ret;
3761 bool winbindd_cache_validate_and_initialize(void)
3763 close_winbindd_cache();
3765 if (lp_winbind_offline_logon()) {
3766 if (winbindd_validate_cache() < 0) {
3767 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3768 "could be restored.\n"));
3772 return initialize_winbindd_cache();
3775 /*********************************************************************
3776 ********************************************************************/
3778 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3779 struct winbindd_tdc_domain **domains,
3780 size_t *num_domains )
3782 struct winbindd_tdc_domain *list = NULL;
3783 size_t idx;
3784 int i;
3785 bool set_only = false;
3787 /* don't allow duplicates */
3789 idx = *num_domains;
3790 list = *domains;
3792 for ( i=0; i< (*num_domains); i++ ) {
3793 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3794 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3795 new_dom->name));
3796 idx = i;
3797 set_only = true;
3799 break;
3803 if ( !set_only ) {
3804 if ( !*domains ) {
3805 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3806 idx = 0;
3807 } else {
3808 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3809 struct winbindd_tdc_domain,
3810 (*num_domains)+1);
3811 idx = *num_domains;
3814 ZERO_STRUCT( list[idx] );
3817 if ( !list )
3818 return false;
3820 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3821 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3823 if ( !is_null_sid( &new_dom->sid ) ) {
3824 sid_copy( &list[idx].sid, &new_dom->sid );
3825 } else {
3826 sid_copy(&list[idx].sid, &global_sid_NULL);
3829 if ( new_dom->domain_flags != 0x0 )
3830 list[idx].trust_flags = new_dom->domain_flags;
3832 if ( new_dom->domain_type != 0x0 )
3833 list[idx].trust_type = new_dom->domain_type;
3835 if ( new_dom->domain_trust_attribs != 0x0 )
3836 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3838 if ( !set_only ) {
3839 *domains = list;
3840 *num_domains = idx + 1;
3843 return true;
3846 /*********************************************************************
3847 ********************************************************************/
3849 static TDB_DATA make_tdc_key( const char *domain_name )
3851 char *keystr = NULL;
3852 TDB_DATA key = { NULL, 0 };
3854 if ( !domain_name ) {
3855 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3856 return key;
3860 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3861 return key;
3863 key = string_term_tdb_data(keystr);
3865 return key;
3868 /*********************************************************************
3869 ********************************************************************/
3871 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3872 size_t num_domains,
3873 unsigned char **buf )
3875 unsigned char *buffer = NULL;
3876 int len = 0;
3877 int buflen = 0;
3878 int i = 0;
3880 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3881 (int)num_domains));
3883 buflen = 0;
3885 again:
3886 len = 0;
3888 /* Store the number of array items first */
3889 len += tdb_pack( buffer+len, buflen-len, "d",
3890 num_domains );
3892 /* now pack each domain trust record */
3893 for ( i=0; i<num_domains; i++ ) {
3895 fstring tmp;
3897 if ( buflen > 0 ) {
3898 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3899 domains[i].domain_name,
3900 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3903 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3904 domains[i].domain_name,
3905 domains[i].dns_name,
3906 sid_to_fstring(tmp, &domains[i].sid),
3907 domains[i].trust_flags,
3908 domains[i].trust_attribs,
3909 domains[i].trust_type );
3912 if ( buflen < len ) {
3913 SAFE_FREE(buffer);
3914 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3915 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3916 buflen = -1;
3917 goto done;
3919 buflen = len;
3920 goto again;
3923 *buf = buffer;
3925 done:
3926 return buflen;
3929 /*********************************************************************
3930 ********************************************************************/
3932 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3933 struct winbindd_tdc_domain **domains )
3935 fstring domain_name, dns_name, sid_string;
3936 uint32 type, attribs, flags;
3937 int num_domains;
3938 int len = 0;
3939 int i;
3940 struct winbindd_tdc_domain *list = NULL;
3942 /* get the number of domains */
3943 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3944 if ( len == -1 ) {
3945 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3946 return 0;
3949 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3950 if ( !list ) {
3951 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3952 return 0;
3955 for ( i=0; i<num_domains; i++ ) {
3956 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3957 domain_name,
3958 dns_name,
3959 sid_string,
3960 &flags,
3961 &attribs,
3962 &type );
3964 if ( len == -1 ) {
3965 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3966 TALLOC_FREE( list );
3967 return 0;
3970 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3971 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3972 domain_name, dns_name, sid_string,
3973 flags, attribs, type));
3975 list[i].domain_name = talloc_strdup( list, domain_name );
3976 list[i].dns_name = talloc_strdup( list, dns_name );
3977 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3978 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3979 domain_name));
3981 list[i].trust_flags = flags;
3982 list[i].trust_attribs = attribs;
3983 list[i].trust_type = type;
3986 *domains = list;
3988 return num_domains;
3991 /*********************************************************************
3992 ********************************************************************/
3994 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3996 TDB_DATA key = make_tdc_key( lp_workgroup() );
3997 TDB_DATA data = { NULL, 0 };
3998 int ret;
4000 if ( !key.dptr )
4001 return false;
4003 /* See if we were asked to delete the cache entry */
4005 if ( !domains ) {
4006 ret = tdb_delete( wcache->tdb, key );
4007 goto done;
4010 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4012 if ( !data.dptr ) {
4013 ret = -1;
4014 goto done;
4017 ret = tdb_store( wcache->tdb, key, data, 0 );
4019 done:
4020 SAFE_FREE( data.dptr );
4021 SAFE_FREE( key.dptr );
4023 return ( ret != -1 );
4026 /*********************************************************************
4027 ********************************************************************/
4029 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4031 TDB_DATA key = make_tdc_key( lp_workgroup() );
4032 TDB_DATA data = { NULL, 0 };
4034 *domains = NULL;
4035 *num_domains = 0;
4037 if ( !key.dptr )
4038 return false;
4040 data = tdb_fetch( wcache->tdb, key );
4042 SAFE_FREE( key.dptr );
4044 if ( !data.dptr )
4045 return false;
4047 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4049 SAFE_FREE( data.dptr );
4051 if ( !*domains )
4052 return false;
4054 return true;
4057 /*********************************************************************
4058 ********************************************************************/
4060 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4062 struct winbindd_tdc_domain *dom_list = NULL;
4063 size_t num_domains = 0;
4064 bool ret = false;
4066 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4067 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4068 domain->name, domain->alt_name,
4069 sid_string_dbg(&domain->sid),
4070 domain->domain_flags,
4071 domain->domain_trust_attribs,
4072 domain->domain_type));
4074 if ( !init_wcache() ) {
4075 return false;
4078 /* fetch the list */
4080 wcache_tdc_fetch_list( &dom_list, &num_domains );
4082 /* add the new domain */
4084 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4085 goto done;
4088 /* pack the domain */
4090 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4091 goto done;
4094 /* Success */
4096 ret = true;
4097 done:
4098 TALLOC_FREE( dom_list );
4100 return ret;
4103 /*********************************************************************
4104 ********************************************************************/
4106 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4108 struct winbindd_tdc_domain *dom_list = NULL;
4109 size_t num_domains = 0;
4110 int i;
4111 struct winbindd_tdc_domain *d = NULL;
4113 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4115 if ( !init_wcache() ) {
4116 return false;
4119 /* fetch the list */
4121 wcache_tdc_fetch_list( &dom_list, &num_domains );
4123 for ( i=0; i<num_domains; i++ ) {
4124 if ( strequal(name, dom_list[i].domain_name) ||
4125 strequal(name, dom_list[i].dns_name) )
4127 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4128 name));
4130 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4131 if ( !d )
4132 break;
4134 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4135 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4136 sid_copy( &d->sid, &dom_list[i].sid );
4137 d->trust_flags = dom_list[i].trust_flags;
4138 d->trust_type = dom_list[i].trust_type;
4139 d->trust_attribs = dom_list[i].trust_attribs;
4141 break;
4145 TALLOC_FREE( dom_list );
4147 return d;
4151 /*********************************************************************
4152 ********************************************************************/
4154 void wcache_tdc_clear( void )
4156 if ( !init_wcache() )
4157 return;
4159 wcache_tdc_store_list( NULL, 0 );
4161 return;
4165 /*********************************************************************
4166 ********************************************************************/
4168 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4169 NTSTATUS status,
4170 const DOM_SID *user_sid,
4171 const char *homedir,
4172 const char *shell,
4173 const char *gecos,
4174 uint32 gid)
4176 struct cache_entry *centry;
4177 fstring tmp;
4179 if ( (centry = centry_start(domain, status)) == NULL )
4180 return;
4182 centry_put_string( centry, homedir );
4183 centry_put_string( centry, shell );
4184 centry_put_string( centry, gecos );
4185 centry_put_uint32( centry, gid );
4187 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4189 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4191 centry_free(centry);
4194 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4195 const DOM_SID *user_sid,
4196 TALLOC_CTX *ctx,
4197 ADS_STRUCT *ads, LDAPMessage *msg,
4198 char **homedir, char **shell, char **gecos,
4199 gid_t *p_gid)
4201 struct winbind_cache *cache = get_cache(domain);
4202 struct cache_entry *centry = NULL;
4203 NTSTATUS nt_status;
4204 fstring tmp;
4206 if (!cache->tdb)
4207 goto do_query;
4209 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4210 sid_to_fstring(tmp, user_sid));
4212 if (!centry)
4213 goto do_query;
4215 *homedir = centry_string( centry, ctx );
4216 *shell = centry_string( centry, ctx );
4217 *gecos = centry_string( centry, ctx );
4218 *p_gid = centry_uint32( centry );
4220 centry_free(centry);
4222 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4223 sid_string_dbg(user_sid)));
4225 return NT_STATUS_OK;
4227 do_query:
4229 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4230 homedir, shell, gecos, p_gid );
4232 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4234 if ( NT_STATUS_IS_OK(nt_status) ) {
4235 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4236 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4237 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4238 DEBUGADD(10, ("\tgid = '%u'\n", *p_gid));
4240 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4241 *homedir, *shell, *gecos, *p_gid );
4244 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4245 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4246 domain->name ));
4247 set_domain_offline( domain );
4250 return nt_status;
4254 /* the cache backend methods are exposed via this structure */
4255 struct winbindd_methods cache_methods = {
4256 true,
4257 query_user_list,
4258 enum_dom_groups,
4259 enum_local_groups,
4260 name_to_sid,
4261 sid_to_name,
4262 rids_to_names,
4263 query_user,
4264 lookup_usergroups,
4265 lookup_useraliases,
4266 lookup_groupmem,
4267 sequence_number,
4268 lockout_policy,
4269 password_policy,
4270 trusted_domains