s3:winbind: Make wcache_lookup_useraliases available publically
[Samba/fernandojvsilva.git] / source3 / winbindd / winbindd_cache.c
blobea3f00e6d8a3cca7ccdd018d13ef224a88d21ec8
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "winbindd.h"
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 #define WINBINDD_CACHE_VERSION 1
35 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
37 extern struct winbindd_methods reconnect_methods;
38 #ifdef HAVE_ADS
39 extern struct winbindd_methods ads_methods;
40 #endif
41 extern struct winbindd_methods builtin_passdb_methods;
44 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
45 * Here are the list of entry types that are *not* stored
46 * as form struct cache_entry in the cache.
49 static const char *non_centry_keys[] = {
50 "SEQNUM/",
51 "DR/",
52 "DE/",
53 "WINBINDD_OFFLINE",
54 WINBINDD_CACHE_VERSION_KEYSTR,
55 NULL
58 /************************************************************************
59 Is this key a non-centry type ?
60 ************************************************************************/
62 static bool is_non_centry_key(TDB_DATA kbuf)
64 int i;
66 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
67 return false;
69 for (i = 0; non_centry_keys[i] != NULL; i++) {
70 size_t namelen = strlen(non_centry_keys[i]);
71 if (kbuf.dsize < namelen) {
72 continue;
74 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
75 return true;
78 return false;
81 /* Global online/offline state - False when online. winbindd starts up online
82 and sets this to true if the first query fails and there's an entry in
83 the cache tdb telling us to stay offline. */
85 static bool global_winbindd_offline_state;
87 struct winbind_cache {
88 TDB_CONTEXT *tdb;
91 struct cache_entry {
92 NTSTATUS status;
93 uint32 sequence_number;
94 uint8 *data;
95 uint32 len, ofs;
98 void (*smb_panic_fn)(const char *const why) = smb_panic;
100 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
102 static struct winbind_cache *wcache;
104 void winbindd_check_cache_size(time_t t)
106 static time_t last_check_time;
107 struct stat st;
109 if (last_check_time == (time_t)0)
110 last_check_time = t;
112 if (t - last_check_time < 60 && t - last_check_time > 0)
113 return;
115 if (wcache == NULL || wcache->tdb == NULL) {
116 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
117 return;
120 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
121 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
122 return;
125 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
126 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
127 (unsigned long)st.st_size,
128 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
129 wcache_flush_cache();
133 /* get the winbind_cache structure */
134 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
136 struct winbind_cache *ret = wcache;
138 /* We have to know what type of domain we are dealing with first. */
140 if (domain->internal) {
141 domain->backend = &builtin_passdb_methods;
142 domain->initialized = True;
144 if ( !domain->initialized ) {
145 init_dc_connection( domain );
149 OK. listen up becasue I'm only going to say this once.
150 We have the following scenarios to consider
151 (a) trusted AD domains on a Samba DC,
152 (b) trusted AD domains and we are joined to a non-kerberos domain
153 (c) trusted AD domains and we are joined to a kerberos (AD) domain
155 For (a) we can always contact the trusted domain using krb5
156 since we have the domain trust account password
158 For (b) we can only use RPC since we have no way of
159 getting a krb5 ticket in our own domain
161 For (c) we can always use krb5 since we have a kerberos trust
163 --jerry
166 if (!domain->backend) {
167 #ifdef HAVE_ADS
168 struct winbindd_domain *our_domain = domain;
170 /* find our domain first so we can figure out if we
171 are joined to a kerberized domain */
173 if ( !domain->primary )
174 our_domain = find_our_domain();
176 if ((our_domain->active_directory || IS_DC)
177 && domain->active_directory
178 && !lp_winbind_rpc_only()) {
179 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
180 domain->backend = &ads_methods;
181 } else {
182 #endif /* HAVE_ADS */
183 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
184 domain->backend = &reconnect_methods;
185 #ifdef HAVE_ADS
187 #endif /* HAVE_ADS */
190 if (ret)
191 return ret;
193 ret = SMB_XMALLOC_P(struct winbind_cache);
194 ZERO_STRUCTP(ret);
196 wcache = ret;
197 wcache_flush_cache();
199 return ret;
203 free a centry structure
205 static void centry_free(struct cache_entry *centry)
207 if (!centry)
208 return;
209 SAFE_FREE(centry->data);
210 free(centry);
213 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
215 if (centry->len - centry->ofs < nbytes) {
216 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
217 (unsigned int)nbytes,
218 centry->len - centry->ofs));
219 return false;
221 return true;
225 pull a uint32 from a cache entry
227 static uint32 centry_uint32(struct cache_entry *centry)
229 uint32 ret;
231 if (!centry_check_bytes(centry, 4)) {
232 smb_panic_fn("centry_uint32");
234 ret = IVAL(centry->data, centry->ofs);
235 centry->ofs += 4;
236 return ret;
240 pull a uint16 from a cache entry
242 static uint16 centry_uint16(struct cache_entry *centry)
244 uint16 ret;
245 if (!centry_check_bytes(centry, 2)) {
246 smb_panic_fn("centry_uint16");
248 ret = CVAL(centry->data, centry->ofs);
249 centry->ofs += 2;
250 return ret;
254 pull a uint8 from a cache entry
256 static uint8 centry_uint8(struct cache_entry *centry)
258 uint8 ret;
259 if (!centry_check_bytes(centry, 1)) {
260 smb_panic_fn("centry_uint8");
262 ret = CVAL(centry->data, centry->ofs);
263 centry->ofs += 1;
264 return ret;
268 pull a NTTIME from a cache entry
270 static NTTIME centry_nttime(struct cache_entry *centry)
272 NTTIME ret;
273 if (!centry_check_bytes(centry, 8)) {
274 smb_panic_fn("centry_nttime");
276 ret = IVAL(centry->data, centry->ofs);
277 centry->ofs += 4;
278 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
279 centry->ofs += 4;
280 return ret;
284 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
286 static time_t centry_time(struct cache_entry *centry)
288 return (time_t)centry_nttime(centry);
291 /* pull a string from a cache entry, using the supplied
292 talloc context
294 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
296 uint32 len;
297 char *ret;
299 len = centry_uint8(centry);
301 if (len == 0xFF) {
302 /* a deliberate NULL string */
303 return NULL;
306 if (!centry_check_bytes(centry, (size_t)len)) {
307 smb_panic_fn("centry_string");
310 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
311 if (!ret) {
312 smb_panic_fn("centry_string out of memory\n");
314 memcpy(ret,centry->data + centry->ofs, len);
315 ret[len] = 0;
316 centry->ofs += len;
317 return ret;
320 /* pull a hash16 from a cache entry, using the supplied
321 talloc context
323 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
325 uint32 len;
326 char *ret;
328 len = centry_uint8(centry);
330 if (len != 16) {
331 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
332 len ));
333 return NULL;
336 if (!centry_check_bytes(centry, 16)) {
337 return NULL;
340 ret = TALLOC_ARRAY(mem_ctx, char, 16);
341 if (!ret) {
342 smb_panic_fn("centry_hash out of memory\n");
344 memcpy(ret,centry->data + centry->ofs, 16);
345 centry->ofs += 16;
346 return ret;
349 /* pull a sid from a cache entry, using the supplied
350 talloc context
352 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
354 char *sid_string;
355 bool ret;
357 sid_string = centry_string(centry, talloc_tos());
358 if (sid_string == NULL) {
359 return false;
361 ret = string_to_sid(sid, sid_string);
362 TALLOC_FREE(sid_string);
363 return ret;
368 pull a NTSTATUS from a cache entry
370 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
372 NTSTATUS status;
374 status = NT_STATUS(centry_uint32(centry));
375 return status;
379 /* the server is considered down if it can't give us a sequence number */
380 static bool wcache_server_down(struct winbindd_domain *domain)
382 bool ret;
384 if (!wcache->tdb)
385 return false;
387 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
389 if (ret)
390 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
391 domain->name ));
392 return ret;
395 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
397 TDB_DATA data;
398 fstring key;
399 uint32 time_diff;
401 if (!wcache->tdb) {
402 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
403 return NT_STATUS_UNSUCCESSFUL;
406 fstr_sprintf( key, "SEQNUM/%s", domain->name );
408 data = tdb_fetch_bystring( wcache->tdb, key );
409 if ( !data.dptr || data.dsize!=8 ) {
410 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
411 return NT_STATUS_UNSUCCESSFUL;
414 domain->sequence_number = IVAL(data.dptr, 0);
415 domain->last_seq_check = IVAL(data.dptr, 4);
417 SAFE_FREE(data.dptr);
419 /* have we expired? */
421 time_diff = now - domain->last_seq_check;
422 if ( time_diff > lp_winbind_cache_time() ) {
423 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
424 domain->name, domain->sequence_number,
425 (uint32)domain->last_seq_check));
426 return NT_STATUS_UNSUCCESSFUL;
429 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
430 domain->name, domain->sequence_number,
431 (uint32)domain->last_seq_check));
433 return NT_STATUS_OK;
436 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
438 TDB_DATA data;
439 fstring key_str;
440 uint8 buf[8];
442 if (!wcache->tdb) {
443 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
444 return NT_STATUS_UNSUCCESSFUL;
447 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
449 SIVAL(buf, 0, domain->sequence_number);
450 SIVAL(buf, 4, domain->last_seq_check);
451 data.dptr = buf;
452 data.dsize = 8;
454 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
455 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
456 return NT_STATUS_UNSUCCESSFUL;
459 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
460 domain->name, domain->sequence_number,
461 (uint32)domain->last_seq_check));
463 return NT_STATUS_OK;
467 refresh the domain sequence number. If force is true
468 then always refresh it, no matter how recently we fetched it
471 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
473 NTSTATUS status;
474 unsigned time_diff;
475 time_t t = time(NULL);
476 unsigned cache_time = lp_winbind_cache_time();
478 if ( IS_DOMAIN_OFFLINE(domain) ) {
479 return;
482 get_cache( domain );
484 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
485 /* trying to reconnect is expensive, don't do it too often */
486 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
487 cache_time *= 8;
489 #endif
491 time_diff = t - domain->last_seq_check;
493 /* see if we have to refetch the domain sequence number */
494 if (!force && (time_diff < cache_time) &&
495 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
496 NT_STATUS_IS_OK(domain->last_status)) {
497 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
498 goto done;
501 /* try to get the sequence number from the tdb cache first */
502 /* this will update the timestamp as well */
504 status = fetch_cache_seqnum( domain, t );
505 if (NT_STATUS_IS_OK(status) &&
506 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
507 NT_STATUS_IS_OK(domain->last_status)) {
508 goto done;
511 /* important! make sure that we know if this is a native
512 mode domain or not. And that we can contact it. */
514 if ( winbindd_can_contact_domain( domain ) ) {
515 status = domain->backend->sequence_number(domain,
516 &domain->sequence_number);
517 } else {
518 /* just use the current time */
519 status = NT_STATUS_OK;
520 domain->sequence_number = time(NULL);
524 /* the above call could have set our domain->backend to NULL when
525 * coming from offline to online mode, make sure to reinitialize the
526 * backend - Guenther */
527 get_cache( domain );
529 if (!NT_STATUS_IS_OK(status)) {
530 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
531 domain->sequence_number = DOM_SEQUENCE_NONE;
534 domain->last_status = status;
535 domain->last_seq_check = time(NULL);
537 /* save the new sequence number in the cache */
538 store_cache_seqnum( domain );
540 done:
541 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
542 domain->name, domain->sequence_number));
544 return;
548 decide if a cache entry has expired
550 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
552 /* If we've been told to be offline - stay in that state... */
553 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
554 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
555 keystr, domain->name ));
556 return false;
559 /* when the domain is offline return the cached entry.
560 * This deals with transient offline states... */
562 if (!domain->online) {
563 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
564 keystr, domain->name ));
565 return false;
568 /* if the server is OK and our cache entry came from when it was down then
569 the entry is invalid */
570 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
571 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
572 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
573 keystr, domain->name ));
574 return true;
577 /* if the server is down or the cache entry is not older than the
578 current sequence number then it is OK */
579 if (wcache_server_down(domain) ||
580 centry->sequence_number == domain->sequence_number) {
581 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
582 keystr, domain->name ));
583 return false;
586 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
587 keystr, domain->name ));
589 /* it's expired */
590 return true;
593 static struct cache_entry *wcache_fetch_raw(char *kstr)
595 TDB_DATA data;
596 struct cache_entry *centry;
597 TDB_DATA key;
599 key = string_tdb_data(kstr);
600 data = tdb_fetch(wcache->tdb, key);
601 if (!data.dptr) {
602 /* a cache miss */
603 return NULL;
606 centry = SMB_XMALLOC_P(struct cache_entry);
607 centry->data = (unsigned char *)data.dptr;
608 centry->len = data.dsize;
609 centry->ofs = 0;
611 if (centry->len < 8) {
612 /* huh? corrupt cache? */
613 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
614 centry_free(centry);
615 return NULL;
618 centry->status = centry_ntstatus(centry);
619 centry->sequence_number = centry_uint32(centry);
621 return centry;
625 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
626 number and return status
628 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
629 struct winbindd_domain *domain,
630 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
631 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
632 struct winbindd_domain *domain,
633 const char *format, ...)
635 va_list ap;
636 char *kstr;
637 struct cache_entry *centry;
639 if (!winbindd_use_cache()) {
640 return NULL;
643 refresh_sequence_number(domain, false);
645 va_start(ap, format);
646 smb_xvasprintf(&kstr, format, ap);
647 va_end(ap);
649 centry = wcache_fetch_raw(kstr);
650 if (centry == NULL) {
651 free(kstr);
652 return NULL;
655 if (centry_expired(domain, kstr, centry)) {
657 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
658 kstr, domain->name ));
660 centry_free(centry);
661 free(kstr);
662 return NULL;
665 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
666 kstr, domain->name ));
668 free(kstr);
669 return centry;
672 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
673 static void wcache_delete(const char *format, ...)
675 va_list ap;
676 char *kstr;
677 TDB_DATA key;
679 va_start(ap, format);
680 smb_xvasprintf(&kstr, format, ap);
681 va_end(ap);
683 key = string_tdb_data(kstr);
685 tdb_delete(wcache->tdb, key);
686 free(kstr);
690 make sure we have at least len bytes available in a centry
692 static void centry_expand(struct cache_entry *centry, uint32 len)
694 if (centry->len - centry->ofs >= len)
695 return;
696 centry->len *= 2;
697 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
698 centry->len);
699 if (!centry->data) {
700 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
701 smb_panic_fn("out of memory in centry_expand");
706 push a uint32 into a centry
708 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
710 centry_expand(centry, 4);
711 SIVAL(centry->data, centry->ofs, v);
712 centry->ofs += 4;
716 push a uint16 into a centry
718 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
720 centry_expand(centry, 2);
721 SIVAL(centry->data, centry->ofs, v);
722 centry->ofs += 2;
726 push a uint8 into a centry
728 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
730 centry_expand(centry, 1);
731 SCVAL(centry->data, centry->ofs, v);
732 centry->ofs += 1;
736 push a string into a centry
738 static void centry_put_string(struct cache_entry *centry, const char *s)
740 int len;
742 if (!s) {
743 /* null strings are marked as len 0xFFFF */
744 centry_put_uint8(centry, 0xFF);
745 return;
748 len = strlen(s);
749 /* can't handle more than 254 char strings. Truncating is probably best */
750 if (len > 254) {
751 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
752 len = 254;
754 centry_put_uint8(centry, len);
755 centry_expand(centry, len);
756 memcpy(centry->data + centry->ofs, s, len);
757 centry->ofs += len;
761 push a 16 byte hash into a centry - treat as 16 byte string.
763 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
765 centry_put_uint8(centry, 16);
766 centry_expand(centry, 16);
767 memcpy(centry->data + centry->ofs, val, 16);
768 centry->ofs += 16;
771 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
773 fstring sid_string;
774 centry_put_string(centry, sid_to_fstring(sid_string, sid));
779 put NTSTATUS into a centry
781 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
783 uint32 status_value = NT_STATUS_V(status);
784 centry_put_uint32(centry, status_value);
789 push a NTTIME into a centry
791 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
793 centry_expand(centry, 8);
794 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
795 centry->ofs += 4;
796 SIVAL(centry->data, centry->ofs, nt >> 32);
797 centry->ofs += 4;
801 push a time_t into a centry - use a 64 bit size.
802 NTTIME here is being used as a convenient 64-bit size.
804 static void centry_put_time(struct cache_entry *centry, time_t t)
806 NTTIME nt = (NTTIME)t;
807 centry_put_nttime(centry, nt);
811 start a centry for output. When finished, call centry_end()
813 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
815 struct cache_entry *centry;
817 if (!wcache->tdb)
818 return NULL;
820 centry = SMB_XMALLOC_P(struct cache_entry);
822 centry->len = 8192; /* reasonable default */
823 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
824 centry->ofs = 0;
825 centry->sequence_number = domain->sequence_number;
826 centry_put_ntstatus(centry, status);
827 centry_put_uint32(centry, centry->sequence_number);
828 return centry;
832 finish a centry and write it to the tdb
834 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
835 static void centry_end(struct cache_entry *centry, const char *format, ...)
837 va_list ap;
838 char *kstr;
839 TDB_DATA key, data;
841 if (!winbindd_use_cache()) {
842 return;
845 va_start(ap, format);
846 smb_xvasprintf(&kstr, format, ap);
847 va_end(ap);
849 key = string_tdb_data(kstr);
850 data.dptr = centry->data;
851 data.dsize = centry->ofs;
853 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
854 free(kstr);
857 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
858 NTSTATUS status, const char *domain_name,
859 const char *name, const DOM_SID *sid,
860 enum lsa_SidType type)
862 struct cache_entry *centry;
863 fstring uname;
865 centry = centry_start(domain, status);
866 if (!centry)
867 return;
868 centry_put_uint32(centry, type);
869 centry_put_sid(centry, sid);
870 fstrcpy(uname, name);
871 strupper_m(uname);
872 centry_end(centry, "NS/%s/%s", domain_name, uname);
873 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
874 uname, sid_string_dbg(sid), nt_errstr(status)));
875 centry_free(centry);
878 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
879 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
881 struct cache_entry *centry;
882 fstring sid_string;
884 centry = centry_start(domain, status);
885 if (!centry)
886 return;
888 if (NT_STATUS_IS_OK(status)) {
889 centry_put_uint32(centry, type);
890 centry_put_string(centry, domain_name);
891 centry_put_string(centry, name);
894 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
895 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
896 name, nt_errstr(status)));
897 centry_free(centry);
901 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
903 struct cache_entry *centry;
904 fstring sid_string;
906 if (is_null_sid(&info->user_sid)) {
907 return;
910 centry = centry_start(domain, status);
911 if (!centry)
912 return;
913 centry_put_string(centry, info->acct_name);
914 centry_put_string(centry, info->full_name);
915 centry_put_string(centry, info->homedir);
916 centry_put_string(centry, info->shell);
917 centry_put_uint32(centry, info->primary_gid);
918 centry_put_sid(centry, &info->user_sid);
919 centry_put_sid(centry, &info->group_sid);
920 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
921 &info->user_sid));
922 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
923 centry_free(centry);
926 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
927 NTSTATUS status,
928 struct samr_DomInfo12 *lockout_policy)
930 struct cache_entry *centry;
932 centry = centry_start(domain, status);
933 if (!centry)
934 return;
936 centry_put_nttime(centry, lockout_policy->lockout_duration);
937 centry_put_nttime(centry, lockout_policy->lockout_window);
938 centry_put_uint16(centry, lockout_policy->lockout_threshold);
940 centry_end(centry, "LOC_POL/%s", domain->name);
942 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
944 centry_free(centry);
949 static void wcache_save_password_policy(struct winbindd_domain *domain,
950 NTSTATUS status,
951 struct samr_DomInfo1 *policy)
953 struct cache_entry *centry;
955 centry = centry_start(domain, status);
956 if (!centry)
957 return;
959 centry_put_uint16(centry, policy->min_password_length);
960 centry_put_uint16(centry, policy->password_history_length);
961 centry_put_uint32(centry, policy->password_properties);
962 centry_put_nttime(centry, policy->max_password_age);
963 centry_put_nttime(centry, policy->min_password_age);
965 centry_end(centry, "PWD_POL/%s", domain->name);
967 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
969 centry_free(centry);
972 /***************************************************************************
973 ***************************************************************************/
975 static void wcache_save_username_alias(struct winbindd_domain *domain,
976 NTSTATUS status,
977 const char *name, const char *alias)
979 struct cache_entry *centry;
980 fstring uname;
982 if ( (centry = centry_start(domain, status)) == NULL )
983 return;
985 centry_put_string( centry, alias );
987 fstrcpy(uname, name);
988 strupper_m(uname);
989 centry_end(centry, "NSS/NA/%s", uname);
991 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
993 centry_free(centry);
996 static void wcache_save_alias_username(struct winbindd_domain *domain,
997 NTSTATUS status,
998 const char *alias, const char *name)
1000 struct cache_entry *centry;
1001 fstring uname;
1003 if ( (centry = centry_start(domain, status)) == NULL )
1004 return;
1006 centry_put_string( centry, name );
1008 fstrcpy(uname, alias);
1009 strupper_m(uname);
1010 centry_end(centry, "NSS/AN/%s", uname);
1012 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1014 centry_free(centry);
1017 /***************************************************************************
1018 ***************************************************************************/
1020 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1021 struct winbindd_domain *domain,
1022 const char *name, char **alias )
1024 struct winbind_cache *cache = get_cache(domain);
1025 struct cache_entry *centry = NULL;
1026 NTSTATUS status;
1027 char *upper_name;
1029 if ( domain->internal )
1030 return NT_STATUS_NOT_SUPPORTED;
1032 if (!cache->tdb)
1033 goto do_query;
1035 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1036 return NT_STATUS_NO_MEMORY;
1037 strupper_m(upper_name);
1039 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1041 SAFE_FREE( upper_name );
1043 if (!centry)
1044 goto do_query;
1046 status = centry->status;
1048 if (!NT_STATUS_IS_OK(status)) {
1049 centry_free(centry);
1050 return status;
1053 *alias = centry_string( centry, mem_ctx );
1055 centry_free(centry);
1057 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1058 name, *alias ? *alias : "(none)"));
1060 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1062 do_query:
1064 /* If its not in cache and we are offline, then fail */
1066 if ( get_global_winbindd_state_offline() || !domain->online ) {
1067 DEBUG(8,("resolve_username_to_alias: rejecting query "
1068 "in offline mode\n"));
1069 return NT_STATUS_NOT_FOUND;
1072 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1074 if ( NT_STATUS_IS_OK( status ) ) {
1075 wcache_save_username_alias(domain, status, name, *alias);
1078 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1079 wcache_save_username_alias(domain, status, name, "(NULL)");
1082 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1083 nt_errstr(status)));
1085 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1086 set_domain_offline( domain );
1089 return status;
1092 /***************************************************************************
1093 ***************************************************************************/
1095 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1096 struct winbindd_domain *domain,
1097 const char *alias, char **name )
1099 struct winbind_cache *cache = get_cache(domain);
1100 struct cache_entry *centry = NULL;
1101 NTSTATUS status;
1102 char *upper_name;
1104 if ( domain->internal )
1105 return NT_STATUS_NOT_SUPPORTED;
1107 if (!cache->tdb)
1108 goto do_query;
1110 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1111 return NT_STATUS_NO_MEMORY;
1112 strupper_m(upper_name);
1114 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1116 SAFE_FREE( upper_name );
1118 if (!centry)
1119 goto do_query;
1121 status = centry->status;
1123 if (!NT_STATUS_IS_OK(status)) {
1124 centry_free(centry);
1125 return status;
1128 *name = centry_string( centry, mem_ctx );
1130 centry_free(centry);
1132 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1133 alias, *name ? *name : "(none)"));
1135 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1137 do_query:
1139 /* If its not in cache and we are offline, then fail */
1141 if ( get_global_winbindd_state_offline() || !domain->online ) {
1142 DEBUG(8,("resolve_alias_to_username: rejecting query "
1143 "in offline mode\n"));
1144 return NT_STATUS_NOT_FOUND;
1147 /* an alias cannot contain a domain prefix or '@' */
1149 if (strchr(alias, '\\') || strchr(alias, '@')) {
1150 DEBUG(10,("resolve_alias_to_username: skipping fully "
1151 "qualified name %s\n", alias));
1152 return NT_STATUS_OBJECT_NAME_INVALID;
1155 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1157 if ( NT_STATUS_IS_OK( status ) ) {
1158 wcache_save_alias_username( domain, status, alias, *name );
1161 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1162 wcache_save_alias_username(domain, status, alias, "(NULL)");
1165 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1166 nt_errstr(status)));
1168 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1169 set_domain_offline( domain );
1172 return status;
1175 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1177 struct winbind_cache *cache = get_cache(domain);
1178 TDB_DATA data;
1179 fstring key_str, tmp;
1180 uint32 rid;
1182 if (!cache->tdb) {
1183 return NT_STATUS_INTERNAL_DB_ERROR;
1186 if (is_null_sid(sid)) {
1187 return NT_STATUS_INVALID_SID;
1190 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1191 return NT_STATUS_INVALID_SID;
1194 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1196 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1197 if (!data.dptr) {
1198 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1201 SAFE_FREE(data.dptr);
1202 return NT_STATUS_OK;
1205 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1206 as new salted ones. */
1208 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1209 TALLOC_CTX *mem_ctx,
1210 const DOM_SID *sid,
1211 const uint8 **cached_nt_pass,
1212 const uint8 **cached_salt)
1214 struct winbind_cache *cache = get_cache(domain);
1215 struct cache_entry *centry = NULL;
1216 NTSTATUS status;
1217 time_t t;
1218 uint32 rid;
1219 fstring tmp;
1221 if (!cache->tdb) {
1222 return NT_STATUS_INTERNAL_DB_ERROR;
1225 if (is_null_sid(sid)) {
1226 return NT_STATUS_INVALID_SID;
1229 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1230 return NT_STATUS_INVALID_SID;
1233 /* Try and get a salted cred first. If we can't
1234 fall back to an unsalted cred. */
1236 centry = wcache_fetch(cache, domain, "CRED/%s",
1237 sid_to_fstring(tmp, sid));
1238 if (!centry) {
1239 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1240 sid_string_dbg(sid)));
1241 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1244 t = centry_time(centry);
1246 /* In the salted case this isn't actually the nt_hash itself,
1247 but the MD5 of the salt + nt_hash. Let the caller
1248 sort this out. It can tell as we only return the cached_salt
1249 if we are returning a salted cred. */
1251 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1252 if (*cached_nt_pass == NULL) {
1253 fstring sidstr;
1255 sid_to_fstring(sidstr, sid);
1257 /* Bad (old) cred cache. Delete and pretend we
1258 don't have it. */
1259 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1260 sidstr));
1261 wcache_delete("CRED/%s", sidstr);
1262 centry_free(centry);
1263 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1266 /* We only have 17 bytes more data in the salted cred case. */
1267 if (centry->len - centry->ofs == 17) {
1268 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1269 } else {
1270 *cached_salt = NULL;
1273 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1274 if (*cached_salt) {
1275 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1278 status = centry->status;
1280 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1281 sid_string_dbg(sid), nt_errstr(status) ));
1283 centry_free(centry);
1284 return status;
1287 /* Store creds for a SID - only writes out new salted ones. */
1289 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1290 TALLOC_CTX *mem_ctx,
1291 const DOM_SID *sid,
1292 const uint8 nt_pass[NT_HASH_LEN])
1294 struct cache_entry *centry;
1295 fstring sid_string;
1296 uint32 rid;
1297 uint8 cred_salt[NT_HASH_LEN];
1298 uint8 salted_hash[NT_HASH_LEN];
1300 if (is_null_sid(sid)) {
1301 return NT_STATUS_INVALID_SID;
1304 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1305 return NT_STATUS_INVALID_SID;
1308 centry = centry_start(domain, NT_STATUS_OK);
1309 if (!centry) {
1310 return NT_STATUS_INTERNAL_DB_ERROR;
1313 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1315 centry_put_time(centry, time(NULL));
1317 /* Create a salt and then salt the hash. */
1318 generate_random_buffer(cred_salt, NT_HASH_LEN);
1319 E_md5hash(cred_salt, nt_pass, salted_hash);
1321 centry_put_hash16(centry, salted_hash);
1322 centry_put_hash16(centry, cred_salt);
1323 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1325 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1327 centry_free(centry);
1329 return NT_STATUS_OK;
1333 /* Query display info. This is the basic user list fn */
1334 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1335 TALLOC_CTX *mem_ctx,
1336 uint32 *num_entries,
1337 WINBIND_USERINFO **info)
1339 struct winbind_cache *cache = get_cache(domain);
1340 struct cache_entry *centry = NULL;
1341 NTSTATUS status;
1342 unsigned int i, retry;
1344 if (!cache->tdb)
1345 goto do_query;
1347 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1348 if (!centry)
1349 goto do_query;
1351 *num_entries = centry_uint32(centry);
1353 if (*num_entries == 0)
1354 goto do_cached;
1356 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1357 if (! (*info)) {
1358 smb_panic_fn("query_user_list out of memory");
1360 for (i=0; i<(*num_entries); i++) {
1361 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1362 (*info)[i].full_name = centry_string(centry, mem_ctx);
1363 (*info)[i].homedir = centry_string(centry, mem_ctx);
1364 (*info)[i].shell = centry_string(centry, mem_ctx);
1365 centry_sid(centry, &(*info)[i].user_sid);
1366 centry_sid(centry, &(*info)[i].group_sid);
1369 do_cached:
1370 status = centry->status;
1372 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1373 domain->name, nt_errstr(status) ));
1375 centry_free(centry);
1376 return status;
1378 do_query:
1379 *num_entries = 0;
1380 *info = NULL;
1382 /* Return status value returned by seq number check */
1384 if (!NT_STATUS_IS_OK(domain->last_status))
1385 return domain->last_status;
1387 /* Put the query_user_list() in a retry loop. There appears to be
1388 * some bug either with Windows 2000 or Samba's handling of large
1389 * rpc replies. This manifests itself as sudden disconnection
1390 * at a random point in the enumeration of a large (60k) user list.
1391 * The retry loop simply tries the operation again. )-: It's not
1392 * pretty but an acceptable workaround until we work out what the
1393 * real problem is. */
1395 retry = 0;
1396 do {
1398 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1399 domain->name ));
1401 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1402 if (!NT_STATUS_IS_OK(status)) {
1403 DEBUG(3, ("query_user_list: returned 0x%08x, "
1404 "retrying\n", NT_STATUS_V(status)));
1406 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1407 DEBUG(3, ("query_user_list: flushing "
1408 "connection cache\n"));
1409 invalidate_cm_connection(&domain->conn);
1412 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1413 (retry++ < 5));
1415 /* and save it */
1416 refresh_sequence_number(domain, false);
1417 centry = centry_start(domain, status);
1418 if (!centry)
1419 goto skip_save;
1420 centry_put_uint32(centry, *num_entries);
1421 for (i=0; i<(*num_entries); i++) {
1422 centry_put_string(centry, (*info)[i].acct_name);
1423 centry_put_string(centry, (*info)[i].full_name);
1424 centry_put_string(centry, (*info)[i].homedir);
1425 centry_put_string(centry, (*info)[i].shell);
1426 centry_put_sid(centry, &(*info)[i].user_sid);
1427 centry_put_sid(centry, &(*info)[i].group_sid);
1428 if (domain->backend && domain->backend->consistent) {
1429 /* when the backend is consistent we can pre-prime some mappings */
1430 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1431 domain->name,
1432 (*info)[i].acct_name,
1433 &(*info)[i].user_sid,
1434 SID_NAME_USER);
1435 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1436 &(*info)[i].user_sid,
1437 domain->name,
1438 (*info)[i].acct_name,
1439 SID_NAME_USER);
1440 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1443 centry_end(centry, "UL/%s", domain->name);
1444 centry_free(centry);
1446 skip_save:
1447 return status;
1450 /* list all domain groups */
1451 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1452 TALLOC_CTX *mem_ctx,
1453 uint32 *num_entries,
1454 struct acct_info **info)
1456 struct winbind_cache *cache = get_cache(domain);
1457 struct cache_entry *centry = NULL;
1458 NTSTATUS status;
1459 unsigned int i;
1461 if (!cache->tdb)
1462 goto do_query;
1464 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1465 if (!centry)
1466 goto do_query;
1468 *num_entries = centry_uint32(centry);
1470 if (*num_entries == 0)
1471 goto do_cached;
1473 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1474 if (! (*info)) {
1475 smb_panic_fn("enum_dom_groups out of memory");
1477 for (i=0; i<(*num_entries); i++) {
1478 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1479 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1480 (*info)[i].rid = centry_uint32(centry);
1483 do_cached:
1484 status = centry->status;
1486 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1487 domain->name, nt_errstr(status) ));
1489 centry_free(centry);
1490 return status;
1492 do_query:
1493 *num_entries = 0;
1494 *info = NULL;
1496 /* Return status value returned by seq number check */
1498 if (!NT_STATUS_IS_OK(domain->last_status))
1499 return domain->last_status;
1501 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1502 domain->name ));
1504 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1506 /* and save it */
1507 refresh_sequence_number(domain, false);
1508 centry = centry_start(domain, status);
1509 if (!centry)
1510 goto skip_save;
1511 centry_put_uint32(centry, *num_entries);
1512 for (i=0; i<(*num_entries); i++) {
1513 centry_put_string(centry, (*info)[i].acct_name);
1514 centry_put_string(centry, (*info)[i].acct_desc);
1515 centry_put_uint32(centry, (*info)[i].rid);
1517 centry_end(centry, "GL/%s/domain", domain->name);
1518 centry_free(centry);
1520 skip_save:
1521 return status;
1524 /* list all domain groups */
1525 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1526 TALLOC_CTX *mem_ctx,
1527 uint32 *num_entries,
1528 struct acct_info **info)
1530 struct winbind_cache *cache = get_cache(domain);
1531 struct cache_entry *centry = NULL;
1532 NTSTATUS status;
1533 unsigned int i;
1535 if (!cache->tdb)
1536 goto do_query;
1538 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1539 if (!centry)
1540 goto do_query;
1542 *num_entries = centry_uint32(centry);
1544 if (*num_entries == 0)
1545 goto do_cached;
1547 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1548 if (! (*info)) {
1549 smb_panic_fn("enum_dom_groups out of memory");
1551 for (i=0; i<(*num_entries); i++) {
1552 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1553 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1554 (*info)[i].rid = centry_uint32(centry);
1557 do_cached:
1559 /* If we are returning cached data and the domain controller
1560 is down then we don't know whether the data is up to date
1561 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1562 indicate this. */
1564 if (wcache_server_down(domain)) {
1565 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1566 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1567 } else
1568 status = centry->status;
1570 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1571 domain->name, nt_errstr(status) ));
1573 centry_free(centry);
1574 return status;
1576 do_query:
1577 *num_entries = 0;
1578 *info = NULL;
1580 /* Return status value returned by seq number check */
1582 if (!NT_STATUS_IS_OK(domain->last_status))
1583 return domain->last_status;
1585 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1586 domain->name ));
1588 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1590 /* and save it */
1591 refresh_sequence_number(domain, false);
1592 centry = centry_start(domain, status);
1593 if (!centry)
1594 goto skip_save;
1595 centry_put_uint32(centry, *num_entries);
1596 for (i=0; i<(*num_entries); i++) {
1597 centry_put_string(centry, (*info)[i].acct_name);
1598 centry_put_string(centry, (*info)[i].acct_desc);
1599 centry_put_uint32(centry, (*info)[i].rid);
1601 centry_end(centry, "GL/%s/local", domain->name);
1602 centry_free(centry);
1604 skip_save:
1605 return status;
1608 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1609 const char *domain_name,
1610 const char *name,
1611 struct dom_sid *sid,
1612 enum lsa_SidType *type)
1614 struct winbind_cache *cache = get_cache(domain);
1615 struct cache_entry *centry;
1616 NTSTATUS status;
1617 char *uname;
1619 if (cache->tdb == NULL) {
1620 return NT_STATUS_NOT_FOUND;
1623 uname = talloc_strdup_upper(talloc_tos(), name);
1624 if (uname == NULL) {
1625 return NT_STATUS_NO_MEMORY;
1628 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1629 TALLOC_FREE(uname);
1630 if (centry == NULL) {
1631 return NT_STATUS_NOT_FOUND;
1634 status = centry->status;
1635 if (NT_STATUS_IS_OK(status)) {
1636 *type = (enum lsa_SidType)centry_uint32(centry);
1637 centry_sid(centry, sid);
1640 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1641 "%s\n", domain->name, nt_errstr(status) ));
1643 centry_free(centry);
1644 return status;
1647 /* convert a single name to a sid in a domain */
1648 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1649 TALLOC_CTX *mem_ctx,
1650 const char *domain_name,
1651 const char *name,
1652 uint32_t flags,
1653 DOM_SID *sid,
1654 enum lsa_SidType *type)
1656 NTSTATUS status;
1658 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1659 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1660 return status;
1663 ZERO_STRUCTP(sid);
1665 /* If the seq number check indicated that there is a problem
1666 * with this DC, then return that status... except for
1667 * access_denied. This is special because the dc may be in
1668 * "restrict anonymous = 1" mode, in which case it will deny
1669 * most unauthenticated operations, but *will* allow the LSA
1670 * name-to-sid that we try as a fallback. */
1672 if (!(NT_STATUS_IS_OK(domain->last_status)
1673 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1674 return domain->last_status;
1676 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1677 domain->name ));
1679 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1680 name, flags, sid, type);
1682 /* and save it */
1683 refresh_sequence_number(domain, false);
1685 if (domain->online &&
1686 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1687 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1689 /* Only save the reverse mapping if this was not a UPN */
1690 if (!strchr(name, '@')) {
1691 strupper_m(CONST_DISCARD(char *,domain_name));
1692 strlower_m(CONST_DISCARD(char *,name));
1693 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1697 return status;
1700 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1701 const struct dom_sid *sid,
1702 TALLOC_CTX *mem_ctx,
1703 char **domain_name,
1704 char **name,
1705 enum lsa_SidType *type)
1707 struct winbind_cache *cache = get_cache(domain);
1708 struct cache_entry *centry;
1709 char *sid_string;
1710 NTSTATUS status;
1712 if (cache->tdb == NULL) {
1713 return NT_STATUS_NOT_FOUND;
1716 sid_string = sid_string_tos(sid);
1717 if (sid_string == NULL) {
1718 return NT_STATUS_NO_MEMORY;
1721 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1722 TALLOC_FREE(sid_string);
1723 if (centry == NULL) {
1724 return NT_STATUS_NOT_FOUND;
1727 if (NT_STATUS_IS_OK(centry->status)) {
1728 *type = (enum lsa_SidType)centry_uint32(centry);
1729 *domain_name = centry_string(centry, mem_ctx);
1730 *name = centry_string(centry, mem_ctx);
1733 status = centry->status;
1734 centry_free(centry);
1736 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1737 "%s\n", domain->name, nt_errstr(status) ));
1739 return status;
1742 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1743 given */
1744 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1745 TALLOC_CTX *mem_ctx,
1746 const DOM_SID *sid,
1747 char **domain_name,
1748 char **name,
1749 enum lsa_SidType *type)
1751 NTSTATUS status;
1753 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1754 type);
1755 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1756 return status;
1759 *name = NULL;
1760 *domain_name = NULL;
1762 /* If the seq number check indicated that there is a problem
1763 * with this DC, then return that status... except for
1764 * access_denied. This is special because the dc may be in
1765 * "restrict anonymous = 1" mode, in which case it will deny
1766 * most unauthenticated operations, but *will* allow the LSA
1767 * sid-to-name that we try as a fallback. */
1769 if (!(NT_STATUS_IS_OK(domain->last_status)
1770 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1771 return domain->last_status;
1773 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1774 domain->name ));
1776 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1778 /* and save it */
1779 refresh_sequence_number(domain, false);
1780 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1782 /* We can't save the name to sid mapping here, as with sid history a
1783 * later name2sid would give the wrong sid. */
1785 return status;
1788 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1789 TALLOC_CTX *mem_ctx,
1790 const DOM_SID *domain_sid,
1791 uint32 *rids,
1792 size_t num_rids,
1793 char **domain_name,
1794 char ***names,
1795 enum lsa_SidType **types)
1797 struct winbind_cache *cache = get_cache(domain);
1798 size_t i;
1799 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1800 bool have_mapped;
1801 bool have_unmapped;
1803 *domain_name = NULL;
1804 *names = NULL;
1805 *types = NULL;
1807 if (!cache->tdb) {
1808 goto do_query;
1811 if (num_rids == 0) {
1812 return NT_STATUS_OK;
1815 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1816 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1818 if ((*names == NULL) || (*types == NULL)) {
1819 result = NT_STATUS_NO_MEMORY;
1820 goto error;
1823 have_mapped = have_unmapped = false;
1825 for (i=0; i<num_rids; i++) {
1826 DOM_SID sid;
1827 struct cache_entry *centry;
1828 fstring tmp;
1830 if (!sid_compose(&sid, domain_sid, rids[i])) {
1831 result = NT_STATUS_INTERNAL_ERROR;
1832 goto error;
1835 centry = wcache_fetch(cache, domain, "SN/%s",
1836 sid_to_fstring(tmp, &sid));
1837 if (!centry) {
1838 goto do_query;
1841 (*types)[i] = SID_NAME_UNKNOWN;
1842 (*names)[i] = talloc_strdup(*names, "");
1844 if (NT_STATUS_IS_OK(centry->status)) {
1845 char *dom;
1846 have_mapped = true;
1847 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1849 dom = centry_string(centry, mem_ctx);
1850 if (*domain_name == NULL) {
1851 *domain_name = dom;
1852 } else {
1853 talloc_free(dom);
1856 (*names)[i] = centry_string(centry, *names);
1858 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1859 have_unmapped = true;
1861 } else {
1862 /* something's definitely wrong */
1863 result = centry->status;
1864 goto error;
1867 centry_free(centry);
1870 if (!have_mapped) {
1871 return NT_STATUS_NONE_MAPPED;
1873 if (!have_unmapped) {
1874 return NT_STATUS_OK;
1876 return STATUS_SOME_UNMAPPED;
1878 do_query:
1880 TALLOC_FREE(*names);
1881 TALLOC_FREE(*types);
1883 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1884 rids, num_rids, domain_name,
1885 names, types);
1888 None of the queried rids has been found so save all negative entries
1890 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1891 for (i = 0; i < num_rids; i++) {
1892 DOM_SID sid;
1893 const char *name = "";
1894 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1895 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1897 if (!sid_compose(&sid, domain_sid, rids[i])) {
1898 return NT_STATUS_INTERNAL_ERROR;
1901 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1902 name, type);
1905 return result;
1909 Some or all of the queried rids have been found.
1911 if (!NT_STATUS_IS_OK(result) &&
1912 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1913 return result;
1916 refresh_sequence_number(domain, false);
1918 for (i=0; i<num_rids; i++) {
1919 DOM_SID sid;
1920 NTSTATUS status;
1922 if (!sid_compose(&sid, domain_sid, rids[i])) {
1923 result = NT_STATUS_INTERNAL_ERROR;
1924 goto error;
1927 status = (*types)[i] == SID_NAME_UNKNOWN ?
1928 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1930 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1931 (*names)[i], (*types)[i]);
1934 return result;
1936 error:
1937 TALLOC_FREE(*names);
1938 TALLOC_FREE(*types);
1939 return result;
1942 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
1943 TALLOC_CTX *mem_ctx,
1944 const struct dom_sid *user_sid,
1945 struct winbind_userinfo *info)
1947 struct winbind_cache *cache = get_cache(domain);
1948 struct cache_entry *centry = NULL;
1949 NTSTATUS status;
1950 char *sid_string;
1952 if (cache->tdb == NULL) {
1953 return NT_STATUS_NOT_FOUND;
1956 sid_string = sid_string_tos(user_sid);
1957 if (sid_string == NULL) {
1958 return NT_STATUS_NO_MEMORY;
1961 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
1962 TALLOC_FREE(sid_string);
1963 if (centry == NULL) {
1964 return NT_STATUS_NOT_FOUND;
1968 * If we have an access denied cache entry and a cached info3
1969 * in the samlogon cache then do a query. This will force the
1970 * rpc back end to return the info3 data.
1973 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
1974 netsamlogon_cache_have(user_sid)) {
1975 DEBUG(10, ("query_user: cached access denied and have cached "
1976 "info3\n"));
1977 domain->last_status = NT_STATUS_OK;
1978 centry_free(centry);
1979 return NT_STATUS_NOT_FOUND;
1982 /* if status is not ok then this is a negative hit
1983 and the rest of the data doesn't matter */
1984 status = centry->status;
1985 if (NT_STATUS_IS_OK(status)) {
1986 info->acct_name = centry_string(centry, mem_ctx);
1987 info->full_name = centry_string(centry, mem_ctx);
1988 info->homedir = centry_string(centry, mem_ctx);
1989 info->shell = centry_string(centry, mem_ctx);
1990 info->primary_gid = centry_uint32(centry);
1991 centry_sid(centry, &info->user_sid);
1992 centry_sid(centry, &info->group_sid);
1995 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
1996 "%s\n", domain->name, nt_errstr(status) ));
1998 centry_free(centry);
1999 return status;
2002 /* Lookup user information from a rid */
2003 static NTSTATUS query_user(struct winbindd_domain *domain,
2004 TALLOC_CTX *mem_ctx,
2005 const DOM_SID *user_sid,
2006 WINBIND_USERINFO *info)
2008 NTSTATUS status;
2010 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2011 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2012 return status;
2015 ZERO_STRUCTP(info);
2017 /* Return status value returned by seq number check */
2019 if (!NT_STATUS_IS_OK(domain->last_status))
2020 return domain->last_status;
2022 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2023 domain->name ));
2025 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2027 /* and save it */
2028 refresh_sequence_number(domain, false);
2029 wcache_save_user(domain, status, info);
2031 return status;
2035 /* Lookup groups a user is a member of. */
2036 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2037 TALLOC_CTX *mem_ctx,
2038 const DOM_SID *user_sid,
2039 uint32 *num_groups, DOM_SID **user_gids)
2041 struct winbind_cache *cache = get_cache(domain);
2042 struct cache_entry *centry = NULL;
2043 NTSTATUS status;
2044 unsigned int i;
2045 fstring sid_string;
2047 if (!cache->tdb)
2048 goto do_query;
2050 centry = wcache_fetch(cache, domain, "UG/%s",
2051 sid_to_fstring(sid_string, user_sid));
2053 /* If we have an access denied cache entry and a cached info3 in the
2054 samlogon cache then do a query. This will force the rpc back end
2055 to return the info3 data. */
2057 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
2058 netsamlogon_cache_have(user_sid)) {
2059 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
2060 domain->last_status = NT_STATUS_OK;
2061 centry_free(centry);
2062 goto do_query;
2065 if (!centry)
2066 goto do_query;
2068 *num_groups = centry_uint32(centry);
2070 if (*num_groups == 0)
2071 goto do_cached;
2073 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2074 if (! (*user_gids)) {
2075 smb_panic_fn("lookup_usergroups out of memory");
2077 for (i=0; i<(*num_groups); i++) {
2078 centry_sid(centry, &(*user_gids)[i]);
2081 do_cached:
2082 status = centry->status;
2084 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2085 domain->name, nt_errstr(status) ));
2087 centry_free(centry);
2088 return status;
2090 do_query:
2091 (*num_groups) = 0;
2092 (*user_gids) = NULL;
2094 /* Return status value returned by seq number check */
2096 if (!NT_STATUS_IS_OK(domain->last_status))
2097 return domain->last_status;
2099 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2100 domain->name ));
2102 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2104 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2105 goto skip_save;
2107 /* and save it */
2108 refresh_sequence_number(domain, false);
2109 centry = centry_start(domain, status);
2110 if (!centry)
2111 goto skip_save;
2113 centry_put_uint32(centry, *num_groups);
2114 for (i=0; i<(*num_groups); i++) {
2115 centry_put_sid(centry, &(*user_gids)[i]);
2118 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2119 centry_free(centry);
2121 skip_save:
2122 return status;
2125 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2126 const struct dom_sid *sids)
2128 uint32_t i;
2129 char *sidlist;
2131 sidlist = talloc_strdup(mem_ctx, "");
2132 if (sidlist == NULL) {
2133 return NULL;
2135 for (i=0; i<num_sids; i++) {
2136 fstring tmp;
2137 sidlist = talloc_asprintf_append_buffer(
2138 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2139 if (sidlist == NULL) {
2140 return NULL;
2143 return sidlist;
2146 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2147 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2148 const struct dom_sid *sids,
2149 uint32_t *pnum_aliases, uint32_t **paliases)
2151 struct winbind_cache *cache = get_cache(domain);
2152 struct cache_entry *centry = NULL;
2153 uint32_t num_aliases;
2154 uint32_t *aliases;
2155 NTSTATUS status;
2156 char *sidlist;
2157 int i;
2159 if (cache->tdb == NULL) {
2160 return NT_STATUS_NOT_FOUND;
2163 if (num_sids == 0) {
2164 *pnum_aliases = 0;
2165 *paliases = NULL;
2166 return NT_STATUS_OK;
2169 /* We need to cache indexed by the whole list of SIDs, the aliases
2170 * resulting might come from any of the SIDs. */
2172 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2173 if (sidlist == NULL) {
2174 return NT_STATUS_NO_MEMORY;
2177 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2178 TALLOC_FREE(sidlist);
2179 if (centry == NULL) {
2180 return NT_STATUS_NOT_FOUND;
2183 num_aliases = centry_uint32(centry);
2184 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2185 if (aliases == NULL) {
2186 centry_free(centry);
2187 return NT_STATUS_NO_MEMORY;
2190 for (i=0; i<num_aliases; i++) {
2191 aliases[i] = centry_uint32(centry);
2194 status = centry->status;
2196 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2197 "status %s\n", domain->name, nt_errstr(status)));
2199 centry_free(centry);
2201 *pnum_aliases = num_aliases;
2202 *paliases = aliases;
2204 return status;
2207 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2208 TALLOC_CTX *mem_ctx,
2209 uint32 num_sids, const DOM_SID *sids,
2210 uint32 *num_aliases, uint32 **alias_rids)
2212 struct cache_entry *centry = NULL;
2213 NTSTATUS status;
2214 char *sidlist;
2215 int i;
2217 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2218 num_aliases, alias_rids);
2219 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2220 return status;
2223 (*num_aliases) = 0;
2224 (*alias_rids) = NULL;
2226 if (!NT_STATUS_IS_OK(domain->last_status))
2227 return domain->last_status;
2229 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2230 "for domain %s\n", domain->name ));
2232 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2233 if (sidlist == NULL) {
2234 return NT_STATUS_NO_MEMORY;
2237 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2238 num_sids, sids,
2239 num_aliases, alias_rids);
2241 /* and save it */
2242 refresh_sequence_number(domain, false);
2243 centry = centry_start(domain, status);
2244 if (!centry)
2245 goto skip_save;
2246 centry_put_uint32(centry, *num_aliases);
2247 for (i=0; i<(*num_aliases); i++)
2248 centry_put_uint32(centry, (*alias_rids)[i]);
2249 centry_end(centry, "UA%s", sidlist);
2250 centry_free(centry);
2252 skip_save:
2253 return status;
2257 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2258 TALLOC_CTX *mem_ctx,
2259 const DOM_SID *group_sid, uint32 *num_names,
2260 DOM_SID **sid_mem, char ***names,
2261 uint32 **name_types)
2263 struct winbind_cache *cache = get_cache(domain);
2264 struct cache_entry *centry = NULL;
2265 NTSTATUS status;
2266 unsigned int i;
2267 fstring sid_string;
2269 if (!cache->tdb)
2270 goto do_query;
2272 centry = wcache_fetch(cache, domain, "GM/%s",
2273 sid_to_fstring(sid_string, group_sid));
2274 if (!centry)
2275 goto do_query;
2277 *num_names = centry_uint32(centry);
2279 if (*num_names == 0)
2280 goto do_cached;
2282 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2283 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2284 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2286 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2287 smb_panic_fn("lookup_groupmem out of memory");
2290 for (i=0; i<(*num_names); i++) {
2291 centry_sid(centry, &(*sid_mem)[i]);
2292 (*names)[i] = centry_string(centry, mem_ctx);
2293 (*name_types)[i] = centry_uint32(centry);
2296 do_cached:
2297 status = centry->status;
2299 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2300 domain->name, nt_errstr(status)));
2302 centry_free(centry);
2303 return status;
2305 do_query:
2306 (*num_names) = 0;
2307 (*sid_mem) = NULL;
2308 (*names) = NULL;
2309 (*name_types) = NULL;
2311 /* Return status value returned by seq number check */
2313 if (!NT_STATUS_IS_OK(domain->last_status))
2314 return domain->last_status;
2316 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2317 domain->name ));
2319 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2320 sid_mem, names, name_types);
2322 /* and save it */
2323 refresh_sequence_number(domain, false);
2324 centry = centry_start(domain, status);
2325 if (!centry)
2326 goto skip_save;
2327 centry_put_uint32(centry, *num_names);
2328 for (i=0; i<(*num_names); i++) {
2329 centry_put_sid(centry, &(*sid_mem)[i]);
2330 centry_put_string(centry, (*names)[i]);
2331 centry_put_uint32(centry, (*name_types)[i]);
2333 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2334 centry_free(centry);
2336 skip_save:
2337 return status;
2340 /* find the sequence number for a domain */
2341 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2343 refresh_sequence_number(domain, false);
2345 *seq = domain->sequence_number;
2347 return NT_STATUS_OK;
2350 /* enumerate trusted domains
2351 * (we need to have the list of trustdoms in the cache when we go offline) -
2352 * Guenther */
2353 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2354 TALLOC_CTX *mem_ctx,
2355 uint32 *num_domains,
2356 char ***names,
2357 char ***alt_names,
2358 DOM_SID **dom_sids)
2360 struct winbind_cache *cache = get_cache(domain);
2361 struct cache_entry *centry = NULL;
2362 NTSTATUS status;
2363 int i;
2365 if (!cache->tdb)
2366 goto do_query;
2368 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2370 if (!centry) {
2371 goto do_query;
2374 *num_domains = centry_uint32(centry);
2376 if (*num_domains) {
2377 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2378 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2379 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2381 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2382 smb_panic_fn("trusted_domains out of memory");
2384 } else {
2385 (*names) = NULL;
2386 (*alt_names) = NULL;
2387 (*dom_sids) = NULL;
2390 for (i=0; i<(*num_domains); i++) {
2391 (*names)[i] = centry_string(centry, mem_ctx);
2392 (*alt_names)[i] = centry_string(centry, mem_ctx);
2393 if (!centry_sid(centry, &(*dom_sids)[i])) {
2394 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2398 status = centry->status;
2400 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2401 domain->name, *num_domains, nt_errstr(status) ));
2403 centry_free(centry);
2404 return status;
2406 do_query:
2407 (*num_domains) = 0;
2408 (*dom_sids) = NULL;
2409 (*names) = NULL;
2410 (*alt_names) = NULL;
2412 /* Return status value returned by seq number check */
2414 if (!NT_STATUS_IS_OK(domain->last_status))
2415 return domain->last_status;
2417 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2418 domain->name ));
2420 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2421 names, alt_names, dom_sids);
2423 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2424 * so that the generic centry handling still applies correctly -
2425 * Guenther*/
2427 if (!NT_STATUS_IS_ERR(status)) {
2428 status = NT_STATUS_OK;
2432 #if 0 /* Disabled as we want the trust dom list to be managed by
2433 the main parent and always to make the query. --jerry */
2435 /* and save it */
2436 refresh_sequence_number(domain, false);
2438 centry = centry_start(domain, status);
2439 if (!centry)
2440 goto skip_save;
2442 centry_put_uint32(centry, *num_domains);
2444 for (i=0; i<(*num_domains); i++) {
2445 centry_put_string(centry, (*names)[i]);
2446 centry_put_string(centry, (*alt_names)[i]);
2447 centry_put_sid(centry, &(*dom_sids)[i]);
2450 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2452 centry_free(centry);
2454 skip_save:
2455 #endif
2457 return status;
2460 /* get lockout policy */
2461 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2462 TALLOC_CTX *mem_ctx,
2463 struct samr_DomInfo12 *policy)
2465 struct winbind_cache *cache = get_cache(domain);
2466 struct cache_entry *centry = NULL;
2467 NTSTATUS status;
2469 if (!cache->tdb)
2470 goto do_query;
2472 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2474 if (!centry)
2475 goto do_query;
2477 policy->lockout_duration = centry_nttime(centry);
2478 policy->lockout_window = centry_nttime(centry);
2479 policy->lockout_threshold = centry_uint16(centry);
2481 status = centry->status;
2483 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2484 domain->name, nt_errstr(status) ));
2486 centry_free(centry);
2487 return status;
2489 do_query:
2490 ZERO_STRUCTP(policy);
2492 /* Return status value returned by seq number check */
2494 if (!NT_STATUS_IS_OK(domain->last_status))
2495 return domain->last_status;
2497 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2498 domain->name ));
2500 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2502 /* and save it */
2503 refresh_sequence_number(domain, false);
2504 wcache_save_lockout_policy(domain, status, policy);
2506 return status;
2509 /* get password policy */
2510 static NTSTATUS password_policy(struct winbindd_domain *domain,
2511 TALLOC_CTX *mem_ctx,
2512 struct samr_DomInfo1 *policy)
2514 struct winbind_cache *cache = get_cache(domain);
2515 struct cache_entry *centry = NULL;
2516 NTSTATUS status;
2518 if (!cache->tdb)
2519 goto do_query;
2521 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2523 if (!centry)
2524 goto do_query;
2526 policy->min_password_length = centry_uint16(centry);
2527 policy->password_history_length = centry_uint16(centry);
2528 policy->password_properties = centry_uint32(centry);
2529 policy->max_password_age = centry_nttime(centry);
2530 policy->min_password_age = centry_nttime(centry);
2532 status = centry->status;
2534 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2535 domain->name, nt_errstr(status) ));
2537 centry_free(centry);
2538 return status;
2540 do_query:
2541 ZERO_STRUCTP(policy);
2543 /* Return status value returned by seq number check */
2545 if (!NT_STATUS_IS_OK(domain->last_status))
2546 return domain->last_status;
2548 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2549 domain->name ));
2551 status = domain->backend->password_policy(domain, mem_ctx, policy);
2553 /* and save it */
2554 refresh_sequence_number(domain, false);
2555 if (NT_STATUS_IS_OK(status)) {
2556 wcache_save_password_policy(domain, status, policy);
2559 return status;
2563 /* Invalidate cached user and group lists coherently */
2565 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2566 void *state)
2568 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2569 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2570 tdb_delete(the_tdb, kbuf);
2572 return 0;
2575 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2577 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2578 struct netr_SamInfo3 *info3)
2580 DOM_SID sid;
2581 fstring key_str, sid_string;
2582 struct winbind_cache *cache;
2584 /* dont clear cached U/SID and UG/SID entries when we want to logon
2585 * offline - gd */
2587 if (lp_winbind_offline_logon()) {
2588 return;
2591 if (!domain)
2592 return;
2594 cache = get_cache(domain);
2596 if (!cache->tdb) {
2597 return;
2600 sid_copy(&sid, info3->base.domain_sid);
2601 sid_append_rid(&sid, info3->base.rid);
2603 /* Clear U/SID cache entry */
2604 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2605 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2606 tdb_delete(cache->tdb, string_tdb_data(key_str));
2608 /* Clear UG/SID cache entry */
2609 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2610 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2611 tdb_delete(cache->tdb, string_tdb_data(key_str));
2613 /* Samba/winbindd never needs this. */
2614 netsamlogon_clear_cached_user(info3);
2617 bool wcache_invalidate_cache(void)
2619 struct winbindd_domain *domain;
2621 for (domain = domain_list(); domain; domain = domain->next) {
2622 struct winbind_cache *cache = get_cache(domain);
2624 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2625 "entries for %s\n", domain->name));
2626 if (cache) {
2627 if (cache->tdb) {
2628 tdb_traverse(cache->tdb, traverse_fn, NULL);
2629 } else {
2630 return false;
2634 return true;
2637 bool init_wcache(void)
2639 if (wcache == NULL) {
2640 wcache = SMB_XMALLOC_P(struct winbind_cache);
2641 ZERO_STRUCTP(wcache);
2644 if (wcache->tdb != NULL)
2645 return true;
2647 /* when working offline we must not clear the cache on restart */
2648 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2649 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2650 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2651 O_RDWR|O_CREAT, 0600);
2653 if (wcache->tdb == NULL) {
2654 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2655 return false;
2658 return true;
2661 /************************************************************************
2662 This is called by the parent to initialize the cache file.
2663 We don't need sophisticated locking here as we know we're the
2664 only opener.
2665 ************************************************************************/
2667 bool initialize_winbindd_cache(void)
2669 bool cache_bad = true;
2670 uint32 vers;
2672 if (!init_wcache()) {
2673 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2674 return false;
2677 /* Check version number. */
2678 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2679 vers == WINBINDD_CACHE_VERSION) {
2680 cache_bad = false;
2683 if (cache_bad) {
2684 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2685 "and re-creating with version number %d\n",
2686 WINBINDD_CACHE_VERSION ));
2688 tdb_close(wcache->tdb);
2689 wcache->tdb = NULL;
2691 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2692 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2693 cache_path("winbindd_cache.tdb"),
2694 strerror(errno) ));
2695 return false;
2697 if (!init_wcache()) {
2698 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2699 "init_wcache failed.\n"));
2700 return false;
2703 /* Write the version. */
2704 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2705 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2706 tdb_errorstr(wcache->tdb) ));
2707 return false;
2711 tdb_close(wcache->tdb);
2712 wcache->tdb = NULL;
2713 return true;
2716 void close_winbindd_cache(void)
2718 if (!wcache) {
2719 return;
2721 if (wcache->tdb) {
2722 tdb_close(wcache->tdb);
2723 wcache->tdb = NULL;
2727 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2728 char **domain_name, char **name,
2729 enum lsa_SidType *type)
2731 struct winbindd_domain *domain;
2732 NTSTATUS status;
2734 domain = find_lookup_domain_from_sid(sid);
2735 if (domain == NULL) {
2736 return false;
2738 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
2739 type);
2740 return NT_STATUS_IS_OK(status);
2743 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2744 const char *domain_name,
2745 const char *name,
2746 DOM_SID *sid,
2747 enum lsa_SidType *type)
2749 struct winbindd_domain *domain;
2750 NTSTATUS status;
2751 bool original_online_state;
2753 domain = find_lookup_domain_from_name(domain_name);
2754 if (domain == NULL) {
2755 return false;
2758 /* If we are doing a cached logon, temporarily set the domain
2759 offline so the cache won't expire the entry */
2761 original_online_state = domain->online;
2762 domain->online = false;
2763 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
2764 domain->online = original_online_state;
2766 return NT_STATUS_IS_OK(status);
2769 void cache_name2sid(struct winbindd_domain *domain,
2770 const char *domain_name, const char *name,
2771 enum lsa_SidType type, const DOM_SID *sid)
2773 refresh_sequence_number(domain, false);
2774 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2775 sid, type);
2779 * The original idea that this cache only contains centries has
2780 * been blurred - now other stuff gets put in here. Ensure we
2781 * ignore these things on cleanup.
2784 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2785 TDB_DATA dbuf, void *state)
2787 struct cache_entry *centry;
2789 if (is_non_centry_key(kbuf)) {
2790 return 0;
2793 centry = wcache_fetch_raw((char *)kbuf.dptr);
2794 if (!centry) {
2795 return 0;
2798 if (!NT_STATUS_IS_OK(centry->status)) {
2799 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2800 tdb_delete(the_tdb, kbuf);
2803 centry_free(centry);
2804 return 0;
2807 /* flush the cache */
2808 void wcache_flush_cache(void)
2810 if (!wcache)
2811 return;
2812 if (wcache->tdb) {
2813 tdb_close(wcache->tdb);
2814 wcache->tdb = NULL;
2816 if (!winbindd_use_cache()) {
2817 return;
2820 /* when working offline we must not clear the cache on restart */
2821 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2822 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2823 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2824 O_RDWR|O_CREAT, 0600);
2826 if (!wcache->tdb) {
2827 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2828 return;
2831 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2833 DEBUG(10,("wcache_flush_cache success\n"));
2836 /* Count cached creds */
2838 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2839 void *state)
2841 int *cred_count = (int*)state;
2843 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2844 (*cred_count)++;
2846 return 0;
2849 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2851 struct winbind_cache *cache = get_cache(domain);
2853 *count = 0;
2855 if (!cache->tdb) {
2856 return NT_STATUS_INTERNAL_DB_ERROR;
2859 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2861 return NT_STATUS_OK;
2864 struct cred_list {
2865 struct cred_list *prev, *next;
2866 TDB_DATA key;
2867 fstring name;
2868 time_t created;
2870 static struct cred_list *wcache_cred_list;
2872 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2873 void *state)
2875 struct cred_list *cred;
2877 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2879 cred = SMB_MALLOC_P(struct cred_list);
2880 if (cred == NULL) {
2881 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2882 return -1;
2885 ZERO_STRUCTP(cred);
2887 /* save a copy of the key */
2889 fstrcpy(cred->name, (const char *)kbuf.dptr);
2890 DLIST_ADD(wcache_cred_list, cred);
2893 return 0;
2896 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2898 struct winbind_cache *cache = get_cache(domain);
2899 NTSTATUS status;
2900 int ret;
2901 struct cred_list *cred, *oldest = NULL;
2903 if (!cache->tdb) {
2904 return NT_STATUS_INTERNAL_DB_ERROR;
2907 /* we possibly already have an entry */
2908 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2910 fstring key_str, tmp;
2912 DEBUG(11,("we already have an entry, deleting that\n"));
2914 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2916 tdb_delete(cache->tdb, string_tdb_data(key_str));
2918 return NT_STATUS_OK;
2921 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2922 if (ret == 0) {
2923 return NT_STATUS_OK;
2924 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2925 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2928 ZERO_STRUCTP(oldest);
2930 for (cred = wcache_cred_list; cred; cred = cred->next) {
2932 TDB_DATA data;
2933 time_t t;
2935 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2936 if (!data.dptr) {
2937 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2938 cred->name));
2939 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2940 goto done;
2943 t = IVAL(data.dptr, 0);
2944 SAFE_FREE(data.dptr);
2946 if (!oldest) {
2947 oldest = SMB_MALLOC_P(struct cred_list);
2948 if (oldest == NULL) {
2949 status = NT_STATUS_NO_MEMORY;
2950 goto done;
2953 fstrcpy(oldest->name, cred->name);
2954 oldest->created = t;
2955 continue;
2958 if (t < oldest->created) {
2959 fstrcpy(oldest->name, cred->name);
2960 oldest->created = t;
2964 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2965 status = NT_STATUS_OK;
2966 } else {
2967 status = NT_STATUS_UNSUCCESSFUL;
2969 done:
2970 SAFE_FREE(wcache_cred_list);
2971 SAFE_FREE(oldest);
2973 return status;
2976 /* Change the global online/offline state. */
2977 bool set_global_winbindd_state_offline(void)
2979 TDB_DATA data;
2981 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2983 /* Only go offline if someone has created
2984 the key "WINBINDD_OFFLINE" in the cache tdb. */
2986 if (wcache == NULL || wcache->tdb == NULL) {
2987 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2988 return false;
2991 if (!lp_winbind_offline_logon()) {
2992 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2993 return false;
2996 if (global_winbindd_offline_state) {
2997 /* Already offline. */
2998 return true;
3001 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3003 if (!data.dptr || data.dsize != 4) {
3004 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3005 SAFE_FREE(data.dptr);
3006 return false;
3007 } else {
3008 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3009 global_winbindd_offline_state = true;
3010 SAFE_FREE(data.dptr);
3011 return true;
3015 void set_global_winbindd_state_online(void)
3017 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3019 if (!lp_winbind_offline_logon()) {
3020 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3021 return;
3024 if (!global_winbindd_offline_state) {
3025 /* Already online. */
3026 return;
3028 global_winbindd_offline_state = false;
3030 if (!wcache->tdb) {
3031 return;
3034 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3035 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3038 bool get_global_winbindd_state_offline(void)
3040 return global_winbindd_offline_state;
3043 /***********************************************************************
3044 Validate functions for all possible cache tdb keys.
3045 ***********************************************************************/
3047 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3048 struct tdb_validation_status *state)
3050 struct cache_entry *centry;
3052 centry = SMB_XMALLOC_P(struct cache_entry);
3053 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3054 if (!centry->data) {
3055 SAFE_FREE(centry);
3056 return NULL;
3058 centry->len = data.dsize;
3059 centry->ofs = 0;
3061 if (centry->len < 8) {
3062 /* huh? corrupt cache? */
3063 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3064 centry_free(centry);
3065 state->bad_entry = true;
3066 state->success = false;
3067 return NULL;
3070 centry->status = NT_STATUS(centry_uint32(centry));
3071 centry->sequence_number = centry_uint32(centry);
3072 return centry;
3075 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3076 struct tdb_validation_status *state)
3078 if (dbuf.dsize != 8) {
3079 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3080 keystr, (unsigned int)dbuf.dsize ));
3081 state->bad_entry = true;
3082 return 1;
3084 return 0;
3087 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3088 struct tdb_validation_status *state)
3090 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3091 if (!centry) {
3092 return 1;
3095 (void)centry_uint32(centry);
3096 if (NT_STATUS_IS_OK(centry->status)) {
3097 DOM_SID sid;
3098 (void)centry_sid(centry, &sid);
3101 centry_free(centry);
3103 if (!(state->success)) {
3104 return 1;
3106 DEBUG(10,("validate_ns: %s ok\n", keystr));
3107 return 0;
3110 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3111 struct tdb_validation_status *state)
3113 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3114 if (!centry) {
3115 return 1;
3118 if (NT_STATUS_IS_OK(centry->status)) {
3119 (void)centry_uint32(centry);
3120 (void)centry_string(centry, mem_ctx);
3121 (void)centry_string(centry, mem_ctx);
3124 centry_free(centry);
3126 if (!(state->success)) {
3127 return 1;
3129 DEBUG(10,("validate_sn: %s ok\n", keystr));
3130 return 0;
3133 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3134 struct tdb_validation_status *state)
3136 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3137 DOM_SID sid;
3139 if (!centry) {
3140 return 1;
3143 (void)centry_string(centry, mem_ctx);
3144 (void)centry_string(centry, mem_ctx);
3145 (void)centry_string(centry, mem_ctx);
3146 (void)centry_string(centry, mem_ctx);
3147 (void)centry_uint32(centry);
3148 (void)centry_sid(centry, &sid);
3149 (void)centry_sid(centry, &sid);
3151 centry_free(centry);
3153 if (!(state->success)) {
3154 return 1;
3156 DEBUG(10,("validate_u: %s ok\n", keystr));
3157 return 0;
3160 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3161 struct tdb_validation_status *state)
3163 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3165 if (!centry) {
3166 return 1;
3169 (void)centry_nttime(centry);
3170 (void)centry_nttime(centry);
3171 (void)centry_uint16(centry);
3173 centry_free(centry);
3175 if (!(state->success)) {
3176 return 1;
3178 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3179 return 0;
3182 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3183 struct tdb_validation_status *state)
3185 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3187 if (!centry) {
3188 return 1;
3191 (void)centry_uint16(centry);
3192 (void)centry_uint16(centry);
3193 (void)centry_uint32(centry);
3194 (void)centry_nttime(centry);
3195 (void)centry_nttime(centry);
3197 centry_free(centry);
3199 if (!(state->success)) {
3200 return 1;
3202 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3203 return 0;
3206 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3207 struct tdb_validation_status *state)
3209 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3211 if (!centry) {
3212 return 1;
3215 (void)centry_time(centry);
3216 (void)centry_hash16(centry, mem_ctx);
3218 /* We only have 17 bytes more data in the salted cred case. */
3219 if (centry->len - centry->ofs == 17) {
3220 (void)centry_hash16(centry, mem_ctx);
3223 centry_free(centry);
3225 if (!(state->success)) {
3226 return 1;
3228 DEBUG(10,("validate_cred: %s ok\n", keystr));
3229 return 0;
3232 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3233 struct tdb_validation_status *state)
3235 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3236 int32 num_entries, i;
3238 if (!centry) {
3239 return 1;
3242 num_entries = (int32)centry_uint32(centry);
3244 for (i=0; i< num_entries; i++) {
3245 DOM_SID sid;
3246 (void)centry_string(centry, mem_ctx);
3247 (void)centry_string(centry, mem_ctx);
3248 (void)centry_string(centry, mem_ctx);
3249 (void)centry_string(centry, mem_ctx);
3250 (void)centry_sid(centry, &sid);
3251 (void)centry_sid(centry, &sid);
3254 centry_free(centry);
3256 if (!(state->success)) {
3257 return 1;
3259 DEBUG(10,("validate_ul: %s ok\n", keystr));
3260 return 0;
3263 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3264 struct tdb_validation_status *state)
3266 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3267 int32 num_entries, i;
3269 if (!centry) {
3270 return 1;
3273 num_entries = centry_uint32(centry);
3275 for (i=0; i< num_entries; i++) {
3276 (void)centry_string(centry, mem_ctx);
3277 (void)centry_string(centry, mem_ctx);
3278 (void)centry_uint32(centry);
3281 centry_free(centry);
3283 if (!(state->success)) {
3284 return 1;
3286 DEBUG(10,("validate_gl: %s ok\n", keystr));
3287 return 0;
3290 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3291 struct tdb_validation_status *state)
3293 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3294 int32 num_groups, i;
3296 if (!centry) {
3297 return 1;
3300 num_groups = centry_uint32(centry);
3302 for (i=0; i< num_groups; i++) {
3303 DOM_SID sid;
3304 centry_sid(centry, &sid);
3307 centry_free(centry);
3309 if (!(state->success)) {
3310 return 1;
3312 DEBUG(10,("validate_ug: %s ok\n", keystr));
3313 return 0;
3316 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3317 struct tdb_validation_status *state)
3319 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3320 int32 num_aliases, i;
3322 if (!centry) {
3323 return 1;
3326 num_aliases = centry_uint32(centry);
3328 for (i=0; i < num_aliases; i++) {
3329 (void)centry_uint32(centry);
3332 centry_free(centry);
3334 if (!(state->success)) {
3335 return 1;
3337 DEBUG(10,("validate_ua: %s ok\n", keystr));
3338 return 0;
3341 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3342 struct tdb_validation_status *state)
3344 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3345 int32 num_names, i;
3347 if (!centry) {
3348 return 1;
3351 num_names = centry_uint32(centry);
3353 for (i=0; i< num_names; i++) {
3354 DOM_SID sid;
3355 centry_sid(centry, &sid);
3356 (void)centry_string(centry, mem_ctx);
3357 (void)centry_uint32(centry);
3360 centry_free(centry);
3362 if (!(state->success)) {
3363 return 1;
3365 DEBUG(10,("validate_gm: %s ok\n", keystr));
3366 return 0;
3369 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3370 struct tdb_validation_status *state)
3372 /* Can't say anything about this other than must be nonzero. */
3373 if (dbuf.dsize == 0) {
3374 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3375 keystr));
3376 state->bad_entry = true;
3377 state->success = false;
3378 return 1;
3381 DEBUG(10,("validate_dr: %s ok\n", keystr));
3382 return 0;
3385 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3386 struct tdb_validation_status *state)
3388 /* Can't say anything about this other than must be nonzero. */
3389 if (dbuf.dsize == 0) {
3390 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3391 keystr));
3392 state->bad_entry = true;
3393 state->success = false;
3394 return 1;
3397 DEBUG(10,("validate_de: %s ok\n", keystr));
3398 return 0;
3401 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3402 TDB_DATA dbuf, struct tdb_validation_status *state)
3404 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3406 if (!centry) {
3407 return 1;
3410 (void)centry_string(centry, mem_ctx);
3411 (void)centry_string(centry, mem_ctx);
3412 (void)centry_string(centry, mem_ctx);
3413 (void)centry_uint32(centry);
3415 centry_free(centry);
3417 if (!(state->success)) {
3418 return 1;
3420 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3421 return 0;
3424 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3425 TDB_DATA dbuf,
3426 struct tdb_validation_status *state)
3428 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3430 if (!centry) {
3431 return 1;
3434 (void)centry_string( centry, mem_ctx );
3436 centry_free(centry);
3438 if (!(state->success)) {
3439 return 1;
3441 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3442 return 0;
3445 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3446 TDB_DATA dbuf,
3447 struct tdb_validation_status *state)
3449 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3451 if (!centry) {
3452 return 1;
3455 (void)centry_string( centry, mem_ctx );
3457 centry_free(centry);
3459 if (!(state->success)) {
3460 return 1;
3462 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3463 return 0;
3466 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3467 struct tdb_validation_status *state)
3469 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3470 int32 num_domains, i;
3472 if (!centry) {
3473 return 1;
3476 num_domains = centry_uint32(centry);
3478 for (i=0; i< num_domains; i++) {
3479 DOM_SID sid;
3480 (void)centry_string(centry, mem_ctx);
3481 (void)centry_string(centry, mem_ctx);
3482 (void)centry_sid(centry, &sid);
3485 centry_free(centry);
3487 if (!(state->success)) {
3488 return 1;
3490 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3491 return 0;
3494 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3495 TDB_DATA dbuf,
3496 struct tdb_validation_status *state)
3498 if (dbuf.dsize == 0) {
3499 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3500 "key %s (len ==0) ?\n", keystr));
3501 state->bad_entry = true;
3502 state->success = false;
3503 return 1;
3506 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3507 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3508 return 0;
3511 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3512 struct tdb_validation_status *state)
3514 if (dbuf.dsize != 4) {
3515 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3516 keystr, (unsigned int)dbuf.dsize ));
3517 state->bad_entry = true;
3518 state->success = false;
3519 return 1;
3521 DEBUG(10,("validate_offline: %s ok\n", keystr));
3522 return 0;
3525 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3526 struct tdb_validation_status *state)
3528 if (dbuf.dsize != 4) {
3529 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3530 "key %s (len %u != 4) ?\n",
3531 keystr, (unsigned int)dbuf.dsize));
3532 state->bad_entry = true;
3533 state->success = false;
3534 return 1;
3537 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3538 return 0;
3541 /***********************************************************************
3542 A list of all possible cache tdb keys with associated validation
3543 functions.
3544 ***********************************************************************/
3546 struct key_val_struct {
3547 const char *keyname;
3548 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3549 } key_val[] = {
3550 {"SEQNUM/", validate_seqnum},
3551 {"NS/", validate_ns},
3552 {"SN/", validate_sn},
3553 {"U/", validate_u},
3554 {"LOC_POL/", validate_loc_pol},
3555 {"PWD_POL/", validate_pwd_pol},
3556 {"CRED/", validate_cred},
3557 {"UL/", validate_ul},
3558 {"GL/", validate_gl},
3559 {"UG/", validate_ug},
3560 {"UA", validate_ua},
3561 {"GM/", validate_gm},
3562 {"DR/", validate_dr},
3563 {"DE/", validate_de},
3564 {"NSS/PWINFO/", validate_pwinfo},
3565 {"TRUSTDOMS/", validate_trustdoms},
3566 {"TRUSTDOMCACHE/", validate_trustdomcache},
3567 {"NSS/NA/", validate_nss_na},
3568 {"NSS/AN/", validate_nss_an},
3569 {"WINBINDD_OFFLINE", validate_offline},
3570 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3571 {NULL, NULL}
3574 /***********************************************************************
3575 Function to look at every entry in the tdb and validate it as far as
3576 possible.
3577 ***********************************************************************/
3579 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3581 int i;
3582 unsigned int max_key_len = 1024;
3583 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3585 /* Paranoia check. */
3586 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3587 max_key_len = 1024 * 1024;
3589 if (kbuf.dsize > max_key_len) {
3590 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3591 "(%u) > (%u)\n\n",
3592 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3593 return 1;
3596 for (i = 0; key_val[i].keyname; i++) {
3597 size_t namelen = strlen(key_val[i].keyname);
3598 if (kbuf.dsize >= namelen && (
3599 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3600 TALLOC_CTX *mem_ctx;
3601 char *keystr;
3602 int ret;
3604 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3605 if (!keystr) {
3606 return 1;
3608 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3609 keystr[kbuf.dsize] = '\0';
3611 mem_ctx = talloc_init("validate_ctx");
3612 if (!mem_ctx) {
3613 SAFE_FREE(keystr);
3614 return 1;
3617 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3618 v_state);
3620 SAFE_FREE(keystr);
3621 talloc_destroy(mem_ctx);
3622 return ret;
3626 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3627 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3628 DEBUG(0,("data :\n"));
3629 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3630 v_state->unknown_key = true;
3631 v_state->success = false;
3632 return 1; /* terminate. */
3635 static void validate_panic(const char *const why)
3637 DEBUG(0,("validating cache: would panic %s\n", why ));
3638 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3639 exit(47);
3642 /***********************************************************************
3643 Try and validate every entry in the winbindd cache. If we fail here,
3644 delete the cache tdb and return non-zero.
3645 ***********************************************************************/
3647 int winbindd_validate_cache(void)
3649 int ret = -1;
3650 const char *tdb_path = cache_path("winbindd_cache.tdb");
3651 TDB_CONTEXT *tdb = NULL;
3653 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3654 smb_panic_fn = validate_panic;
3657 tdb = tdb_open_log(tdb_path,
3658 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3659 ( lp_winbind_offline_logon()
3660 ? TDB_DEFAULT
3661 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3662 O_RDWR|O_CREAT,
3663 0600);
3664 if (!tdb) {
3665 DEBUG(0, ("winbindd_validate_cache: "
3666 "error opening/initializing tdb\n"));
3667 goto done;
3669 tdb_close(tdb);
3671 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3673 if (ret != 0) {
3674 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3675 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3676 unlink(tdb_path);
3679 done:
3680 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3681 smb_panic_fn = smb_panic;
3682 return ret;
3685 /***********************************************************************
3686 Try and validate every entry in the winbindd cache.
3687 ***********************************************************************/
3689 int winbindd_validate_cache_nobackup(void)
3691 int ret = -1;
3692 const char *tdb_path = cache_path("winbindd_cache.tdb");
3694 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3695 smb_panic_fn = validate_panic;
3698 if (wcache == NULL || wcache->tdb == NULL) {
3699 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3700 } else {
3701 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3704 if (ret != 0) {
3705 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3706 "successful.\n"));
3709 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3710 "function\n"));
3711 smb_panic_fn = smb_panic;
3712 return ret;
3715 bool winbindd_cache_validate_and_initialize(void)
3717 close_winbindd_cache();
3719 if (lp_winbind_offline_logon()) {
3720 if (winbindd_validate_cache() < 0) {
3721 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3722 "could be restored.\n"));
3726 return initialize_winbindd_cache();
3729 /*********************************************************************
3730 ********************************************************************/
3732 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3733 struct winbindd_tdc_domain **domains,
3734 size_t *num_domains )
3736 struct winbindd_tdc_domain *list = NULL;
3737 size_t idx;
3738 int i;
3739 bool set_only = false;
3741 /* don't allow duplicates */
3743 idx = *num_domains;
3744 list = *domains;
3746 for ( i=0; i< (*num_domains); i++ ) {
3747 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3748 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3749 new_dom->name));
3750 idx = i;
3751 set_only = true;
3753 break;
3757 if ( !set_only ) {
3758 if ( !*domains ) {
3759 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3760 idx = 0;
3761 } else {
3762 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3763 struct winbindd_tdc_domain,
3764 (*num_domains)+1);
3765 idx = *num_domains;
3768 ZERO_STRUCT( list[idx] );
3771 if ( !list )
3772 return false;
3774 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3775 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3777 if ( !is_null_sid( &new_dom->sid ) ) {
3778 sid_copy( &list[idx].sid, &new_dom->sid );
3779 } else {
3780 sid_copy(&list[idx].sid, &global_sid_NULL);
3783 if ( new_dom->domain_flags != 0x0 )
3784 list[idx].trust_flags = new_dom->domain_flags;
3786 if ( new_dom->domain_type != 0x0 )
3787 list[idx].trust_type = new_dom->domain_type;
3789 if ( new_dom->domain_trust_attribs != 0x0 )
3790 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3792 if ( !set_only ) {
3793 *domains = list;
3794 *num_domains = idx + 1;
3797 return true;
3800 /*********************************************************************
3801 ********************************************************************/
3803 static TDB_DATA make_tdc_key( const char *domain_name )
3805 char *keystr = NULL;
3806 TDB_DATA key = { NULL, 0 };
3808 if ( !domain_name ) {
3809 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3810 return key;
3813 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3814 return key;
3816 key = string_term_tdb_data(keystr);
3818 return key;
3821 /*********************************************************************
3822 ********************************************************************/
3824 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3825 size_t num_domains,
3826 unsigned char **buf )
3828 unsigned char *buffer = NULL;
3829 int len = 0;
3830 int buflen = 0;
3831 int i = 0;
3833 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3834 (int)num_domains));
3836 buflen = 0;
3838 again:
3839 len = 0;
3841 /* Store the number of array items first */
3842 len += tdb_pack( buffer+len, buflen-len, "d",
3843 num_domains );
3845 /* now pack each domain trust record */
3846 for ( i=0; i<num_domains; i++ ) {
3848 fstring tmp;
3850 if ( buflen > 0 ) {
3851 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3852 domains[i].domain_name,
3853 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3856 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3857 domains[i].domain_name,
3858 domains[i].dns_name,
3859 sid_to_fstring(tmp, &domains[i].sid),
3860 domains[i].trust_flags,
3861 domains[i].trust_attribs,
3862 domains[i].trust_type );
3865 if ( buflen < len ) {
3866 SAFE_FREE(buffer);
3867 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3868 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3869 buflen = -1;
3870 goto done;
3872 buflen = len;
3873 goto again;
3876 *buf = buffer;
3878 done:
3879 return buflen;
3882 /*********************************************************************
3883 ********************************************************************/
3885 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3886 struct winbindd_tdc_domain **domains )
3888 fstring domain_name, dns_name, sid_string;
3889 uint32 type, attribs, flags;
3890 int num_domains;
3891 int len = 0;
3892 int i;
3893 struct winbindd_tdc_domain *list = NULL;
3895 /* get the number of domains */
3896 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3897 if ( len == -1 ) {
3898 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3899 return 0;
3902 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3903 if ( !list ) {
3904 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3905 return 0;
3908 for ( i=0; i<num_domains; i++ ) {
3909 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3910 domain_name,
3911 dns_name,
3912 sid_string,
3913 &flags,
3914 &attribs,
3915 &type );
3917 if ( len == -1 ) {
3918 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3919 TALLOC_FREE( list );
3920 return 0;
3923 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3924 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3925 domain_name, dns_name, sid_string,
3926 flags, attribs, type));
3928 list[i].domain_name = talloc_strdup( list, domain_name );
3929 list[i].dns_name = talloc_strdup( list, dns_name );
3930 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3931 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3932 domain_name));
3934 list[i].trust_flags = flags;
3935 list[i].trust_attribs = attribs;
3936 list[i].trust_type = type;
3939 *domains = list;
3941 return num_domains;
3944 /*********************************************************************
3945 ********************************************************************/
3947 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3949 TDB_DATA key = make_tdc_key( lp_workgroup() );
3950 TDB_DATA data = { NULL, 0 };
3951 int ret;
3953 if ( !key.dptr )
3954 return false;
3956 /* See if we were asked to delete the cache entry */
3958 if ( !domains ) {
3959 ret = tdb_delete( wcache->tdb, key );
3960 goto done;
3963 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3965 if ( !data.dptr ) {
3966 ret = -1;
3967 goto done;
3970 ret = tdb_store( wcache->tdb, key, data, 0 );
3972 done:
3973 SAFE_FREE( data.dptr );
3974 SAFE_FREE( key.dptr );
3976 return ( ret != -1 );
3979 /*********************************************************************
3980 ********************************************************************/
3982 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3984 TDB_DATA key = make_tdc_key( lp_workgroup() );
3985 TDB_DATA data = { NULL, 0 };
3987 *domains = NULL;
3988 *num_domains = 0;
3990 if ( !key.dptr )
3991 return false;
3993 data = tdb_fetch( wcache->tdb, key );
3995 SAFE_FREE( key.dptr );
3997 if ( !data.dptr )
3998 return false;
4000 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4002 SAFE_FREE( data.dptr );
4004 if ( !*domains )
4005 return false;
4007 return true;
4010 /*********************************************************************
4011 ********************************************************************/
4013 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4015 struct winbindd_tdc_domain *dom_list = NULL;
4016 size_t num_domains = 0;
4017 bool ret = false;
4019 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4020 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4021 domain->name, domain->alt_name,
4022 sid_string_dbg(&domain->sid),
4023 domain->domain_flags,
4024 domain->domain_trust_attribs,
4025 domain->domain_type));
4027 if ( !init_wcache() ) {
4028 return false;
4031 /* fetch the list */
4033 wcache_tdc_fetch_list( &dom_list, &num_domains );
4035 /* add the new domain */
4037 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4038 goto done;
4041 /* pack the domain */
4043 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4044 goto done;
4047 /* Success */
4049 ret = true;
4050 done:
4051 TALLOC_FREE( dom_list );
4053 return ret;
4056 /*********************************************************************
4057 ********************************************************************/
4059 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4061 struct winbindd_tdc_domain *dom_list = NULL;
4062 size_t num_domains = 0;
4063 int i;
4064 struct winbindd_tdc_domain *d = NULL;
4066 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4068 if ( !init_wcache() ) {
4069 return false;
4072 /* fetch the list */
4074 wcache_tdc_fetch_list( &dom_list, &num_domains );
4076 for ( i=0; i<num_domains; i++ ) {
4077 if ( strequal(name, dom_list[i].domain_name) ||
4078 strequal(name, dom_list[i].dns_name) )
4080 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4081 name));
4083 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4084 if ( !d )
4085 break;
4087 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4088 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4089 sid_copy( &d->sid, &dom_list[i].sid );
4090 d->trust_flags = dom_list[i].trust_flags;
4091 d->trust_type = dom_list[i].trust_type;
4092 d->trust_attribs = dom_list[i].trust_attribs;
4094 break;
4098 TALLOC_FREE( dom_list );
4100 return d;
4104 /*********************************************************************
4105 ********************************************************************/
4107 void wcache_tdc_clear( void )
4109 if ( !init_wcache() )
4110 return;
4112 wcache_tdc_store_list( NULL, 0 );
4114 return;
4118 /*********************************************************************
4119 ********************************************************************/
4121 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4122 NTSTATUS status,
4123 const DOM_SID *user_sid,
4124 const char *homedir,
4125 const char *shell,
4126 const char *gecos,
4127 uint32 gid)
4129 struct cache_entry *centry;
4130 fstring tmp;
4132 if ( (centry = centry_start(domain, status)) == NULL )
4133 return;
4135 centry_put_string( centry, homedir );
4136 centry_put_string( centry, shell );
4137 centry_put_string( centry, gecos );
4138 centry_put_uint32( centry, gid );
4140 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4142 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4144 centry_free(centry);
4147 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4148 const DOM_SID *user_sid,
4149 TALLOC_CTX *ctx,
4150 ADS_STRUCT *ads, LDAPMessage *msg,
4151 const char **homedir, const char **shell,
4152 const char **gecos, gid_t *p_gid)
4154 struct winbind_cache *cache = get_cache(domain);
4155 struct cache_entry *centry = NULL;
4156 NTSTATUS nt_status;
4157 fstring tmp;
4159 if (!cache->tdb)
4160 goto do_query;
4162 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4163 sid_to_fstring(tmp, user_sid));
4165 if (!centry)
4166 goto do_query;
4168 *homedir = centry_string( centry, ctx );
4169 *shell = centry_string( centry, ctx );
4170 *gecos = centry_string( centry, ctx );
4171 *p_gid = centry_uint32( centry );
4173 centry_free(centry);
4175 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4176 sid_string_dbg(user_sid)));
4178 return NT_STATUS_OK;
4180 do_query:
4182 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4183 homedir, shell, gecos, p_gid );
4185 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4187 if ( NT_STATUS_IS_OK(nt_status) ) {
4188 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4189 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4190 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4191 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4193 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4194 *homedir, *shell, *gecos, *p_gid );
4197 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4198 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4199 domain->name ));
4200 set_domain_offline( domain );
4203 return nt_status;
4207 /* the cache backend methods are exposed via this structure */
4208 struct winbindd_methods cache_methods = {
4209 true,
4210 query_user_list,
4211 enum_dom_groups,
4212 enum_local_groups,
4213 name_to_sid,
4214 sid_to_name,
4215 rids_to_names,
4216 query_user,
4217 lookup_usergroups,
4218 lookup_useraliases,
4219 lookup_groupmem,
4220 sequence_number,
4221 lockout_policy,
4222 password_policy,
4223 trusted_domains