WHATSNEW: Update release notes.
[Samba.git] / source3 / winbindd / winbindd_cache.c
blob141ceb1b5ec6b32cbbe39f5e1e93029d1248db69
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "winbindd.h"
28 #include "tdb_validate.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 #define WINBINDD_CACHE_VERSION 1
34 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
36 extern struct winbindd_methods reconnect_methods;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40 extern struct winbindd_methods builtin_passdb_methods;
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys[] = {
49 "SEQNUM/",
50 "DR/",
51 "DE/",
52 "WINBINDD_OFFLINE",
53 WINBINDD_CACHE_VERSION_KEYSTR,
54 NULL
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static bool is_non_centry_key(TDB_DATA kbuf)
63 int i;
65 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66 return false;
68 for (i = 0; non_centry_keys[i] != NULL; i++) {
69 size_t namelen = strlen(non_centry_keys[i]);
70 if (kbuf.dsize < namelen) {
71 continue;
73 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74 return true;
77 return false;
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static bool global_winbindd_offline_state;
86 struct winbind_cache {
87 TDB_CONTEXT *tdb;
90 struct cache_entry {
91 NTSTATUS status;
92 uint32 sequence_number;
93 uint8 *data;
94 uint32 len, ofs;
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache *wcache;
103 void winbindd_check_cache_size(time_t t)
105 static time_t last_check_time;
106 struct stat st;
108 if (last_check_time == (time_t)0)
109 last_check_time = t;
111 if (t - last_check_time < 60 && t - last_check_time > 0)
112 return;
114 if (wcache == NULL || wcache->tdb == NULL) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116 return;
119 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121 return;
124 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st.st_size,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
135 struct winbind_cache *ret = wcache;
137 /* We have to know what type of domain we are dealing with first. */
139 if (domain->internal) {
140 domain->backend = &builtin_passdb_methods;
141 domain->initialized = True;
143 if ( !domain->initialized ) {
144 init_dc_connection( domain );
148 OK. listen up becasue I'm only going to say this once.
149 We have the following scenarios to consider
150 (a) trusted AD domains on a Samba DC,
151 (b) trusted AD domains and we are joined to a non-kerberos domain
152 (c) trusted AD domains and we are joined to a kerberos (AD) domain
154 For (a) we can always contact the trusted domain using krb5
155 since we have the domain trust account password
157 For (b) we can only use RPC since we have no way of
158 getting a krb5 ticket in our own domain
160 For (c) we can always use krb5 since we have a kerberos trust
162 --jerry
165 if (!domain->backend) {
166 #ifdef HAVE_ADS
167 struct winbindd_domain *our_domain = domain;
169 /* find our domain first so we can figure out if we
170 are joined to a kerberized domain */
172 if ( !domain->primary )
173 our_domain = find_our_domain();
175 if ((our_domain->active_directory || IS_DC)
176 && domain->active_directory
177 && !lp_winbind_rpc_only()) {
178 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
179 domain->backend = &ads_methods;
180 } else {
181 #endif /* HAVE_ADS */
182 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
183 domain->backend = &reconnect_methods;
184 #ifdef HAVE_ADS
186 #endif /* HAVE_ADS */
189 if (ret)
190 return ret;
192 ret = SMB_XMALLOC_P(struct winbind_cache);
193 ZERO_STRUCTP(ret);
195 wcache = ret;
196 wcache_flush_cache();
198 return ret;
202 free a centry structure
204 static void centry_free(struct cache_entry *centry)
206 if (!centry)
207 return;
208 SAFE_FREE(centry->data);
209 free(centry);
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
214 if (centry->len - centry->ofs < nbytes) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes,
217 centry->len - centry->ofs));
218 return false;
220 return true;
224 pull a uint32 from a cache entry
226 static uint32 centry_uint32(struct cache_entry *centry)
228 uint32 ret;
230 if (!centry_check_bytes(centry, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret = IVAL(centry->data, centry->ofs);
234 centry->ofs += 4;
235 return ret;
239 pull a uint16 from a cache entry
241 static uint16 centry_uint16(struct cache_entry *centry)
243 uint16 ret;
244 if (!centry_check_bytes(centry, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 2;
249 return ret;
253 pull a uint8 from a cache entry
255 static uint8 centry_uint8(struct cache_entry *centry)
257 uint8 ret;
258 if (!centry_check_bytes(centry, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret = CVAL(centry->data, centry->ofs);
262 centry->ofs += 1;
263 return ret;
267 pull a NTTIME from a cache entry
269 static NTTIME centry_nttime(struct cache_entry *centry)
271 NTTIME ret;
272 if (!centry_check_bytes(centry, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret = IVAL(centry->data, centry->ofs);
276 centry->ofs += 4;
277 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278 centry->ofs += 4;
279 return ret;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry *centry)
287 return (time_t)centry_nttime(centry);
290 /* pull a string from a cache entry, using the supplied
291 talloc context
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 uint32 len;
296 char *ret;
298 len = centry_uint8(centry);
300 if (len == 0xFF) {
301 /* a deliberate NULL string */
302 return NULL;
305 if (!centry_check_bytes(centry, (size_t)len)) {
306 smb_panic_fn("centry_string");
309 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310 if (!ret) {
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (!centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret,centry->data + centry->ofs, 16);
344 centry->ofs += 16;
345 return ret;
348 /* pull a sid from a cache entry, using the supplied
349 talloc context
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
353 char *sid_string;
354 sid_string = centry_string(centry, mem_ctx);
355 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356 return false;
358 return true;
363 pull a NTSTATUS from a cache entry
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
367 NTSTATUS status;
369 status = NT_STATUS(centry_uint32(centry));
370 return status;
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain *domain)
377 bool ret;
379 if (!wcache->tdb)
380 return false;
382 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
384 if (ret)
385 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
386 domain->name ));
387 return ret;
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
392 TDB_DATA data;
393 fstring key;
394 uint32 time_diff;
396 if (!wcache->tdb) {
397 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398 return NT_STATUS_UNSUCCESSFUL;
401 fstr_sprintf( key, "SEQNUM/%s", domain->name );
403 data = tdb_fetch_bystring( wcache->tdb, key );
404 if ( !data.dptr || data.dsize!=8 ) {
405 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
406 return NT_STATUS_UNSUCCESSFUL;
409 domain->sequence_number = IVAL(data.dptr, 0);
410 domain->last_seq_check = IVAL(data.dptr, 4);
412 SAFE_FREE(data.dptr);
414 /* have we expired? */
416 time_diff = now - domain->last_seq_check;
417 if ( time_diff > lp_winbind_cache_time() ) {
418 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419 domain->name, domain->sequence_number,
420 (uint32)domain->last_seq_check));
421 return NT_STATUS_UNSUCCESSFUL;
424 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
425 domain->name, domain->sequence_number,
426 (uint32)domain->last_seq_check));
428 return NT_STATUS_OK;
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
433 TDB_DATA data;
434 fstring key_str;
435 uint8 buf[8];
437 if (!wcache->tdb) {
438 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439 return NT_STATUS_UNSUCCESSFUL;
442 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
444 SIVAL(buf, 0, domain->sequence_number);
445 SIVAL(buf, 4, domain->last_seq_check);
446 data.dptr = buf;
447 data.dsize = 8;
449 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
450 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
458 return NT_STATUS_OK;
462 refresh the domain sequence number. If force is true
463 then always refresh it, no matter how recently we fetched it
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
468 NTSTATUS status;
469 unsigned time_diff;
470 time_t t = time(NULL);
471 unsigned cache_time = lp_winbind_cache_time();
473 if ( IS_DOMAIN_OFFLINE(domain) ) {
474 return;
477 get_cache( domain );
479 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
480 /* trying to reconnect is expensive, don't do it too often */
481 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
482 cache_time *= 8;
484 #endif
486 time_diff = t - domain->last_seq_check;
488 /* see if we have to refetch the domain sequence number */
489 if (!force && (time_diff < cache_time) &&
490 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
491 NT_STATUS_IS_OK(domain->last_status)) {
492 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
493 goto done;
496 /* try to get the sequence number from the tdb cache first */
497 /* this will update the timestamp as well */
499 status = fetch_cache_seqnum( domain, t );
500 if (NT_STATUS_IS_OK(status) &&
501 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
502 NT_STATUS_IS_OK(domain->last_status)) {
503 goto done;
506 /* important! make sure that we know if this is a native
507 mode domain or not. And that we can contact it. */
509 if ( winbindd_can_contact_domain( domain ) ) {
510 status = domain->backend->sequence_number(domain,
511 &domain->sequence_number);
512 } else {
513 /* just use the current time */
514 status = NT_STATUS_OK;
515 domain->sequence_number = time(NULL);
519 /* the above call could have set our domain->backend to NULL when
520 * coming from offline to online mode, make sure to reinitialize the
521 * backend - Guenther */
522 get_cache( domain );
524 if (!NT_STATUS_IS_OK(status)) {
525 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
526 domain->sequence_number = DOM_SEQUENCE_NONE;
529 domain->last_status = status;
530 domain->last_seq_check = time(NULL);
532 /* save the new sequence number in the cache */
533 store_cache_seqnum( domain );
535 done:
536 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
537 domain->name, domain->sequence_number));
539 return;
543 decide if a cache entry has expired
545 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
547 /* If we've been told to be offline - stay in that state... */
548 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
549 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
550 keystr, domain->name ));
551 return false;
554 /* when the domain is offline return the cached entry.
555 * This deals with transient offline states... */
557 if (!domain->online) {
558 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
559 keystr, domain->name ));
560 return false;
563 /* if the server is OK and our cache entry came from when it was down then
564 the entry is invalid */
565 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
566 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
567 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
568 keystr, domain->name ));
569 return true;
572 /* if the server is down or the cache entry is not older than the
573 current sequence number then it is OK */
574 if (wcache_server_down(domain) ||
575 centry->sequence_number == domain->sequence_number) {
576 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
577 keystr, domain->name ));
578 return false;
581 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
582 keystr, domain->name ));
584 /* it's expired */
585 return true;
588 static struct cache_entry *wcache_fetch_raw(char *kstr)
590 TDB_DATA data;
591 struct cache_entry *centry;
592 TDB_DATA key;
594 key = string_tdb_data(kstr);
595 data = tdb_fetch(wcache->tdb, key);
596 if (!data.dptr) {
597 /* a cache miss */
598 return NULL;
601 centry = SMB_XMALLOC_P(struct cache_entry);
602 centry->data = (unsigned char *)data.dptr;
603 centry->len = data.dsize;
604 centry->ofs = 0;
606 if (centry->len < 8) {
607 /* huh? corrupt cache? */
608 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
609 centry_free(centry);
610 return NULL;
613 centry->status = centry_ntstatus(centry);
614 centry->sequence_number = centry_uint32(centry);
616 return centry;
620 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
621 number and return status
623 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
624 struct winbindd_domain *domain,
625 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
626 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
627 struct winbindd_domain *domain,
628 const char *format, ...)
630 va_list ap;
631 char *kstr;
632 struct cache_entry *centry;
634 if (!winbindd_use_cache()) {
635 return NULL;
638 refresh_sequence_number(domain, false);
640 va_start(ap, format);
641 smb_xvasprintf(&kstr, format, ap);
642 va_end(ap);
644 centry = wcache_fetch_raw(kstr);
645 if (centry == NULL) {
646 free(kstr);
647 return NULL;
650 if (centry_expired(domain, kstr, centry)) {
652 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
653 kstr, domain->name ));
655 centry_free(centry);
656 free(kstr);
657 return NULL;
660 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
661 kstr, domain->name ));
663 free(kstr);
664 return centry;
667 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
668 static void wcache_delete(const char *format, ...)
670 va_list ap;
671 char *kstr;
672 TDB_DATA key;
674 va_start(ap, format);
675 smb_xvasprintf(&kstr, format, ap);
676 va_end(ap);
678 key = string_tdb_data(kstr);
680 tdb_delete(wcache->tdb, key);
681 free(kstr);
685 make sure we have at least len bytes available in a centry
687 static void centry_expand(struct cache_entry *centry, uint32 len)
689 if (centry->len - centry->ofs >= len)
690 return;
691 centry->len *= 2;
692 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
693 centry->len);
694 if (!centry->data) {
695 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
696 smb_panic_fn("out of memory in centry_expand");
701 push a uint32 into a centry
703 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
705 centry_expand(centry, 4);
706 SIVAL(centry->data, centry->ofs, v);
707 centry->ofs += 4;
711 push a uint16 into a centry
713 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
715 centry_expand(centry, 2);
716 SIVAL(centry->data, centry->ofs, v);
717 centry->ofs += 2;
721 push a uint8 into a centry
723 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
725 centry_expand(centry, 1);
726 SCVAL(centry->data, centry->ofs, v);
727 centry->ofs += 1;
731 push a string into a centry
733 static void centry_put_string(struct cache_entry *centry, const char *s)
735 int len;
737 if (!s) {
738 /* null strings are marked as len 0xFFFF */
739 centry_put_uint8(centry, 0xFF);
740 return;
743 len = strlen(s);
744 /* can't handle more than 254 char strings. Truncating is probably best */
745 if (len > 254) {
746 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
747 len = 254;
749 centry_put_uint8(centry, len);
750 centry_expand(centry, len);
751 memcpy(centry->data + centry->ofs, s, len);
752 centry->ofs += len;
756 push a 16 byte hash into a centry - treat as 16 byte string.
758 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
760 centry_put_uint8(centry, 16);
761 centry_expand(centry, 16);
762 memcpy(centry->data + centry->ofs, val, 16);
763 centry->ofs += 16;
766 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
768 fstring sid_string;
769 centry_put_string(centry, sid_to_fstring(sid_string, sid));
774 put NTSTATUS into a centry
776 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
778 uint32 status_value = NT_STATUS_V(status);
779 centry_put_uint32(centry, status_value);
784 push a NTTIME into a centry
786 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
788 centry_expand(centry, 8);
789 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
790 centry->ofs += 4;
791 SIVAL(centry->data, centry->ofs, nt >> 32);
792 centry->ofs += 4;
796 push a time_t into a centry - use a 64 bit size.
797 NTTIME here is being used as a convenient 64-bit size.
799 static void centry_put_time(struct cache_entry *centry, time_t t)
801 NTTIME nt = (NTTIME)t;
802 centry_put_nttime(centry, nt);
806 start a centry for output. When finished, call centry_end()
808 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
810 struct cache_entry *centry;
812 if (!wcache->tdb)
813 return NULL;
815 centry = SMB_XMALLOC_P(struct cache_entry);
817 centry->len = 8192; /* reasonable default */
818 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
819 centry->ofs = 0;
820 centry->sequence_number = domain->sequence_number;
821 centry_put_ntstatus(centry, status);
822 centry_put_uint32(centry, centry->sequence_number);
823 return centry;
827 finish a centry and write it to the tdb
829 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
830 static void centry_end(struct cache_entry *centry, const char *format, ...)
832 va_list ap;
833 char *kstr;
834 TDB_DATA key, data;
836 if (!winbindd_use_cache()) {
837 return;
840 va_start(ap, format);
841 smb_xvasprintf(&kstr, format, ap);
842 va_end(ap);
844 key = string_tdb_data(kstr);
845 data.dptr = centry->data;
846 data.dsize = centry->ofs;
848 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
849 free(kstr);
852 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
853 NTSTATUS status, const char *domain_name,
854 const char *name, const DOM_SID *sid,
855 enum lsa_SidType type)
857 struct cache_entry *centry;
858 fstring uname;
860 centry = centry_start(domain, status);
861 if (!centry)
862 return;
863 centry_put_uint32(centry, type);
864 centry_put_sid(centry, sid);
865 fstrcpy(uname, name);
866 strupper_m(uname);
867 centry_end(centry, "NS/%s/%s", domain_name, uname);
868 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
869 uname, sid_string_dbg(sid), nt_errstr(status)));
870 centry_free(centry);
873 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
874 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
876 struct cache_entry *centry;
877 fstring sid_string;
879 centry = centry_start(domain, status);
880 if (!centry)
881 return;
883 if (NT_STATUS_IS_OK(status)) {
884 centry_put_uint32(centry, type);
885 centry_put_string(centry, domain_name);
886 centry_put_string(centry, name);
889 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
890 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
891 name, nt_errstr(status)));
892 centry_free(centry);
896 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
898 struct cache_entry *centry;
899 fstring sid_string;
901 if (is_null_sid(&info->user_sid)) {
902 return;
905 centry = centry_start(domain, status);
906 if (!centry)
907 return;
908 centry_put_string(centry, info->acct_name);
909 centry_put_string(centry, info->full_name);
910 centry_put_string(centry, info->homedir);
911 centry_put_string(centry, info->shell);
912 centry_put_uint32(centry, info->primary_gid);
913 centry_put_sid(centry, &info->user_sid);
914 centry_put_sid(centry, &info->group_sid);
915 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
916 &info->user_sid));
917 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
918 centry_free(centry);
921 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
922 NTSTATUS status,
923 struct samr_DomInfo12 *lockout_policy)
925 struct cache_entry *centry;
927 centry = centry_start(domain, status);
928 if (!centry)
929 return;
931 centry_put_nttime(centry, lockout_policy->lockout_duration);
932 centry_put_nttime(centry, lockout_policy->lockout_window);
933 centry_put_uint16(centry, lockout_policy->lockout_threshold);
935 centry_end(centry, "LOC_POL/%s", domain->name);
937 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
939 centry_free(centry);
944 static void wcache_save_password_policy(struct winbindd_domain *domain,
945 NTSTATUS status,
946 struct samr_DomInfo1 *policy)
948 struct cache_entry *centry;
950 centry = centry_start(domain, status);
951 if (!centry)
952 return;
954 centry_put_uint16(centry, policy->min_password_length);
955 centry_put_uint16(centry, policy->password_history_length);
956 centry_put_uint32(centry, policy->password_properties);
957 centry_put_nttime(centry, policy->max_password_age);
958 centry_put_nttime(centry, policy->min_password_age);
960 centry_end(centry, "PWD_POL/%s", domain->name);
962 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
964 centry_free(centry);
967 /***************************************************************************
968 ***************************************************************************/
970 static void wcache_save_username_alias(struct winbindd_domain *domain,
971 NTSTATUS status,
972 const char *name, const char *alias)
974 struct cache_entry *centry;
975 fstring uname;
977 if ( (centry = centry_start(domain, status)) == NULL )
978 return;
980 centry_put_string( centry, alias );
982 fstrcpy(uname, name);
983 strupper_m(uname);
984 centry_end(centry, "NSS/NA/%s", uname);
986 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
988 centry_free(centry);
991 static void wcache_save_alias_username(struct winbindd_domain *domain,
992 NTSTATUS status,
993 const char *alias, const char *name)
995 struct cache_entry *centry;
996 fstring uname;
998 if ( (centry = centry_start(domain, status)) == NULL )
999 return;
1001 centry_put_string( centry, name );
1003 fstrcpy(uname, alias);
1004 strupper_m(uname);
1005 centry_end(centry, "NSS/AN/%s", uname);
1007 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1009 centry_free(centry);
1012 /***************************************************************************
1013 ***************************************************************************/
1015 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1016 struct winbindd_domain *domain,
1017 const char *name, char **alias )
1019 struct winbind_cache *cache = get_cache(domain);
1020 struct cache_entry *centry = NULL;
1021 NTSTATUS status;
1022 char *upper_name;
1024 if ( domain->internal )
1025 return NT_STATUS_NOT_SUPPORTED;
1027 if (!cache->tdb)
1028 goto do_query;
1030 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1031 return NT_STATUS_NO_MEMORY;
1032 strupper_m(upper_name);
1034 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1036 SAFE_FREE( upper_name );
1038 if (!centry)
1039 goto do_query;
1041 status = centry->status;
1043 if (!NT_STATUS_IS_OK(status)) {
1044 centry_free(centry);
1045 return status;
1048 *alias = centry_string( centry, mem_ctx );
1050 centry_free(centry);
1052 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1053 name, *alias ? *alias : "(none)"));
1055 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1057 do_query:
1059 /* If its not in cache and we are offline, then fail */
1061 if ( get_global_winbindd_state_offline() || !domain->online ) {
1062 DEBUG(8,("resolve_username_to_alias: rejecting query "
1063 "in offline mode\n"));
1064 return NT_STATUS_NOT_FOUND;
1067 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1069 if ( NT_STATUS_IS_OK( status ) ) {
1070 wcache_save_username_alias(domain, status, name, *alias);
1073 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1074 wcache_save_username_alias(domain, status, name, "(NULL)");
1077 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1078 nt_errstr(status)));
1080 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1081 set_domain_offline( domain );
1084 return status;
1087 /***************************************************************************
1088 ***************************************************************************/
1090 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1091 struct winbindd_domain *domain,
1092 const char *alias, char **name )
1094 struct winbind_cache *cache = get_cache(domain);
1095 struct cache_entry *centry = NULL;
1096 NTSTATUS status;
1097 char *upper_name;
1099 if ( domain->internal )
1100 return NT_STATUS_NOT_SUPPORTED;
1102 if (!cache->tdb)
1103 goto do_query;
1105 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1106 return NT_STATUS_NO_MEMORY;
1107 strupper_m(upper_name);
1109 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1111 SAFE_FREE( upper_name );
1113 if (!centry)
1114 goto do_query;
1116 status = centry->status;
1118 if (!NT_STATUS_IS_OK(status)) {
1119 centry_free(centry);
1120 return status;
1123 *name = centry_string( centry, mem_ctx );
1125 centry_free(centry);
1127 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1128 alias, *name ? *name : "(none)"));
1130 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1132 do_query:
1134 /* If its not in cache and we are offline, then fail */
1136 if ( get_global_winbindd_state_offline() || !domain->online ) {
1137 DEBUG(8,("resolve_alias_to_username: rejecting query "
1138 "in offline mode\n"));
1139 return NT_STATUS_NOT_FOUND;
1142 /* an alias cannot contain a domain prefix or '@' */
1144 if (strchr(alias, '\\') || strchr(alias, '@')) {
1145 DEBUG(10,("resolve_alias_to_username: skipping fully "
1146 "qualified name %s\n", alias));
1147 return NT_STATUS_OBJECT_NAME_INVALID;
1150 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1152 if ( NT_STATUS_IS_OK( status ) ) {
1153 wcache_save_alias_username( domain, status, alias, *name );
1156 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1157 wcache_save_alias_username(domain, status, alias, "(NULL)");
1160 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1161 nt_errstr(status)));
1163 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1164 set_domain_offline( domain );
1167 return status;
1170 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1172 struct winbind_cache *cache = get_cache(domain);
1173 TDB_DATA data;
1174 fstring key_str, tmp;
1175 uint32 rid;
1177 if (!cache->tdb) {
1178 return NT_STATUS_INTERNAL_DB_ERROR;
1181 if (is_null_sid(sid)) {
1182 return NT_STATUS_INVALID_SID;
1185 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1186 return NT_STATUS_INVALID_SID;
1189 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1191 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1192 if (!data.dptr) {
1193 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1196 SAFE_FREE(data.dptr);
1197 return NT_STATUS_OK;
1200 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1201 as new salted ones. */
1203 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1204 TALLOC_CTX *mem_ctx,
1205 const DOM_SID *sid,
1206 const uint8 **cached_nt_pass,
1207 const uint8 **cached_salt)
1209 struct winbind_cache *cache = get_cache(domain);
1210 struct cache_entry *centry = NULL;
1211 NTSTATUS status;
1212 time_t t;
1213 uint32 rid;
1214 fstring tmp;
1216 if (!cache->tdb) {
1217 return NT_STATUS_INTERNAL_DB_ERROR;
1220 if (is_null_sid(sid)) {
1221 return NT_STATUS_INVALID_SID;
1224 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1225 return NT_STATUS_INVALID_SID;
1228 /* Try and get a salted cred first. If we can't
1229 fall back to an unsalted cred. */
1231 centry = wcache_fetch(cache, domain, "CRED/%s",
1232 sid_to_fstring(tmp, sid));
1233 if (!centry) {
1234 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1235 sid_string_dbg(sid)));
1236 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1239 t = centry_time(centry);
1241 /* In the salted case this isn't actually the nt_hash itself,
1242 but the MD5 of the salt + nt_hash. Let the caller
1243 sort this out. It can tell as we only return the cached_salt
1244 if we are returning a salted cred. */
1246 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1247 if (*cached_nt_pass == NULL) {
1248 fstring sidstr;
1250 sid_to_fstring(sidstr, sid);
1252 /* Bad (old) cred cache. Delete and pretend we
1253 don't have it. */
1254 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1255 sidstr));
1256 wcache_delete("CRED/%s", sidstr);
1257 centry_free(centry);
1258 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1261 /* We only have 17 bytes more data in the salted cred case. */
1262 if (centry->len - centry->ofs == 17) {
1263 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1264 } else {
1265 *cached_salt = NULL;
1268 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1269 if (*cached_salt) {
1270 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1273 status = centry->status;
1275 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1276 sid_string_dbg(sid), nt_errstr(status) ));
1278 centry_free(centry);
1279 return status;
1282 /* Store creds for a SID - only writes out new salted ones. */
1284 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1285 TALLOC_CTX *mem_ctx,
1286 const DOM_SID *sid,
1287 const uint8 nt_pass[NT_HASH_LEN])
1289 struct cache_entry *centry;
1290 fstring sid_string;
1291 uint32 rid;
1292 uint8 cred_salt[NT_HASH_LEN];
1293 uint8 salted_hash[NT_HASH_LEN];
1295 if (is_null_sid(sid)) {
1296 return NT_STATUS_INVALID_SID;
1299 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1300 return NT_STATUS_INVALID_SID;
1303 centry = centry_start(domain, NT_STATUS_OK);
1304 if (!centry) {
1305 return NT_STATUS_INTERNAL_DB_ERROR;
1308 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1310 centry_put_time(centry, time(NULL));
1312 /* Create a salt and then salt the hash. */
1313 generate_random_buffer(cred_salt, NT_HASH_LEN);
1314 E_md5hash(cred_salt, nt_pass, salted_hash);
1316 centry_put_hash16(centry, salted_hash);
1317 centry_put_hash16(centry, cred_salt);
1318 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1320 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1322 centry_free(centry);
1324 return NT_STATUS_OK;
1328 /* Query display info. This is the basic user list fn */
1329 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1330 TALLOC_CTX *mem_ctx,
1331 uint32 *num_entries,
1332 WINBIND_USERINFO **info)
1334 struct winbind_cache *cache = get_cache(domain);
1335 struct cache_entry *centry = NULL;
1336 NTSTATUS status;
1337 unsigned int i, retry;
1339 if (!cache->tdb)
1340 goto do_query;
1342 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1343 if (!centry)
1344 goto do_query;
1346 *num_entries = centry_uint32(centry);
1348 if (*num_entries == 0)
1349 goto do_cached;
1351 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1352 if (! (*info)) {
1353 smb_panic_fn("query_user_list out of memory");
1355 for (i=0; i<(*num_entries); i++) {
1356 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1357 (*info)[i].full_name = centry_string(centry, mem_ctx);
1358 (*info)[i].homedir = centry_string(centry, mem_ctx);
1359 (*info)[i].shell = centry_string(centry, mem_ctx);
1360 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1361 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1364 do_cached:
1365 status = centry->status;
1367 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1368 domain->name, nt_errstr(status) ));
1370 centry_free(centry);
1371 return status;
1373 do_query:
1374 *num_entries = 0;
1375 *info = NULL;
1377 /* Return status value returned by seq number check */
1379 if (!NT_STATUS_IS_OK(domain->last_status))
1380 return domain->last_status;
1382 /* Put the query_user_list() in a retry loop. There appears to be
1383 * some bug either with Windows 2000 or Samba's handling of large
1384 * rpc replies. This manifests itself as sudden disconnection
1385 * at a random point in the enumeration of a large (60k) user list.
1386 * The retry loop simply tries the operation again. )-: It's not
1387 * pretty but an acceptable workaround until we work out what the
1388 * real problem is. */
1390 retry = 0;
1391 do {
1393 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1394 domain->name ));
1396 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1397 if (!NT_STATUS_IS_OK(status)) {
1398 DEBUG(3, ("query_user_list: returned 0x%08x, "
1399 "retrying\n", NT_STATUS_V(status)));
1401 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1402 DEBUG(3, ("query_user_list: flushing "
1403 "connection cache\n"));
1404 invalidate_cm_connection(&domain->conn);
1407 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1408 (retry++ < 5));
1410 /* and save it */
1411 refresh_sequence_number(domain, false);
1412 centry = centry_start(domain, status);
1413 if (!centry)
1414 goto skip_save;
1415 centry_put_uint32(centry, *num_entries);
1416 for (i=0; i<(*num_entries); i++) {
1417 centry_put_string(centry, (*info)[i].acct_name);
1418 centry_put_string(centry, (*info)[i].full_name);
1419 centry_put_string(centry, (*info)[i].homedir);
1420 centry_put_string(centry, (*info)[i].shell);
1421 centry_put_sid(centry, &(*info)[i].user_sid);
1422 centry_put_sid(centry, &(*info)[i].group_sid);
1423 if (domain->backend && domain->backend->consistent) {
1424 /* when the backend is consistent we can pre-prime some mappings */
1425 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1426 domain->name,
1427 (*info)[i].acct_name,
1428 &(*info)[i].user_sid,
1429 SID_NAME_USER);
1430 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1431 &(*info)[i].user_sid,
1432 domain->name,
1433 (*info)[i].acct_name,
1434 SID_NAME_USER);
1435 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1438 centry_end(centry, "UL/%s", domain->name);
1439 centry_free(centry);
1441 skip_save:
1442 return status;
1445 /* list all domain groups */
1446 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1447 TALLOC_CTX *mem_ctx,
1448 uint32 *num_entries,
1449 struct acct_info **info)
1451 struct winbind_cache *cache = get_cache(domain);
1452 struct cache_entry *centry = NULL;
1453 NTSTATUS status;
1454 unsigned int i;
1456 if (!cache->tdb)
1457 goto do_query;
1459 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1460 if (!centry)
1461 goto do_query;
1463 *num_entries = centry_uint32(centry);
1465 if (*num_entries == 0)
1466 goto do_cached;
1468 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1469 if (! (*info)) {
1470 smb_panic_fn("enum_dom_groups out of memory");
1472 for (i=0; i<(*num_entries); i++) {
1473 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1474 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1475 (*info)[i].rid = centry_uint32(centry);
1478 do_cached:
1479 status = centry->status;
1481 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1482 domain->name, nt_errstr(status) ));
1484 centry_free(centry);
1485 return status;
1487 do_query:
1488 *num_entries = 0;
1489 *info = NULL;
1491 /* Return status value returned by seq number check */
1493 if (!NT_STATUS_IS_OK(domain->last_status))
1494 return domain->last_status;
1496 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1497 domain->name ));
1499 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1501 /* and save it */
1502 refresh_sequence_number(domain, false);
1503 centry = centry_start(domain, status);
1504 if (!centry)
1505 goto skip_save;
1506 centry_put_uint32(centry, *num_entries);
1507 for (i=0; i<(*num_entries); i++) {
1508 centry_put_string(centry, (*info)[i].acct_name);
1509 centry_put_string(centry, (*info)[i].acct_desc);
1510 centry_put_uint32(centry, (*info)[i].rid);
1512 centry_end(centry, "GL/%s/domain", domain->name);
1513 centry_free(centry);
1515 skip_save:
1516 return status;
1519 /* list all domain groups */
1520 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1521 TALLOC_CTX *mem_ctx,
1522 uint32 *num_entries,
1523 struct acct_info **info)
1525 struct winbind_cache *cache = get_cache(domain);
1526 struct cache_entry *centry = NULL;
1527 NTSTATUS status;
1528 unsigned int i;
1530 if (!cache->tdb)
1531 goto do_query;
1533 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1534 if (!centry)
1535 goto do_query;
1537 *num_entries = centry_uint32(centry);
1539 if (*num_entries == 0)
1540 goto do_cached;
1542 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1543 if (! (*info)) {
1544 smb_panic_fn("enum_dom_groups out of memory");
1546 for (i=0; i<(*num_entries); i++) {
1547 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1548 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1549 (*info)[i].rid = centry_uint32(centry);
1552 do_cached:
1554 /* If we are returning cached data and the domain controller
1555 is down then we don't know whether the data is up to date
1556 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1557 indicate this. */
1559 if (wcache_server_down(domain)) {
1560 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1561 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1562 } else
1563 status = centry->status;
1565 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1566 domain->name, nt_errstr(status) ));
1568 centry_free(centry);
1569 return status;
1571 do_query:
1572 *num_entries = 0;
1573 *info = NULL;
1575 /* Return status value returned by seq number check */
1577 if (!NT_STATUS_IS_OK(domain->last_status))
1578 return domain->last_status;
1580 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1581 domain->name ));
1583 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1585 /* and save it */
1586 refresh_sequence_number(domain, false);
1587 centry = centry_start(domain, status);
1588 if (!centry)
1589 goto skip_save;
1590 centry_put_uint32(centry, *num_entries);
1591 for (i=0; i<(*num_entries); i++) {
1592 centry_put_string(centry, (*info)[i].acct_name);
1593 centry_put_string(centry, (*info)[i].acct_desc);
1594 centry_put_uint32(centry, (*info)[i].rid);
1596 centry_end(centry, "GL/%s/local", domain->name);
1597 centry_free(centry);
1599 skip_save:
1600 return status;
1603 /* convert a single name to a sid in a domain */
1604 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1605 TALLOC_CTX *mem_ctx,
1606 enum winbindd_cmd orig_cmd,
1607 const char *domain_name,
1608 const char *name,
1609 DOM_SID *sid,
1610 enum lsa_SidType *type)
1612 struct winbind_cache *cache = get_cache(domain);
1613 struct cache_entry *centry = NULL;
1614 NTSTATUS status;
1615 fstring uname;
1617 if (!cache->tdb)
1618 goto do_query;
1620 fstrcpy(uname, name);
1621 strupper_m(uname);
1622 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1623 if (!centry)
1624 goto do_query;
1626 status = centry->status;
1627 if (NT_STATUS_IS_OK(status)) {
1628 *type = (enum lsa_SidType)centry_uint32(centry);
1629 centry_sid(centry, mem_ctx, sid);
1632 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1633 domain->name, nt_errstr(status) ));
1635 centry_free(centry);
1636 return status;
1638 do_query:
1639 ZERO_STRUCTP(sid);
1641 /* If the seq number check indicated that there is a problem
1642 * with this DC, then return that status... except for
1643 * access_denied. This is special because the dc may be in
1644 * "restrict anonymous = 1" mode, in which case it will deny
1645 * most unauthenticated operations, but *will* allow the LSA
1646 * name-to-sid that we try as a fallback. */
1648 if (!(NT_STATUS_IS_OK(domain->last_status)
1649 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1650 return domain->last_status;
1652 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1653 domain->name ));
1655 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1656 domain_name, name, sid, type);
1658 /* and save it */
1659 refresh_sequence_number(domain, false);
1661 if (domain->online &&
1662 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1663 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1665 /* Only save the reverse mapping if this was not a UPN */
1666 if (!strchr(name, '@')) {
1667 strupper_m(CONST_DISCARD(char *,domain_name));
1668 strlower_m(CONST_DISCARD(char *,name));
1669 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1673 return status;
1676 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1677 given */
1678 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1679 TALLOC_CTX *mem_ctx,
1680 const DOM_SID *sid,
1681 char **domain_name,
1682 char **name,
1683 enum lsa_SidType *type)
1685 struct winbind_cache *cache = get_cache(domain);
1686 struct cache_entry *centry = NULL;
1687 NTSTATUS status;
1688 fstring sid_string;
1690 if (!cache->tdb)
1691 goto do_query;
1693 centry = wcache_fetch(cache, domain, "SN/%s",
1694 sid_to_fstring(sid_string, sid));
1695 if (!centry)
1696 goto do_query;
1698 status = centry->status;
1699 if (NT_STATUS_IS_OK(status)) {
1700 *type = (enum lsa_SidType)centry_uint32(centry);
1701 *domain_name = centry_string(centry, mem_ctx);
1702 *name = centry_string(centry, mem_ctx);
1705 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1706 domain->name, nt_errstr(status) ));
1708 centry_free(centry);
1709 return status;
1711 do_query:
1712 *name = NULL;
1713 *domain_name = NULL;
1715 /* If the seq number check indicated that there is a problem
1716 * with this DC, then return that status... except for
1717 * access_denied. This is special because the dc may be in
1718 * "restrict anonymous = 1" mode, in which case it will deny
1719 * most unauthenticated operations, but *will* allow the LSA
1720 * sid-to-name that we try as a fallback. */
1722 if (!(NT_STATUS_IS_OK(domain->last_status)
1723 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1724 return domain->last_status;
1726 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1727 domain->name ));
1729 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1731 /* and save it */
1732 refresh_sequence_number(domain, false);
1733 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1735 /* We can't save the name to sid mapping here, as with sid history a
1736 * later name2sid would give the wrong sid. */
1738 return status;
1741 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1742 TALLOC_CTX *mem_ctx,
1743 const DOM_SID *domain_sid,
1744 uint32 *rids,
1745 size_t num_rids,
1746 char **domain_name,
1747 char ***names,
1748 enum lsa_SidType **types)
1750 struct winbind_cache *cache = get_cache(domain);
1751 size_t i;
1752 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1753 bool have_mapped;
1754 bool have_unmapped;
1756 *domain_name = NULL;
1757 *names = NULL;
1758 *types = NULL;
1760 if (!cache->tdb) {
1761 goto do_query;
1764 if (num_rids == 0) {
1765 return NT_STATUS_OK;
1768 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1769 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1771 if ((*names == NULL) || (*types == NULL)) {
1772 result = NT_STATUS_NO_MEMORY;
1773 goto error;
1776 have_mapped = have_unmapped = false;
1778 for (i=0; i<num_rids; i++) {
1779 DOM_SID sid;
1780 struct cache_entry *centry;
1781 fstring tmp;
1783 if (!sid_compose(&sid, domain_sid, rids[i])) {
1784 result = NT_STATUS_INTERNAL_ERROR;
1785 goto error;
1788 centry = wcache_fetch(cache, domain, "SN/%s",
1789 sid_to_fstring(tmp, &sid));
1790 if (!centry) {
1791 goto do_query;
1794 (*types)[i] = SID_NAME_UNKNOWN;
1795 (*names)[i] = talloc_strdup(*names, "");
1797 if (NT_STATUS_IS_OK(centry->status)) {
1798 char *dom;
1799 have_mapped = true;
1800 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1802 dom = centry_string(centry, mem_ctx);
1803 if (*domain_name == NULL) {
1804 *domain_name = dom;
1805 } else {
1806 talloc_free(dom);
1809 (*names)[i] = centry_string(centry, *names);
1811 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1812 have_unmapped = true;
1814 } else {
1815 /* something's definitely wrong */
1816 result = centry->status;
1817 goto error;
1820 centry_free(centry);
1823 if (!have_mapped) {
1824 return NT_STATUS_NONE_MAPPED;
1826 if (!have_unmapped) {
1827 return NT_STATUS_OK;
1829 return STATUS_SOME_UNMAPPED;
1831 do_query:
1833 TALLOC_FREE(*names);
1834 TALLOC_FREE(*types);
1836 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1837 rids, num_rids, domain_name,
1838 names, types);
1841 None of the queried rids has been found so save all negative entries
1843 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1844 for (i = 0; i < num_rids; i++) {
1845 DOM_SID sid;
1846 const char *name = "";
1847 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1848 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1850 if (!sid_compose(&sid, domain_sid, rids[i])) {
1851 return NT_STATUS_INTERNAL_ERROR;
1854 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1855 name, type);
1858 return result;
1862 Some or all of the queried rids have been found.
1864 if (!NT_STATUS_IS_OK(result) &&
1865 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1866 return result;
1869 refresh_sequence_number(domain, false);
1871 for (i=0; i<num_rids; i++) {
1872 DOM_SID sid;
1873 NTSTATUS status;
1875 if (!sid_compose(&sid, domain_sid, rids[i])) {
1876 result = NT_STATUS_INTERNAL_ERROR;
1877 goto error;
1880 status = (*types)[i] == SID_NAME_UNKNOWN ?
1881 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1883 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1884 (*names)[i], (*types)[i]);
1887 return result;
1889 error:
1891 TALLOC_FREE(*names);
1892 TALLOC_FREE(*types);
1893 return result;
1896 /* Lookup user information from a rid */
1897 static NTSTATUS query_user(struct winbindd_domain *domain,
1898 TALLOC_CTX *mem_ctx,
1899 const DOM_SID *user_sid,
1900 WINBIND_USERINFO *info)
1902 struct winbind_cache *cache = get_cache(domain);
1903 struct cache_entry *centry = NULL;
1904 NTSTATUS status;
1905 fstring tmp;
1907 if (!cache->tdb)
1908 goto do_query;
1910 centry = wcache_fetch(cache, domain, "U/%s",
1911 sid_to_fstring(tmp, user_sid));
1913 /* If we have an access denied cache entry and a cached info3 in the
1914 samlogon cache then do a query. This will force the rpc back end
1915 to return the info3 data. */
1917 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1918 netsamlogon_cache_have(user_sid)) {
1919 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1920 domain->last_status = NT_STATUS_OK;
1921 centry_free(centry);
1922 goto do_query;
1925 if (!centry)
1926 goto do_query;
1928 /* if status is not ok then this is a negative hit
1929 and the rest of the data doesn't matter */
1930 status = centry->status;
1931 if (NT_STATUS_IS_OK(status)) {
1932 info->acct_name = centry_string(centry, mem_ctx);
1933 info->full_name = centry_string(centry, mem_ctx);
1934 info->homedir = centry_string(centry, mem_ctx);
1935 info->shell = centry_string(centry, mem_ctx);
1936 info->primary_gid = centry_uint32(centry);
1937 centry_sid(centry, mem_ctx, &info->user_sid);
1938 centry_sid(centry, mem_ctx, &info->group_sid);
1941 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1942 domain->name, nt_errstr(status) ));
1944 centry_free(centry);
1945 return status;
1947 do_query:
1948 ZERO_STRUCTP(info);
1950 /* Return status value returned by seq number check */
1952 if (!NT_STATUS_IS_OK(domain->last_status))
1953 return domain->last_status;
1955 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1956 domain->name ));
1958 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1960 /* and save it */
1961 refresh_sequence_number(domain, false);
1962 wcache_save_user(domain, status, info);
1964 return status;
1968 /* Lookup groups a user is a member of. */
1969 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1970 TALLOC_CTX *mem_ctx,
1971 const DOM_SID *user_sid,
1972 uint32 *num_groups, DOM_SID **user_gids)
1974 struct winbind_cache *cache = get_cache(domain);
1975 struct cache_entry *centry = NULL;
1976 NTSTATUS status;
1977 unsigned int i;
1978 fstring sid_string;
1980 if (!cache->tdb)
1981 goto do_query;
1983 centry = wcache_fetch(cache, domain, "UG/%s",
1984 sid_to_fstring(sid_string, user_sid));
1986 /* If we have an access denied cache entry and a cached info3 in the
1987 samlogon cache then do a query. This will force the rpc back end
1988 to return the info3 data. */
1990 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1991 netsamlogon_cache_have(user_sid)) {
1992 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1993 domain->last_status = NT_STATUS_OK;
1994 centry_free(centry);
1995 goto do_query;
1998 if (!centry)
1999 goto do_query;
2001 *num_groups = centry_uint32(centry);
2003 if (*num_groups == 0)
2004 goto do_cached;
2006 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2007 if (! (*user_gids)) {
2008 smb_panic_fn("lookup_usergroups out of memory");
2010 for (i=0; i<(*num_groups); i++) {
2011 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
2014 do_cached:
2015 status = centry->status;
2017 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2018 domain->name, nt_errstr(status) ));
2020 centry_free(centry);
2021 return status;
2023 do_query:
2024 (*num_groups) = 0;
2025 (*user_gids) = NULL;
2027 /* Return status value returned by seq number check */
2029 if (!NT_STATUS_IS_OK(domain->last_status))
2030 return domain->last_status;
2032 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2033 domain->name ));
2035 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2037 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2038 goto skip_save;
2040 /* and save it */
2041 refresh_sequence_number(domain, false);
2042 centry = centry_start(domain, status);
2043 if (!centry)
2044 goto skip_save;
2046 centry_put_uint32(centry, *num_groups);
2047 for (i=0; i<(*num_groups); i++) {
2048 centry_put_sid(centry, &(*user_gids)[i]);
2051 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2052 centry_free(centry);
2054 skip_save:
2055 return status;
2058 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2059 TALLOC_CTX *mem_ctx,
2060 uint32 num_sids, const DOM_SID *sids,
2061 uint32 *num_aliases, uint32 **alias_rids)
2063 struct winbind_cache *cache = get_cache(domain);
2064 struct cache_entry *centry = NULL;
2065 NTSTATUS status;
2066 char *sidlist = talloc_strdup(mem_ctx, "");
2067 int i;
2069 if (!cache->tdb)
2070 goto do_query;
2072 if (num_sids == 0) {
2073 *num_aliases = 0;
2074 *alias_rids = NULL;
2075 return NT_STATUS_OK;
2078 /* We need to cache indexed by the whole list of SIDs, the aliases
2079 * resulting might come from any of the SIDs. */
2081 for (i=0; i<num_sids; i++) {
2082 fstring tmp;
2083 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2084 sid_to_fstring(tmp, &sids[i]));
2085 if (sidlist == NULL)
2086 return NT_STATUS_NO_MEMORY;
2089 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2091 if (!centry)
2092 goto do_query;
2094 *num_aliases = centry_uint32(centry);
2095 *alias_rids = NULL;
2097 if (*num_aliases) {
2098 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2100 if ((*alias_rids) == NULL) {
2101 centry_free(centry);
2102 return NT_STATUS_NO_MEMORY;
2104 } else {
2105 (*alias_rids) = NULL;
2108 for (i=0; i<(*num_aliases); i++)
2109 (*alias_rids)[i] = centry_uint32(centry);
2111 status = centry->status;
2113 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2114 "status %s\n", domain->name, nt_errstr(status)));
2116 centry_free(centry);
2117 return status;
2119 do_query:
2120 (*num_aliases) = 0;
2121 (*alias_rids) = NULL;
2123 if (!NT_STATUS_IS_OK(domain->last_status))
2124 return domain->last_status;
2126 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2127 "for domain %s\n", domain->name ));
2129 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2130 num_sids, sids,
2131 num_aliases, alias_rids);
2133 /* and save it */
2134 refresh_sequence_number(domain, false);
2135 centry = centry_start(domain, status);
2136 if (!centry)
2137 goto skip_save;
2138 centry_put_uint32(centry, *num_aliases);
2139 for (i=0; i<(*num_aliases); i++)
2140 centry_put_uint32(centry, (*alias_rids)[i]);
2141 centry_end(centry, "UA%s", sidlist);
2142 centry_free(centry);
2144 skip_save:
2145 return status;
2149 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2150 TALLOC_CTX *mem_ctx,
2151 const DOM_SID *group_sid, uint32 *num_names,
2152 DOM_SID **sid_mem, char ***names,
2153 uint32 **name_types)
2155 struct winbind_cache *cache = get_cache(domain);
2156 struct cache_entry *centry = NULL;
2157 NTSTATUS status;
2158 unsigned int i;
2159 fstring sid_string;
2161 if (!cache->tdb)
2162 goto do_query;
2164 centry = wcache_fetch(cache, domain, "GM/%s",
2165 sid_to_fstring(sid_string, group_sid));
2166 if (!centry)
2167 goto do_query;
2169 *num_names = centry_uint32(centry);
2171 if (*num_names == 0)
2172 goto do_cached;
2174 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2175 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2176 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2178 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2179 smb_panic_fn("lookup_groupmem out of memory");
2182 for (i=0; i<(*num_names); i++) {
2183 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
2184 (*names)[i] = centry_string(centry, mem_ctx);
2185 (*name_types)[i] = centry_uint32(centry);
2188 do_cached:
2189 status = centry->status;
2191 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2192 domain->name, nt_errstr(status)));
2194 centry_free(centry);
2195 return status;
2197 do_query:
2198 (*num_names) = 0;
2199 (*sid_mem) = NULL;
2200 (*names) = NULL;
2201 (*name_types) = NULL;
2203 /* Return status value returned by seq number check */
2205 if (!NT_STATUS_IS_OK(domain->last_status))
2206 return domain->last_status;
2208 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2209 domain->name ));
2211 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2212 sid_mem, names, name_types);
2214 /* and save it */
2215 refresh_sequence_number(domain, false);
2216 centry = centry_start(domain, status);
2217 if (!centry)
2218 goto skip_save;
2219 centry_put_uint32(centry, *num_names);
2220 for (i=0; i<(*num_names); i++) {
2221 centry_put_sid(centry, &(*sid_mem)[i]);
2222 centry_put_string(centry, (*names)[i]);
2223 centry_put_uint32(centry, (*name_types)[i]);
2225 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2226 centry_free(centry);
2228 skip_save:
2229 return status;
2232 /* find the sequence number for a domain */
2233 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2235 refresh_sequence_number(domain, false);
2237 *seq = domain->sequence_number;
2239 return NT_STATUS_OK;
2242 /* enumerate trusted domains
2243 * (we need to have the list of trustdoms in the cache when we go offline) -
2244 * Guenther */
2245 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2246 TALLOC_CTX *mem_ctx,
2247 uint32 *num_domains,
2248 char ***names,
2249 char ***alt_names,
2250 DOM_SID **dom_sids)
2252 struct winbind_cache *cache = get_cache(domain);
2253 struct cache_entry *centry = NULL;
2254 NTSTATUS status;
2255 int i;
2257 if (!cache->tdb)
2258 goto do_query;
2260 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2262 if (!centry) {
2263 goto do_query;
2266 *num_domains = centry_uint32(centry);
2268 if (*num_domains) {
2269 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2270 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2271 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2273 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2274 smb_panic_fn("trusted_domains out of memory");
2276 } else {
2277 (*names) = NULL;
2278 (*alt_names) = NULL;
2279 (*dom_sids) = NULL;
2282 for (i=0; i<(*num_domains); i++) {
2283 (*names)[i] = centry_string(centry, mem_ctx);
2284 (*alt_names)[i] = centry_string(centry, mem_ctx);
2285 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2286 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2290 status = centry->status;
2292 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2293 domain->name, *num_domains, nt_errstr(status) ));
2295 centry_free(centry);
2296 return status;
2298 do_query:
2299 (*num_domains) = 0;
2300 (*dom_sids) = NULL;
2301 (*names) = NULL;
2302 (*alt_names) = NULL;
2304 /* Return status value returned by seq number check */
2306 if (!NT_STATUS_IS_OK(domain->last_status))
2307 return domain->last_status;
2309 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2310 domain->name ));
2312 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2313 names, alt_names, dom_sids);
2315 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2316 * so that the generic centry handling still applies correctly -
2317 * Guenther*/
2319 if (!NT_STATUS_IS_ERR(status)) {
2320 status = NT_STATUS_OK;
2324 #if 0 /* Disabled as we want the trust dom list to be managed by
2325 the main parent and always to make the query. --jerry */
2327 /* and save it */
2328 refresh_sequence_number(domain, false);
2330 centry = centry_start(domain, status);
2331 if (!centry)
2332 goto skip_save;
2334 centry_put_uint32(centry, *num_domains);
2336 for (i=0; i<(*num_domains); i++) {
2337 centry_put_string(centry, (*names)[i]);
2338 centry_put_string(centry, (*alt_names)[i]);
2339 centry_put_sid(centry, &(*dom_sids)[i]);
2342 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2344 centry_free(centry);
2346 skip_save:
2347 #endif
2349 return status;
2352 /* get lockout policy */
2353 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2354 TALLOC_CTX *mem_ctx,
2355 struct samr_DomInfo12 *policy)
2357 struct winbind_cache *cache = get_cache(domain);
2358 struct cache_entry *centry = NULL;
2359 NTSTATUS status;
2361 if (!cache->tdb)
2362 goto do_query;
2364 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2366 if (!centry)
2367 goto do_query;
2369 policy->lockout_duration = centry_nttime(centry);
2370 policy->lockout_window = centry_nttime(centry);
2371 policy->lockout_threshold = centry_uint16(centry);
2373 status = centry->status;
2375 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2376 domain->name, nt_errstr(status) ));
2378 centry_free(centry);
2379 return status;
2381 do_query:
2382 ZERO_STRUCTP(policy);
2384 /* Return status value returned by seq number check */
2386 if (!NT_STATUS_IS_OK(domain->last_status))
2387 return domain->last_status;
2389 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2390 domain->name ));
2392 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2394 /* and save it */
2395 refresh_sequence_number(domain, false);
2396 wcache_save_lockout_policy(domain, status, policy);
2398 return status;
2401 /* get password policy */
2402 static NTSTATUS password_policy(struct winbindd_domain *domain,
2403 TALLOC_CTX *mem_ctx,
2404 struct samr_DomInfo1 *policy)
2406 struct winbind_cache *cache = get_cache(domain);
2407 struct cache_entry *centry = NULL;
2408 NTSTATUS status;
2410 if (!cache->tdb)
2411 goto do_query;
2413 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2415 if (!centry)
2416 goto do_query;
2418 policy->min_password_length = centry_uint16(centry);
2419 policy->password_history_length = centry_uint16(centry);
2420 policy->password_properties = centry_uint32(centry);
2421 policy->max_password_age = centry_nttime(centry);
2422 policy->min_password_age = centry_nttime(centry);
2424 status = centry->status;
2426 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2427 domain->name, nt_errstr(status) ));
2429 centry_free(centry);
2430 return status;
2432 do_query:
2433 ZERO_STRUCTP(policy);
2435 /* Return status value returned by seq number check */
2437 if (!NT_STATUS_IS_OK(domain->last_status))
2438 return domain->last_status;
2440 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2441 domain->name ));
2443 status = domain->backend->password_policy(domain, mem_ctx, policy);
2445 /* and save it */
2446 refresh_sequence_number(domain, false);
2447 if (NT_STATUS_IS_OK(status)) {
2448 wcache_save_password_policy(domain, status, policy);
2451 return status;
2455 /* Invalidate cached user and group lists coherently */
2457 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2458 void *state)
2460 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2461 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2462 tdb_delete(the_tdb, kbuf);
2464 return 0;
2467 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2469 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2470 struct netr_SamInfo3 *info3)
2472 DOM_SID sid;
2473 fstring key_str, sid_string;
2474 struct winbind_cache *cache;
2476 /* dont clear cached U/SID and UG/SID entries when we want to logon
2477 * offline - gd */
2479 if (lp_winbind_offline_logon()) {
2480 return;
2483 if (!domain)
2484 return;
2486 cache = get_cache(domain);
2488 if (!cache->tdb) {
2489 return;
2492 sid_copy(&sid, info3->base.domain_sid);
2493 sid_append_rid(&sid, info3->base.rid);
2495 /* Clear U/SID cache entry */
2496 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2497 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2498 tdb_delete(cache->tdb, string_tdb_data(key_str));
2500 /* Clear UG/SID cache entry */
2501 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2502 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2503 tdb_delete(cache->tdb, string_tdb_data(key_str));
2505 /* Samba/winbindd never needs this. */
2506 netsamlogon_clear_cached_user(info3);
2509 bool wcache_invalidate_cache(void)
2511 struct winbindd_domain *domain;
2513 for (domain = domain_list(); domain; domain = domain->next) {
2514 struct winbind_cache *cache = get_cache(domain);
2516 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2517 "entries for %s\n", domain->name));
2518 if (cache) {
2519 if (cache->tdb) {
2520 tdb_traverse(cache->tdb, traverse_fn, NULL);
2521 } else {
2522 return false;
2526 return true;
2529 bool init_wcache(void)
2531 if (wcache == NULL) {
2532 wcache = SMB_XMALLOC_P(struct winbind_cache);
2533 ZERO_STRUCTP(wcache);
2536 if (wcache->tdb != NULL)
2537 return true;
2539 /* when working offline we must not clear the cache on restart */
2540 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2541 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2542 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2543 O_RDWR|O_CREAT, 0600);
2545 if (wcache->tdb == NULL) {
2546 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2547 return false;
2550 return true;
2553 /************************************************************************
2554 This is called by the parent to initialize the cache file.
2555 We don't need sophisticated locking here as we know we're the
2556 only opener.
2557 ************************************************************************/
2559 bool initialize_winbindd_cache(void)
2561 bool cache_bad = true;
2562 uint32 vers;
2564 if (!init_wcache()) {
2565 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2566 return false;
2569 /* Check version number. */
2570 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2571 vers == WINBINDD_CACHE_VERSION) {
2572 cache_bad = false;
2575 if (cache_bad) {
2576 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2577 "and re-creating with version number %d\n",
2578 WINBINDD_CACHE_VERSION ));
2580 tdb_close(wcache->tdb);
2581 wcache->tdb = NULL;
2583 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2584 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2585 cache_path("winbindd_cache.tdb"),
2586 strerror(errno) ));
2587 return false;
2589 if (!init_wcache()) {
2590 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2591 "init_wcache failed.\n"));
2592 return false;
2595 /* Write the version. */
2596 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2597 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2598 tdb_errorstr(wcache->tdb) ));
2599 return false;
2603 tdb_close(wcache->tdb);
2604 wcache->tdb = NULL;
2605 return true;
2608 void close_winbindd_cache(void)
2610 if (!wcache) {
2611 return;
2613 if (wcache->tdb) {
2614 tdb_close(wcache->tdb);
2615 wcache->tdb = NULL;
2619 void cache_store_response(pid_t pid, struct winbindd_response *response)
2621 fstring key_str;
2623 if (!init_wcache())
2624 return;
2626 DEBUG(10, ("Storing response for pid %d, len %d\n",
2627 (int)pid, response->length));
2629 fstr_sprintf(key_str, "DR/%d", (int)pid);
2630 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2631 make_tdb_data((uint8 *)response, sizeof(*response)),
2632 TDB_REPLACE) == -1)
2633 return;
2635 if (response->length == sizeof(*response))
2636 return;
2638 /* There's extra data */
2640 DEBUG(10, ("Storing extra data: len=%d\n",
2641 (int)(response->length - sizeof(*response))));
2643 fstr_sprintf(key_str, "DE/%d", (int)pid);
2644 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2645 make_tdb_data((uint8 *)response->extra_data.data,
2646 response->length - sizeof(*response)),
2647 TDB_REPLACE) == 0)
2648 return;
2650 /* We could not store the extra data, make sure the tdb does not
2651 * contain a main record with wrong dangling extra data */
2653 fstr_sprintf(key_str, "DR/%d", (int)pid);
2654 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2656 return;
2659 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2661 TDB_DATA data;
2662 fstring key_str;
2664 if (!init_wcache())
2665 return false;
2667 DEBUG(10, ("Retrieving response for pid %d\n", (int)pid));
2669 fstr_sprintf(key_str, "DR/%d", (int)pid);
2670 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2672 if (data.dptr == NULL)
2673 return false;
2675 if (data.dsize != sizeof(*response))
2676 return false;
2678 memcpy(response, data.dptr, data.dsize);
2679 SAFE_FREE(data.dptr);
2681 if (response->length == sizeof(*response)) {
2682 response->extra_data.data = NULL;
2683 return true;
2686 /* There's extra data */
2688 DEBUG(10, ("Retrieving extra data length=%d\n",
2689 (int)(response->length - sizeof(*response))));
2691 fstr_sprintf(key_str, "DE/%d", (int)pid);
2692 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2694 if (data.dptr == NULL) {
2695 DEBUG(0, ("Did not find extra data\n"));
2696 return false;
2699 if (data.dsize != (response->length - sizeof(*response))) {
2700 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2701 SAFE_FREE(data.dptr);
2702 return false;
2705 dump_data(11, (uint8 *)data.dptr, data.dsize);
2707 response->extra_data.data = data.dptr;
2708 return true;
2711 void cache_cleanup_response(pid_t pid)
2713 fstring key_str;
2715 if (!init_wcache())
2716 return;
2718 fstr_sprintf(key_str, "DR/%d", (int)pid);
2719 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2721 fstr_sprintf(key_str, "DE/%d", (int)pid);
2722 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2724 return;
2728 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2729 char **domain_name, char **name,
2730 enum lsa_SidType *type)
2732 struct winbindd_domain *domain;
2733 struct winbind_cache *cache;
2734 struct cache_entry *centry = NULL;
2735 NTSTATUS status;
2736 fstring tmp;
2738 domain = find_lookup_domain_from_sid(sid);
2739 if (domain == NULL) {
2740 return false;
2743 cache = get_cache(domain);
2745 if (cache->tdb == NULL) {
2746 return false;
2749 centry = wcache_fetch(cache, domain, "SN/%s",
2750 sid_to_fstring(tmp, sid));
2751 if (centry == NULL) {
2752 return false;
2755 if (NT_STATUS_IS_OK(centry->status)) {
2756 *type = (enum lsa_SidType)centry_uint32(centry);
2757 *domain_name = centry_string(centry, mem_ctx);
2758 *name = centry_string(centry, mem_ctx);
2761 status = centry->status;
2762 centry_free(centry);
2763 return NT_STATUS_IS_OK(status);
2766 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2767 const char *domain_name,
2768 const char *name,
2769 DOM_SID *sid,
2770 enum lsa_SidType *type)
2772 struct winbindd_domain *domain;
2773 struct winbind_cache *cache;
2774 struct cache_entry *centry = NULL;
2775 NTSTATUS status;
2776 fstring uname;
2777 bool original_online_state;
2779 domain = find_lookup_domain_from_name(domain_name);
2780 if (domain == NULL) {
2781 return false;
2784 cache = get_cache(domain);
2786 if (cache->tdb == NULL) {
2787 return false;
2790 fstrcpy(uname, name);
2791 strupper_m(uname);
2793 /* If we are doing a cached logon, temporarily set the domain
2794 offline so the cache won't expire the entry */
2796 original_online_state = domain->online;
2797 domain->online = false;
2798 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2799 domain->online = original_online_state;
2801 if (centry == NULL) {
2802 return false;
2805 if (NT_STATUS_IS_OK(centry->status)) {
2806 *type = (enum lsa_SidType)centry_uint32(centry);
2807 centry_sid(centry, mem_ctx, sid);
2810 status = centry->status;
2811 centry_free(centry);
2813 return NT_STATUS_IS_OK(status);
2816 void cache_name2sid(struct winbindd_domain *domain,
2817 const char *domain_name, const char *name,
2818 enum lsa_SidType type, const DOM_SID *sid)
2820 refresh_sequence_number(domain, false);
2821 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2822 sid, type);
2826 * The original idea that this cache only contains centries has
2827 * been blurred - now other stuff gets put in here. Ensure we
2828 * ignore these things on cleanup.
2831 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2832 TDB_DATA dbuf, void *state)
2834 struct cache_entry *centry;
2836 if (is_non_centry_key(kbuf)) {
2837 return 0;
2840 centry = wcache_fetch_raw((char *)kbuf.dptr);
2841 if (!centry) {
2842 return 0;
2845 if (!NT_STATUS_IS_OK(centry->status)) {
2846 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2847 tdb_delete(the_tdb, kbuf);
2850 centry_free(centry);
2851 return 0;
2854 /* flush the cache */
2855 void wcache_flush_cache(void)
2857 if (!wcache)
2858 return;
2859 if (wcache->tdb) {
2860 tdb_close(wcache->tdb);
2861 wcache->tdb = NULL;
2863 if (!winbindd_use_cache()) {
2864 return;
2867 /* when working offline we must not clear the cache on restart */
2868 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2869 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2870 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2871 O_RDWR|O_CREAT, 0600);
2873 if (!wcache->tdb) {
2874 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2875 return;
2878 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2880 DEBUG(10,("wcache_flush_cache success\n"));
2883 /* Count cached creds */
2885 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2886 void *state)
2888 int *cred_count = (int*)state;
2890 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2891 (*cred_count)++;
2893 return 0;
2896 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2898 struct winbind_cache *cache = get_cache(domain);
2900 *count = 0;
2902 if (!cache->tdb) {
2903 return NT_STATUS_INTERNAL_DB_ERROR;
2906 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2908 return NT_STATUS_OK;
2911 struct cred_list {
2912 struct cred_list *prev, *next;
2913 TDB_DATA key;
2914 fstring name;
2915 time_t created;
2917 static struct cred_list *wcache_cred_list;
2919 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2920 void *state)
2922 struct cred_list *cred;
2924 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2926 cred = SMB_MALLOC_P(struct cred_list);
2927 if (cred == NULL) {
2928 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2929 return -1;
2932 ZERO_STRUCTP(cred);
2934 /* save a copy of the key */
2936 fstrcpy(cred->name, (const char *)kbuf.dptr);
2937 DLIST_ADD(wcache_cred_list, cred);
2940 return 0;
2943 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2945 struct winbind_cache *cache = get_cache(domain);
2946 NTSTATUS status;
2947 int ret;
2948 struct cred_list *cred, *oldest = NULL;
2950 if (!cache->tdb) {
2951 return NT_STATUS_INTERNAL_DB_ERROR;
2954 /* we possibly already have an entry */
2955 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2957 fstring key_str, tmp;
2959 DEBUG(11,("we already have an entry, deleting that\n"));
2961 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2963 tdb_delete(cache->tdb, string_tdb_data(key_str));
2965 return NT_STATUS_OK;
2968 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2969 if (ret == 0) {
2970 return NT_STATUS_OK;
2971 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2972 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2975 ZERO_STRUCTP(oldest);
2977 for (cred = wcache_cred_list; cred; cred = cred->next) {
2979 TDB_DATA data;
2980 time_t t;
2982 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2983 if (!data.dptr) {
2984 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2985 cred->name));
2986 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2987 goto done;
2990 t = IVAL(data.dptr, 0);
2991 SAFE_FREE(data.dptr);
2993 if (!oldest) {
2994 oldest = SMB_MALLOC_P(struct cred_list);
2995 if (oldest == NULL) {
2996 status = NT_STATUS_NO_MEMORY;
2997 goto done;
3000 fstrcpy(oldest->name, cred->name);
3001 oldest->created = t;
3002 continue;
3005 if (t < oldest->created) {
3006 fstrcpy(oldest->name, cred->name);
3007 oldest->created = t;
3011 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3012 status = NT_STATUS_OK;
3013 } else {
3014 status = NT_STATUS_UNSUCCESSFUL;
3016 done:
3017 SAFE_FREE(wcache_cred_list);
3018 SAFE_FREE(oldest);
3020 return status;
3023 /* Change the global online/offline state. */
3024 bool set_global_winbindd_state_offline(void)
3026 TDB_DATA data;
3028 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3030 /* Only go offline if someone has created
3031 the key "WINBINDD_OFFLINE" in the cache tdb. */
3033 if (wcache == NULL || wcache->tdb == NULL) {
3034 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3035 return false;
3038 if (!lp_winbind_offline_logon()) {
3039 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3040 return false;
3043 if (global_winbindd_offline_state) {
3044 /* Already offline. */
3045 return true;
3048 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3050 if (!data.dptr || data.dsize != 4) {
3051 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3052 SAFE_FREE(data.dptr);
3053 return false;
3054 } else {
3055 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3056 global_winbindd_offline_state = true;
3057 SAFE_FREE(data.dptr);
3058 return true;
3062 void set_global_winbindd_state_online(void)
3064 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3066 if (!lp_winbind_offline_logon()) {
3067 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3068 return;
3071 if (!global_winbindd_offline_state) {
3072 /* Already online. */
3073 return;
3075 global_winbindd_offline_state = false;
3077 if (!wcache->tdb) {
3078 return;
3081 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3082 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3085 bool get_global_winbindd_state_offline(void)
3087 return global_winbindd_offline_state;
3090 /***********************************************************************
3091 Validate functions for all possible cache tdb keys.
3092 ***********************************************************************/
3094 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3095 struct tdb_validation_status *state)
3097 struct cache_entry *centry;
3099 centry = SMB_XMALLOC_P(struct cache_entry);
3100 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3101 if (!centry->data) {
3102 SAFE_FREE(centry);
3103 return NULL;
3105 centry->len = data.dsize;
3106 centry->ofs = 0;
3108 if (centry->len < 8) {
3109 /* huh? corrupt cache? */
3110 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3111 centry_free(centry);
3112 state->bad_entry = true;
3113 state->success = false;
3114 return NULL;
3117 centry->status = NT_STATUS(centry_uint32(centry));
3118 centry->sequence_number = centry_uint32(centry);
3119 return centry;
3122 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3123 struct tdb_validation_status *state)
3125 if (dbuf.dsize != 8) {
3126 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3127 keystr, (unsigned int)dbuf.dsize ));
3128 state->bad_entry = true;
3129 return 1;
3131 return 0;
3134 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3135 struct tdb_validation_status *state)
3137 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3138 if (!centry) {
3139 return 1;
3142 (void)centry_uint32(centry);
3143 if (NT_STATUS_IS_OK(centry->status)) {
3144 DOM_SID sid;
3145 (void)centry_sid(centry, mem_ctx, &sid);
3148 centry_free(centry);
3150 if (!(state->success)) {
3151 return 1;
3153 DEBUG(10,("validate_ns: %s ok\n", keystr));
3154 return 0;
3157 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3158 struct tdb_validation_status *state)
3160 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3161 if (!centry) {
3162 return 1;
3165 if (NT_STATUS_IS_OK(centry->status)) {
3166 (void)centry_uint32(centry);
3167 (void)centry_string(centry, mem_ctx);
3168 (void)centry_string(centry, mem_ctx);
3171 centry_free(centry);
3173 if (!(state->success)) {
3174 return 1;
3176 DEBUG(10,("validate_sn: %s ok\n", keystr));
3177 return 0;
3180 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3181 struct tdb_validation_status *state)
3183 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3184 DOM_SID sid;
3186 if (!centry) {
3187 return 1;
3190 (void)centry_string(centry, mem_ctx);
3191 (void)centry_string(centry, mem_ctx);
3192 (void)centry_string(centry, mem_ctx);
3193 (void)centry_string(centry, mem_ctx);
3194 (void)centry_uint32(centry);
3195 (void)centry_sid(centry, mem_ctx, &sid);
3196 (void)centry_sid(centry, mem_ctx, &sid);
3198 centry_free(centry);
3200 if (!(state->success)) {
3201 return 1;
3203 DEBUG(10,("validate_u: %s ok\n", keystr));
3204 return 0;
3207 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3208 struct tdb_validation_status *state)
3210 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3212 if (!centry) {
3213 return 1;
3216 (void)centry_nttime(centry);
3217 (void)centry_nttime(centry);
3218 (void)centry_uint16(centry);
3220 centry_free(centry);
3222 if (!(state->success)) {
3223 return 1;
3225 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3226 return 0;
3229 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3230 struct tdb_validation_status *state)
3232 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3234 if (!centry) {
3235 return 1;
3238 (void)centry_uint16(centry);
3239 (void)centry_uint16(centry);
3240 (void)centry_uint32(centry);
3241 (void)centry_nttime(centry);
3242 (void)centry_nttime(centry);
3244 centry_free(centry);
3246 if (!(state->success)) {
3247 return 1;
3249 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3250 return 0;
3253 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3254 struct tdb_validation_status *state)
3256 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3258 if (!centry) {
3259 return 1;
3262 (void)centry_time(centry);
3263 (void)centry_hash16(centry, mem_ctx);
3265 /* We only have 17 bytes more data in the salted cred case. */
3266 if (centry->len - centry->ofs == 17) {
3267 (void)centry_hash16(centry, mem_ctx);
3270 centry_free(centry);
3272 if (!(state->success)) {
3273 return 1;
3275 DEBUG(10,("validate_cred: %s ok\n", keystr));
3276 return 0;
3279 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3280 struct tdb_validation_status *state)
3282 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3283 int32 num_entries, i;
3285 if (!centry) {
3286 return 1;
3289 num_entries = (int32)centry_uint32(centry);
3291 for (i=0; i< num_entries; i++) {
3292 DOM_SID sid;
3293 (void)centry_string(centry, mem_ctx);
3294 (void)centry_string(centry, mem_ctx);
3295 (void)centry_string(centry, mem_ctx);
3296 (void)centry_string(centry, mem_ctx);
3297 (void)centry_sid(centry, mem_ctx, &sid);
3298 (void)centry_sid(centry, mem_ctx, &sid);
3301 centry_free(centry);
3303 if (!(state->success)) {
3304 return 1;
3306 DEBUG(10,("validate_ul: %s ok\n", keystr));
3307 return 0;
3310 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3311 struct tdb_validation_status *state)
3313 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3314 int32 num_entries, i;
3316 if (!centry) {
3317 return 1;
3320 num_entries = centry_uint32(centry);
3322 for (i=0; i< num_entries; i++) {
3323 (void)centry_string(centry, mem_ctx);
3324 (void)centry_string(centry, mem_ctx);
3325 (void)centry_uint32(centry);
3328 centry_free(centry);
3330 if (!(state->success)) {
3331 return 1;
3333 DEBUG(10,("validate_gl: %s ok\n", keystr));
3334 return 0;
3337 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3338 struct tdb_validation_status *state)
3340 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3341 int32 num_groups, i;
3343 if (!centry) {
3344 return 1;
3347 num_groups = centry_uint32(centry);
3349 for (i=0; i< num_groups; i++) {
3350 DOM_SID sid;
3351 centry_sid(centry, mem_ctx, &sid);
3354 centry_free(centry);
3356 if (!(state->success)) {
3357 return 1;
3359 DEBUG(10,("validate_ug: %s ok\n", keystr));
3360 return 0;
3363 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3364 struct tdb_validation_status *state)
3366 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3367 int32 num_aliases, i;
3369 if (!centry) {
3370 return 1;
3373 num_aliases = centry_uint32(centry);
3375 for (i=0; i < num_aliases; i++) {
3376 (void)centry_uint32(centry);
3379 centry_free(centry);
3381 if (!(state->success)) {
3382 return 1;
3384 DEBUG(10,("validate_ua: %s ok\n", keystr));
3385 return 0;
3388 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3389 struct tdb_validation_status *state)
3391 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3392 int32 num_names, i;
3394 if (!centry) {
3395 return 1;
3398 num_names = centry_uint32(centry);
3400 for (i=0; i< num_names; i++) {
3401 DOM_SID sid;
3402 centry_sid(centry, mem_ctx, &sid);
3403 (void)centry_string(centry, mem_ctx);
3404 (void)centry_uint32(centry);
3407 centry_free(centry);
3409 if (!(state->success)) {
3410 return 1;
3412 DEBUG(10,("validate_gm: %s ok\n", keystr));
3413 return 0;
3416 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3417 struct tdb_validation_status *state)
3419 /* Can't say anything about this other than must be nonzero. */
3420 if (dbuf.dsize == 0) {
3421 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3422 keystr));
3423 state->bad_entry = true;
3424 state->success = false;
3425 return 1;
3428 DEBUG(10,("validate_dr: %s ok\n", keystr));
3429 return 0;
3432 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3433 struct tdb_validation_status *state)
3435 /* Can't say anything about this other than must be nonzero. */
3436 if (dbuf.dsize == 0) {
3437 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3438 keystr));
3439 state->bad_entry = true;
3440 state->success = false;
3441 return 1;
3444 DEBUG(10,("validate_de: %s ok\n", keystr));
3445 return 0;
3448 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3449 TDB_DATA dbuf, struct tdb_validation_status *state)
3451 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3453 if (!centry) {
3454 return 1;
3457 (void)centry_string(centry, mem_ctx);
3458 (void)centry_string(centry, mem_ctx);
3459 (void)centry_string(centry, mem_ctx);
3460 (void)centry_uint32(centry);
3462 centry_free(centry);
3464 if (!(state->success)) {
3465 return 1;
3467 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3468 return 0;
3471 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3472 TDB_DATA dbuf,
3473 struct tdb_validation_status *state)
3475 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3477 if (!centry) {
3478 return 1;
3481 (void)centry_string( centry, mem_ctx );
3483 centry_free(centry);
3485 if (!(state->success)) {
3486 return 1;
3488 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3489 return 0;
3492 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3493 TDB_DATA dbuf,
3494 struct tdb_validation_status *state)
3496 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3498 if (!centry) {
3499 return 1;
3502 (void)centry_string( centry, mem_ctx );
3504 centry_free(centry);
3506 if (!(state->success)) {
3507 return 1;
3509 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3510 return 0;
3513 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3514 struct tdb_validation_status *state)
3516 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3517 int32 num_domains, i;
3519 if (!centry) {
3520 return 1;
3523 num_domains = centry_uint32(centry);
3525 for (i=0; i< num_domains; i++) {
3526 DOM_SID sid;
3527 (void)centry_string(centry, mem_ctx);
3528 (void)centry_string(centry, mem_ctx);
3529 (void)centry_sid(centry, mem_ctx, &sid);
3532 centry_free(centry);
3534 if (!(state->success)) {
3535 return 1;
3537 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3538 return 0;
3541 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3542 TDB_DATA dbuf,
3543 struct tdb_validation_status *state)
3545 if (dbuf.dsize == 0) {
3546 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3547 "key %s (len ==0) ?\n", keystr));
3548 state->bad_entry = true;
3549 state->success = false;
3550 return 1;
3553 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3554 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3555 return 0;
3558 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3559 struct tdb_validation_status *state)
3561 if (dbuf.dsize != 4) {
3562 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3563 keystr, (unsigned int)dbuf.dsize ));
3564 state->bad_entry = true;
3565 state->success = false;
3566 return 1;
3568 DEBUG(10,("validate_offline: %s ok\n", keystr));
3569 return 0;
3572 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3573 struct tdb_validation_status *state)
3575 if (dbuf.dsize != 4) {
3576 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3577 "key %s (len %u != 4) ?\n",
3578 keystr, (unsigned int)dbuf.dsize));
3579 state->bad_entry = true;
3580 state->success = false;
3581 return 1;
3584 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3585 return 0;
3588 /***********************************************************************
3589 A list of all possible cache tdb keys with associated validation
3590 functions.
3591 ***********************************************************************/
3593 struct key_val_struct {
3594 const char *keyname;
3595 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3596 } key_val[] = {
3597 {"SEQNUM/", validate_seqnum},
3598 {"NS/", validate_ns},
3599 {"SN/", validate_sn},
3600 {"U/", validate_u},
3601 {"LOC_POL/", validate_loc_pol},
3602 {"PWD_POL/", validate_pwd_pol},
3603 {"CRED/", validate_cred},
3604 {"UL/", validate_ul},
3605 {"GL/", validate_gl},
3606 {"UG/", validate_ug},
3607 {"UA", validate_ua},
3608 {"GM/", validate_gm},
3609 {"DR/", validate_dr},
3610 {"DE/", validate_de},
3611 {"NSS/PWINFO/", validate_pwinfo},
3612 {"TRUSTDOMS/", validate_trustdoms},
3613 {"TRUSTDOMCACHE/", validate_trustdomcache},
3614 {"NSS/NA/", validate_nss_na},
3615 {"NSS/AN/", validate_nss_an},
3616 {"WINBINDD_OFFLINE", validate_offline},
3617 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3618 {NULL, NULL}
3621 /***********************************************************************
3622 Function to look at every entry in the tdb and validate it as far as
3623 possible.
3624 ***********************************************************************/
3626 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3628 int i;
3629 unsigned int max_key_len = 1024;
3630 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3632 /* Paranoia check. */
3633 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3634 max_key_len = 1024 * 1024;
3636 if (kbuf.dsize > max_key_len) {
3637 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3638 "(%u) > (%u)\n\n",
3639 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3640 return 1;
3643 for (i = 0; key_val[i].keyname; i++) {
3644 size_t namelen = strlen(key_val[i].keyname);
3645 if (kbuf.dsize >= namelen && (
3646 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3647 TALLOC_CTX *mem_ctx;
3648 char *keystr;
3649 int ret;
3651 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3652 if (!keystr) {
3653 return 1;
3655 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3656 keystr[kbuf.dsize] = '\0';
3658 mem_ctx = talloc_init("validate_ctx");
3659 if (!mem_ctx) {
3660 SAFE_FREE(keystr);
3661 return 1;
3664 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3665 v_state);
3667 SAFE_FREE(keystr);
3668 talloc_destroy(mem_ctx);
3669 return ret;
3673 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3674 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3675 DEBUG(0,("data :\n"));
3676 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3677 v_state->unknown_key = true;
3678 v_state->success = false;
3679 return 1; /* terminate. */
3682 static void validate_panic(const char *const why)
3684 DEBUG(0,("validating cache: would panic %s\n", why ));
3685 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3686 exit(47);
3689 /***********************************************************************
3690 Try and validate every entry in the winbindd cache. If we fail here,
3691 delete the cache tdb and return non-zero.
3692 ***********************************************************************/
3694 int winbindd_validate_cache(void)
3696 int ret = -1;
3697 const char *tdb_path = cache_path("winbindd_cache.tdb");
3698 TDB_CONTEXT *tdb = NULL;
3700 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3701 smb_panic_fn = validate_panic;
3704 tdb = tdb_open_log(tdb_path,
3705 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3706 ( lp_winbind_offline_logon()
3707 ? TDB_DEFAULT
3708 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3709 O_RDWR|O_CREAT,
3710 0600);
3711 if (!tdb) {
3712 DEBUG(0, ("winbindd_validate_cache: "
3713 "error opening/initializing tdb\n"));
3714 goto done;
3716 tdb_close(tdb);
3718 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3720 if (ret != 0) {
3721 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3722 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3723 unlink(tdb_path);
3726 done:
3727 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3728 smb_panic_fn = smb_panic;
3729 return ret;
3732 /***********************************************************************
3733 Try and validate every entry in the winbindd cache.
3734 ***********************************************************************/
3736 int winbindd_validate_cache_nobackup(void)
3738 int ret = -1;
3739 const char *tdb_path = cache_path("winbindd_cache.tdb");
3741 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3742 smb_panic_fn = validate_panic;
3745 if (wcache == NULL || wcache->tdb == NULL) {
3746 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3747 } else {
3748 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3751 if (ret != 0) {
3752 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3753 "successful.\n"));
3756 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3757 "function\n"));
3758 smb_panic_fn = smb_panic;
3759 return ret;
3762 bool winbindd_cache_validate_and_initialize(void)
3764 close_winbindd_cache();
3766 if (lp_winbind_offline_logon()) {
3767 if (winbindd_validate_cache() < 0) {
3768 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3769 "could be restored.\n"));
3773 return initialize_winbindd_cache();
3776 /*********************************************************************
3777 ********************************************************************/
3779 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3780 struct winbindd_tdc_domain **domains,
3781 size_t *num_domains )
3783 struct winbindd_tdc_domain *list = NULL;
3784 size_t idx;
3785 int i;
3786 bool set_only = false;
3788 /* don't allow duplicates */
3790 idx = *num_domains;
3791 list = *domains;
3793 for ( i=0; i< (*num_domains); i++ ) {
3794 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3795 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3796 new_dom->name));
3797 idx = i;
3798 set_only = true;
3800 break;
3804 if ( !set_only ) {
3805 if ( !*domains ) {
3806 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3807 idx = 0;
3808 } else {
3809 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3810 struct winbindd_tdc_domain,
3811 (*num_domains)+1);
3812 idx = *num_domains;
3815 ZERO_STRUCT( list[idx] );
3818 if ( !list )
3819 return false;
3821 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3822 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3824 if ( !is_null_sid( &new_dom->sid ) ) {
3825 sid_copy( &list[idx].sid, &new_dom->sid );
3826 } else {
3827 sid_copy(&list[idx].sid, &global_sid_NULL);
3830 if ( new_dom->domain_flags != 0x0 )
3831 list[idx].trust_flags = new_dom->domain_flags;
3833 if ( new_dom->domain_type != 0x0 )
3834 list[idx].trust_type = new_dom->domain_type;
3836 if ( new_dom->domain_trust_attribs != 0x0 )
3837 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3839 if ( !set_only ) {
3840 *domains = list;
3841 *num_domains = idx + 1;
3844 return true;
3847 /*********************************************************************
3848 ********************************************************************/
3850 static TDB_DATA make_tdc_key( const char *domain_name )
3852 char *keystr = NULL;
3853 TDB_DATA key = { NULL, 0 };
3855 if ( !domain_name ) {
3856 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3857 return key;
3861 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3862 return key;
3864 key = string_term_tdb_data(keystr);
3866 return key;
3869 /*********************************************************************
3870 ********************************************************************/
3872 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3873 size_t num_domains,
3874 unsigned char **buf )
3876 unsigned char *buffer = NULL;
3877 int len = 0;
3878 int buflen = 0;
3879 int i = 0;
3881 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3882 (int)num_domains));
3884 buflen = 0;
3886 again:
3887 len = 0;
3889 /* Store the number of array items first */
3890 len += tdb_pack( buffer+len, buflen-len, "d",
3891 num_domains );
3893 /* now pack each domain trust record */
3894 for ( i=0; i<num_domains; i++ ) {
3896 fstring tmp;
3898 if ( buflen > 0 ) {
3899 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3900 domains[i].domain_name,
3901 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3904 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3905 domains[i].domain_name,
3906 domains[i].dns_name,
3907 sid_to_fstring(tmp, &domains[i].sid),
3908 domains[i].trust_flags,
3909 domains[i].trust_attribs,
3910 domains[i].trust_type );
3913 if ( buflen < len ) {
3914 SAFE_FREE(buffer);
3915 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3916 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3917 buflen = -1;
3918 goto done;
3920 buflen = len;
3921 goto again;
3924 *buf = buffer;
3926 done:
3927 return buflen;
3930 /*********************************************************************
3931 ********************************************************************/
3933 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3934 struct winbindd_tdc_domain **domains )
3936 fstring domain_name, dns_name, sid_string;
3937 uint32 type, attribs, flags;
3938 int num_domains;
3939 int len = 0;
3940 int i;
3941 struct winbindd_tdc_domain *list = NULL;
3943 /* get the number of domains */
3944 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3945 if ( len == -1 ) {
3946 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3947 return 0;
3950 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3951 if ( !list ) {
3952 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3953 return 0;
3956 for ( i=0; i<num_domains; i++ ) {
3957 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3958 domain_name,
3959 dns_name,
3960 sid_string,
3961 &flags,
3962 &attribs,
3963 &type );
3965 if ( len == -1 ) {
3966 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3967 TALLOC_FREE( list );
3968 return 0;
3971 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3972 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3973 domain_name, dns_name, sid_string,
3974 flags, attribs, type));
3976 list[i].domain_name = talloc_strdup( list, domain_name );
3977 list[i].dns_name = talloc_strdup( list, dns_name );
3978 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3979 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3980 domain_name));
3982 list[i].trust_flags = flags;
3983 list[i].trust_attribs = attribs;
3984 list[i].trust_type = type;
3987 *domains = list;
3989 return num_domains;
3992 /*********************************************************************
3993 ********************************************************************/
3995 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3997 TDB_DATA key = make_tdc_key( lp_workgroup() );
3998 TDB_DATA data = { NULL, 0 };
3999 int ret;
4001 if ( !key.dptr )
4002 return false;
4004 /* See if we were asked to delete the cache entry */
4006 if ( !domains ) {
4007 ret = tdb_delete( wcache->tdb, key );
4008 goto done;
4011 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4013 if ( !data.dptr ) {
4014 ret = -1;
4015 goto done;
4018 ret = tdb_store( wcache->tdb, key, data, 0 );
4020 done:
4021 SAFE_FREE( data.dptr );
4022 SAFE_FREE( key.dptr );
4024 return ( ret != -1 );
4027 /*********************************************************************
4028 ********************************************************************/
4030 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4032 TDB_DATA key = make_tdc_key( lp_workgroup() );
4033 TDB_DATA data = { NULL, 0 };
4035 *domains = NULL;
4036 *num_domains = 0;
4038 if ( !key.dptr )
4039 return false;
4041 data = tdb_fetch( wcache->tdb, key );
4043 SAFE_FREE( key.dptr );
4045 if ( !data.dptr )
4046 return false;
4048 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4050 SAFE_FREE( data.dptr );
4052 if ( !*domains )
4053 return false;
4055 return true;
4058 /*********************************************************************
4059 ********************************************************************/
4061 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4063 struct winbindd_tdc_domain *dom_list = NULL;
4064 size_t num_domains = 0;
4065 bool ret = false;
4067 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4068 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4069 domain->name, domain->alt_name,
4070 sid_string_dbg(&domain->sid),
4071 domain->domain_flags,
4072 domain->domain_trust_attribs,
4073 domain->domain_type));
4075 if ( !init_wcache() ) {
4076 return false;
4079 /* fetch the list */
4081 wcache_tdc_fetch_list( &dom_list, &num_domains );
4083 /* add the new domain */
4085 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4086 goto done;
4089 /* pack the domain */
4091 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4092 goto done;
4095 /* Success */
4097 ret = true;
4098 done:
4099 TALLOC_FREE( dom_list );
4101 return ret;
4104 /*********************************************************************
4105 ********************************************************************/
4107 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4109 struct winbindd_tdc_domain *dom_list = NULL;
4110 size_t num_domains = 0;
4111 int i;
4112 struct winbindd_tdc_domain *d = NULL;
4114 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4116 if ( !init_wcache() ) {
4117 return false;
4120 /* fetch the list */
4122 wcache_tdc_fetch_list( &dom_list, &num_domains );
4124 for ( i=0; i<num_domains; i++ ) {
4125 if ( strequal(name, dom_list[i].domain_name) ||
4126 strequal(name, dom_list[i].dns_name) )
4128 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4129 name));
4131 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4132 if ( !d )
4133 break;
4135 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4136 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4137 sid_copy( &d->sid, &dom_list[i].sid );
4138 d->trust_flags = dom_list[i].trust_flags;
4139 d->trust_type = dom_list[i].trust_type;
4140 d->trust_attribs = dom_list[i].trust_attribs;
4142 break;
4146 TALLOC_FREE( dom_list );
4148 return d;
4152 /*********************************************************************
4153 ********************************************************************/
4155 void wcache_tdc_clear( void )
4157 if ( !init_wcache() )
4158 return;
4160 wcache_tdc_store_list( NULL, 0 );
4162 return;
4166 /*********************************************************************
4167 ********************************************************************/
4169 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4170 NTSTATUS status,
4171 const DOM_SID *user_sid,
4172 const char *homedir,
4173 const char *shell,
4174 const char *gecos,
4175 uint32 gid)
4177 struct cache_entry *centry;
4178 fstring tmp;
4180 if ( (centry = centry_start(domain, status)) == NULL )
4181 return;
4183 centry_put_string( centry, homedir );
4184 centry_put_string( centry, shell );
4185 centry_put_string( centry, gecos );
4186 centry_put_uint32( centry, gid );
4188 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4190 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4192 centry_free(centry);
4195 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4196 const DOM_SID *user_sid,
4197 TALLOC_CTX *ctx,
4198 ADS_STRUCT *ads, LDAPMessage *msg,
4199 char **homedir, char **shell, char **gecos,
4200 gid_t *p_gid)
4202 struct winbind_cache *cache = get_cache(domain);
4203 struct cache_entry *centry = NULL;
4204 NTSTATUS nt_status;
4205 fstring tmp;
4207 if (!cache->tdb)
4208 goto do_query;
4210 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4211 sid_to_fstring(tmp, user_sid));
4213 if (!centry)
4214 goto do_query;
4216 *homedir = centry_string( centry, ctx );
4217 *shell = centry_string( centry, ctx );
4218 *gecos = centry_string( centry, ctx );
4219 *p_gid = centry_uint32( centry );
4221 centry_free(centry);
4223 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4224 sid_string_dbg(user_sid)));
4226 return NT_STATUS_OK;
4228 do_query:
4230 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4231 homedir, shell, gecos, p_gid );
4233 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4235 if ( NT_STATUS_IS_OK(nt_status) ) {
4236 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4237 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4238 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4239 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4241 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4242 *homedir, *shell, *gecos, *p_gid );
4245 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4246 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4247 domain->name ));
4248 set_domain_offline( domain );
4251 return nt_status;
4255 /* the cache backend methods are exposed via this structure */
4256 struct winbindd_methods cache_methods = {
4257 true,
4258 query_user_list,
4259 enum_dom_groups,
4260 enum_local_groups,
4261 name_to_sid,
4262 sid_to_name,
4263 rids_to_names,
4264 query_user,
4265 lookup_usergroups,
4266 lookup_useraliases,
4267 lookup_groupmem,
4268 sequence_number,
4269 lockout_policy,
4270 password_policy,
4271 trusted_domains