s3/packaging: pam_winbind has been moved to section 8.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob3913d965caa71f84401faeb971a9867769571bfb
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, TALLOC_CTX *mem_ctx, DOM_SID *sid)
354 char *sid_string;
355 sid_string = centry_string(centry, mem_ctx);
356 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
357 return false;
359 return true;
364 pull a NTSTATUS from a cache entry
366 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
368 NTSTATUS status;
370 status = NT_STATUS(centry_uint32(centry));
371 return status;
375 /* the server is considered down if it can't give us a sequence number */
376 static bool wcache_server_down(struct winbindd_domain *domain)
378 bool ret;
380 if (!wcache->tdb)
381 return false;
383 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
385 if (ret)
386 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
387 domain->name ));
388 return ret;
391 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
393 TDB_DATA data;
394 fstring key;
395 uint32 time_diff;
397 if (!wcache->tdb) {
398 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
399 return NT_STATUS_UNSUCCESSFUL;
402 fstr_sprintf( key, "SEQNUM/%s", domain->name );
404 data = tdb_fetch_bystring( wcache->tdb, key );
405 if ( !data.dptr || data.dsize!=8 ) {
406 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
407 return NT_STATUS_UNSUCCESSFUL;
410 domain->sequence_number = IVAL(data.dptr, 0);
411 domain->last_seq_check = IVAL(data.dptr, 4);
413 SAFE_FREE(data.dptr);
415 /* have we expired? */
417 time_diff = now - domain->last_seq_check;
418 if ( time_diff > lp_winbind_cache_time() ) {
419 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
420 domain->name, domain->sequence_number,
421 (uint32)domain->last_seq_check));
422 return NT_STATUS_UNSUCCESSFUL;
425 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
426 domain->name, domain->sequence_number,
427 (uint32)domain->last_seq_check));
429 return NT_STATUS_OK;
432 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
434 TDB_DATA data;
435 fstring key_str;
436 uint8 buf[8];
438 if (!wcache->tdb) {
439 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
440 return NT_STATUS_UNSUCCESSFUL;
443 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
445 SIVAL(buf, 0, domain->sequence_number);
446 SIVAL(buf, 4, domain->last_seq_check);
447 data.dptr = buf;
448 data.dsize = 8;
450 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
451 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
452 return NT_STATUS_UNSUCCESSFUL;
455 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
456 domain->name, domain->sequence_number,
457 (uint32)domain->last_seq_check));
459 return NT_STATUS_OK;
463 refresh the domain sequence number. If force is true
464 then always refresh it, no matter how recently we fetched it
467 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
469 NTSTATUS status;
470 unsigned time_diff;
471 time_t t = time(NULL);
472 unsigned cache_time = lp_winbind_cache_time();
474 if ( IS_DOMAIN_OFFLINE(domain) ) {
475 return;
478 get_cache( domain );
480 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
481 /* trying to reconnect is expensive, don't do it too often */
482 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
483 cache_time *= 8;
485 #endif
487 time_diff = t - domain->last_seq_check;
489 /* see if we have to refetch the domain sequence number */
490 if (!force && (time_diff < cache_time) &&
491 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
492 NT_STATUS_IS_OK(domain->last_status)) {
493 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
494 goto done;
497 /* try to get the sequence number from the tdb cache first */
498 /* this will update the timestamp as well */
500 status = fetch_cache_seqnum( domain, t );
501 if (NT_STATUS_IS_OK(status) &&
502 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
503 NT_STATUS_IS_OK(domain->last_status)) {
504 goto done;
507 /* important! make sure that we know if this is a native
508 mode domain or not. And that we can contact it. */
510 if ( winbindd_can_contact_domain( domain ) ) {
511 status = domain->backend->sequence_number(domain,
512 &domain->sequence_number);
513 } else {
514 /* just use the current time */
515 status = NT_STATUS_OK;
516 domain->sequence_number = time(NULL);
520 /* the above call could have set our domain->backend to NULL when
521 * coming from offline to online mode, make sure to reinitialize the
522 * backend - Guenther */
523 get_cache( domain );
525 if (!NT_STATUS_IS_OK(status)) {
526 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
527 domain->sequence_number = DOM_SEQUENCE_NONE;
530 domain->last_status = status;
531 domain->last_seq_check = time(NULL);
533 /* save the new sequence number in the cache */
534 store_cache_seqnum( domain );
536 done:
537 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
538 domain->name, domain->sequence_number));
540 return;
544 decide if a cache entry has expired
546 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
548 /* If we've been told to be offline - stay in that state... */
549 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
550 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
551 keystr, domain->name ));
552 return false;
555 /* when the domain is offline return the cached entry.
556 * This deals with transient offline states... */
558 if (!domain->online) {
559 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
560 keystr, domain->name ));
561 return false;
564 /* if the server is OK and our cache entry came from when it was down then
565 the entry is invalid */
566 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
567 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
568 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
569 keystr, domain->name ));
570 return true;
573 /* if the server is down or the cache entry is not older than the
574 current sequence number then it is OK */
575 if (wcache_server_down(domain) ||
576 centry->sequence_number == domain->sequence_number) {
577 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
578 keystr, domain->name ));
579 return false;
582 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
583 keystr, domain->name ));
585 /* it's expired */
586 return true;
589 static struct cache_entry *wcache_fetch_raw(char *kstr)
591 TDB_DATA data;
592 struct cache_entry *centry;
593 TDB_DATA key;
595 key = string_tdb_data(kstr);
596 data = tdb_fetch(wcache->tdb, key);
597 if (!data.dptr) {
598 /* a cache miss */
599 return NULL;
602 centry = SMB_XMALLOC_P(struct cache_entry);
603 centry->data = (unsigned char *)data.dptr;
604 centry->len = data.dsize;
605 centry->ofs = 0;
607 if (centry->len < 8) {
608 /* huh? corrupt cache? */
609 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
610 centry_free(centry);
611 return NULL;
614 centry->status = centry_ntstatus(centry);
615 centry->sequence_number = centry_uint32(centry);
617 return centry;
621 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
622 number and return status
624 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
625 struct winbindd_domain *domain,
626 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
627 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
628 struct winbindd_domain *domain,
629 const char *format, ...)
631 va_list ap;
632 char *kstr;
633 struct cache_entry *centry;
635 if (!winbindd_use_cache()) {
636 return NULL;
639 refresh_sequence_number(domain, false);
641 va_start(ap, format);
642 smb_xvasprintf(&kstr, format, ap);
643 va_end(ap);
645 centry = wcache_fetch_raw(kstr);
646 if (centry == NULL) {
647 free(kstr);
648 return NULL;
651 if (centry_expired(domain, kstr, centry)) {
653 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
654 kstr, domain->name ));
656 centry_free(centry);
657 free(kstr);
658 return NULL;
661 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
662 kstr, domain->name ));
664 free(kstr);
665 return centry;
668 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
669 static void wcache_delete(const char *format, ...)
671 va_list ap;
672 char *kstr;
673 TDB_DATA key;
675 va_start(ap, format);
676 smb_xvasprintf(&kstr, format, ap);
677 va_end(ap);
679 key = string_tdb_data(kstr);
681 tdb_delete(wcache->tdb, key);
682 free(kstr);
686 make sure we have at least len bytes available in a centry
688 static void centry_expand(struct cache_entry *centry, uint32 len)
690 if (centry->len - centry->ofs >= len)
691 return;
692 centry->len *= 2;
693 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
694 centry->len);
695 if (!centry->data) {
696 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
697 smb_panic_fn("out of memory in centry_expand");
702 push a uint32 into a centry
704 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
706 centry_expand(centry, 4);
707 SIVAL(centry->data, centry->ofs, v);
708 centry->ofs += 4;
712 push a uint16 into a centry
714 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
716 centry_expand(centry, 2);
717 SIVAL(centry->data, centry->ofs, v);
718 centry->ofs += 2;
722 push a uint8 into a centry
724 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
726 centry_expand(centry, 1);
727 SCVAL(centry->data, centry->ofs, v);
728 centry->ofs += 1;
732 push a string into a centry
734 static void centry_put_string(struct cache_entry *centry, const char *s)
736 int len;
738 if (!s) {
739 /* null strings are marked as len 0xFFFF */
740 centry_put_uint8(centry, 0xFF);
741 return;
744 len = strlen(s);
745 /* can't handle more than 254 char strings. Truncating is probably best */
746 if (len > 254) {
747 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
748 len = 254;
750 centry_put_uint8(centry, len);
751 centry_expand(centry, len);
752 memcpy(centry->data + centry->ofs, s, len);
753 centry->ofs += len;
757 push a 16 byte hash into a centry - treat as 16 byte string.
759 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
761 centry_put_uint8(centry, 16);
762 centry_expand(centry, 16);
763 memcpy(centry->data + centry->ofs, val, 16);
764 centry->ofs += 16;
767 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
769 fstring sid_string;
770 centry_put_string(centry, sid_to_fstring(sid_string, sid));
775 put NTSTATUS into a centry
777 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
779 uint32 status_value = NT_STATUS_V(status);
780 centry_put_uint32(centry, status_value);
785 push a NTTIME into a centry
787 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
789 centry_expand(centry, 8);
790 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
791 centry->ofs += 4;
792 SIVAL(centry->data, centry->ofs, nt >> 32);
793 centry->ofs += 4;
797 push a time_t into a centry - use a 64 bit size.
798 NTTIME here is being used as a convenient 64-bit size.
800 static void centry_put_time(struct cache_entry *centry, time_t t)
802 NTTIME nt = (NTTIME)t;
803 centry_put_nttime(centry, nt);
807 start a centry for output. When finished, call centry_end()
809 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
811 struct cache_entry *centry;
813 if (!wcache->tdb)
814 return NULL;
816 centry = SMB_XMALLOC_P(struct cache_entry);
818 centry->len = 8192; /* reasonable default */
819 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
820 centry->ofs = 0;
821 centry->sequence_number = domain->sequence_number;
822 centry_put_ntstatus(centry, status);
823 centry_put_uint32(centry, centry->sequence_number);
824 return centry;
828 finish a centry and write it to the tdb
830 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
831 static void centry_end(struct cache_entry *centry, const char *format, ...)
833 va_list ap;
834 char *kstr;
835 TDB_DATA key, data;
837 if (!winbindd_use_cache()) {
838 return;
841 va_start(ap, format);
842 smb_xvasprintf(&kstr, format, ap);
843 va_end(ap);
845 key = string_tdb_data(kstr);
846 data.dptr = centry->data;
847 data.dsize = centry->ofs;
849 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
850 free(kstr);
853 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
854 NTSTATUS status, const char *domain_name,
855 const char *name, const DOM_SID *sid,
856 enum lsa_SidType type)
858 struct cache_entry *centry;
859 fstring uname;
861 centry = centry_start(domain, status);
862 if (!centry)
863 return;
864 centry_put_uint32(centry, type);
865 centry_put_sid(centry, sid);
866 fstrcpy(uname, name);
867 strupper_m(uname);
868 centry_end(centry, "NS/%s/%s", domain_name, uname);
869 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
870 uname, sid_string_dbg(sid), nt_errstr(status)));
871 centry_free(centry);
874 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
875 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
877 struct cache_entry *centry;
878 fstring sid_string;
880 centry = centry_start(domain, status);
881 if (!centry)
882 return;
884 if (NT_STATUS_IS_OK(status)) {
885 centry_put_uint32(centry, type);
886 centry_put_string(centry, domain_name);
887 centry_put_string(centry, name);
890 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
891 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
892 name, nt_errstr(status)));
893 centry_free(centry);
897 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
899 struct cache_entry *centry;
900 fstring sid_string;
902 if (is_null_sid(&info->user_sid)) {
903 return;
906 centry = centry_start(domain, status);
907 if (!centry)
908 return;
909 centry_put_string(centry, info->acct_name);
910 centry_put_string(centry, info->full_name);
911 centry_put_string(centry, info->homedir);
912 centry_put_string(centry, info->shell);
913 centry_put_uint32(centry, info->primary_gid);
914 centry_put_sid(centry, &info->user_sid);
915 centry_put_sid(centry, &info->group_sid);
916 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
917 &info->user_sid));
918 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
919 centry_free(centry);
922 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
923 NTSTATUS status,
924 struct samr_DomInfo12 *lockout_policy)
926 struct cache_entry *centry;
928 centry = centry_start(domain, status);
929 if (!centry)
930 return;
932 centry_put_nttime(centry, lockout_policy->lockout_duration);
933 centry_put_nttime(centry, lockout_policy->lockout_window);
934 centry_put_uint16(centry, lockout_policy->lockout_threshold);
936 centry_end(centry, "LOC_POL/%s", domain->name);
938 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
940 centry_free(centry);
945 static void wcache_save_password_policy(struct winbindd_domain *domain,
946 NTSTATUS status,
947 struct samr_DomInfo1 *policy)
949 struct cache_entry *centry;
951 centry = centry_start(domain, status);
952 if (!centry)
953 return;
955 centry_put_uint16(centry, policy->min_password_length);
956 centry_put_uint16(centry, policy->password_history_length);
957 centry_put_uint32(centry, policy->password_properties);
958 centry_put_nttime(centry, policy->max_password_age);
959 centry_put_nttime(centry, policy->min_password_age);
961 centry_end(centry, "PWD_POL/%s", domain->name);
963 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
965 centry_free(centry);
968 /***************************************************************************
969 ***************************************************************************/
971 static void wcache_save_username_alias(struct winbindd_domain *domain,
972 NTSTATUS status,
973 const char *name, const char *alias)
975 struct cache_entry *centry;
976 fstring uname;
978 if ( (centry = centry_start(domain, status)) == NULL )
979 return;
981 centry_put_string( centry, alias );
983 fstrcpy(uname, name);
984 strupper_m(uname);
985 centry_end(centry, "NSS/NA/%s", uname);
987 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
989 centry_free(centry);
992 static void wcache_save_alias_username(struct winbindd_domain *domain,
993 NTSTATUS status,
994 const char *alias, const char *name)
996 struct cache_entry *centry;
997 fstring uname;
999 if ( (centry = centry_start(domain, status)) == NULL )
1000 return;
1002 centry_put_string( centry, name );
1004 fstrcpy(uname, alias);
1005 strupper_m(uname);
1006 centry_end(centry, "NSS/AN/%s", uname);
1008 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1010 centry_free(centry);
1013 /***************************************************************************
1014 ***************************************************************************/
1016 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1017 struct winbindd_domain *domain,
1018 const char *name, char **alias )
1020 struct winbind_cache *cache = get_cache(domain);
1021 struct cache_entry *centry = NULL;
1022 NTSTATUS status;
1023 char *upper_name;
1025 if ( domain->internal )
1026 return NT_STATUS_NOT_SUPPORTED;
1028 if (!cache->tdb)
1029 goto do_query;
1031 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1032 return NT_STATUS_NO_MEMORY;
1033 strupper_m(upper_name);
1035 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1037 SAFE_FREE( upper_name );
1039 if (!centry)
1040 goto do_query;
1042 status = centry->status;
1044 if (!NT_STATUS_IS_OK(status)) {
1045 centry_free(centry);
1046 return status;
1049 *alias = centry_string( centry, mem_ctx );
1051 centry_free(centry);
1053 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1054 name, *alias ? *alias : "(none)"));
1056 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1058 do_query:
1060 /* If its not in cache and we are offline, then fail */
1062 if ( get_global_winbindd_state_offline() || !domain->online ) {
1063 DEBUG(8,("resolve_username_to_alias: rejecting query "
1064 "in offline mode\n"));
1065 return NT_STATUS_NOT_FOUND;
1068 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1070 if ( NT_STATUS_IS_OK( status ) ) {
1071 wcache_save_username_alias(domain, status, name, *alias);
1074 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1075 wcache_save_username_alias(domain, status, name, "(NULL)");
1078 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1079 nt_errstr(status)));
1081 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1082 set_domain_offline( domain );
1085 return status;
1088 /***************************************************************************
1089 ***************************************************************************/
1091 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1092 struct winbindd_domain *domain,
1093 const char *alias, char **name )
1095 struct winbind_cache *cache = get_cache(domain);
1096 struct cache_entry *centry = NULL;
1097 NTSTATUS status;
1098 char *upper_name;
1100 if ( domain->internal )
1101 return NT_STATUS_NOT_SUPPORTED;
1103 if (!cache->tdb)
1104 goto do_query;
1106 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1107 return NT_STATUS_NO_MEMORY;
1108 strupper_m(upper_name);
1110 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1112 SAFE_FREE( upper_name );
1114 if (!centry)
1115 goto do_query;
1117 status = centry->status;
1119 if (!NT_STATUS_IS_OK(status)) {
1120 centry_free(centry);
1121 return status;
1124 *name = centry_string( centry, mem_ctx );
1126 centry_free(centry);
1128 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1129 alias, *name ? *name : "(none)"));
1131 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1133 do_query:
1135 /* If its not in cache and we are offline, then fail */
1137 if ( get_global_winbindd_state_offline() || !domain->online ) {
1138 DEBUG(8,("resolve_alias_to_username: rejecting query "
1139 "in offline mode\n"));
1140 return NT_STATUS_NOT_FOUND;
1143 /* an alias cannot contain a domain prefix or '@' */
1145 if (strchr(alias, '\\') || strchr(alias, '@')) {
1146 DEBUG(10,("resolve_alias_to_username: skipping fully "
1147 "qualified name %s\n", alias));
1148 return NT_STATUS_OBJECT_NAME_INVALID;
1151 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1153 if ( NT_STATUS_IS_OK( status ) ) {
1154 wcache_save_alias_username( domain, status, alias, *name );
1157 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1158 wcache_save_alias_username(domain, status, alias, "(NULL)");
1161 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1162 nt_errstr(status)));
1164 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1165 set_domain_offline( domain );
1168 return status;
1171 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1173 struct winbind_cache *cache = get_cache(domain);
1174 TDB_DATA data;
1175 fstring key_str, tmp;
1176 uint32 rid;
1178 if (!cache->tdb) {
1179 return NT_STATUS_INTERNAL_DB_ERROR;
1182 if (is_null_sid(sid)) {
1183 return NT_STATUS_INVALID_SID;
1186 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1187 return NT_STATUS_INVALID_SID;
1190 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1192 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1193 if (!data.dptr) {
1194 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1197 SAFE_FREE(data.dptr);
1198 return NT_STATUS_OK;
1201 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1202 as new salted ones. */
1204 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1205 TALLOC_CTX *mem_ctx,
1206 const DOM_SID *sid,
1207 const uint8 **cached_nt_pass,
1208 const uint8 **cached_salt)
1210 struct winbind_cache *cache = get_cache(domain);
1211 struct cache_entry *centry = NULL;
1212 NTSTATUS status;
1213 time_t t;
1214 uint32 rid;
1215 fstring tmp;
1217 if (!cache->tdb) {
1218 return NT_STATUS_INTERNAL_DB_ERROR;
1221 if (is_null_sid(sid)) {
1222 return NT_STATUS_INVALID_SID;
1225 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1226 return NT_STATUS_INVALID_SID;
1229 /* Try and get a salted cred first. If we can't
1230 fall back to an unsalted cred. */
1232 centry = wcache_fetch(cache, domain, "CRED/%s",
1233 sid_to_fstring(tmp, sid));
1234 if (!centry) {
1235 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1236 sid_string_dbg(sid)));
1237 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1240 t = centry_time(centry);
1242 /* In the salted case this isn't actually the nt_hash itself,
1243 but the MD5 of the salt + nt_hash. Let the caller
1244 sort this out. It can tell as we only return the cached_salt
1245 if we are returning a salted cred. */
1247 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1248 if (*cached_nt_pass == NULL) {
1249 fstring sidstr;
1251 sid_to_fstring(sidstr, sid);
1253 /* Bad (old) cred cache. Delete and pretend we
1254 don't have it. */
1255 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1256 sidstr));
1257 wcache_delete("CRED/%s", sidstr);
1258 centry_free(centry);
1259 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1262 /* We only have 17 bytes more data in the salted cred case. */
1263 if (centry->len - centry->ofs == 17) {
1264 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1265 } else {
1266 *cached_salt = NULL;
1269 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1270 if (*cached_salt) {
1271 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1274 status = centry->status;
1276 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1277 sid_string_dbg(sid), nt_errstr(status) ));
1279 centry_free(centry);
1280 return status;
1283 /* Store creds for a SID - only writes out new salted ones. */
1285 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1286 TALLOC_CTX *mem_ctx,
1287 const DOM_SID *sid,
1288 const uint8 nt_pass[NT_HASH_LEN])
1290 struct cache_entry *centry;
1291 fstring sid_string;
1292 uint32 rid;
1293 uint8 cred_salt[NT_HASH_LEN];
1294 uint8 salted_hash[NT_HASH_LEN];
1296 if (is_null_sid(sid)) {
1297 return NT_STATUS_INVALID_SID;
1300 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1301 return NT_STATUS_INVALID_SID;
1304 centry = centry_start(domain, NT_STATUS_OK);
1305 if (!centry) {
1306 return NT_STATUS_INTERNAL_DB_ERROR;
1309 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1311 centry_put_time(centry, time(NULL));
1313 /* Create a salt and then salt the hash. */
1314 generate_random_buffer(cred_salt, NT_HASH_LEN);
1315 E_md5hash(cred_salt, nt_pass, salted_hash);
1317 centry_put_hash16(centry, salted_hash);
1318 centry_put_hash16(centry, cred_salt);
1319 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1321 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1323 centry_free(centry);
1325 return NT_STATUS_OK;
1329 /* Query display info. This is the basic user list fn */
1330 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1331 TALLOC_CTX *mem_ctx,
1332 uint32 *num_entries,
1333 WINBIND_USERINFO **info)
1335 struct winbind_cache *cache = get_cache(domain);
1336 struct cache_entry *centry = NULL;
1337 NTSTATUS status;
1338 unsigned int i, retry;
1340 if (!cache->tdb)
1341 goto do_query;
1343 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1344 if (!centry)
1345 goto do_query;
1347 *num_entries = centry_uint32(centry);
1349 if (*num_entries == 0)
1350 goto do_cached;
1352 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1353 if (! (*info)) {
1354 smb_panic_fn("query_user_list out of memory");
1356 for (i=0; i<(*num_entries); i++) {
1357 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1358 (*info)[i].full_name = centry_string(centry, mem_ctx);
1359 (*info)[i].homedir = centry_string(centry, mem_ctx);
1360 (*info)[i].shell = centry_string(centry, mem_ctx);
1361 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1362 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1365 do_cached:
1366 status = centry->status;
1368 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1369 domain->name, nt_errstr(status) ));
1371 centry_free(centry);
1372 return status;
1374 do_query:
1375 *num_entries = 0;
1376 *info = NULL;
1378 /* Return status value returned by seq number check */
1380 if (!NT_STATUS_IS_OK(domain->last_status))
1381 return domain->last_status;
1383 /* Put the query_user_list() in a retry loop. There appears to be
1384 * some bug either with Windows 2000 or Samba's handling of large
1385 * rpc replies. This manifests itself as sudden disconnection
1386 * at a random point in the enumeration of a large (60k) user list.
1387 * The retry loop simply tries the operation again. )-: It's not
1388 * pretty but an acceptable workaround until we work out what the
1389 * real problem is. */
1391 retry = 0;
1392 do {
1394 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1395 domain->name ));
1397 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1398 if (!NT_STATUS_IS_OK(status)) {
1399 DEBUG(3, ("query_user_list: returned 0x%08x, "
1400 "retrying\n", NT_STATUS_V(status)));
1402 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1403 DEBUG(3, ("query_user_list: flushing "
1404 "connection cache\n"));
1405 invalidate_cm_connection(&domain->conn);
1408 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1409 (retry++ < 5));
1411 /* and save it */
1412 refresh_sequence_number(domain, false);
1413 centry = centry_start(domain, status);
1414 if (!centry)
1415 goto skip_save;
1416 centry_put_uint32(centry, *num_entries);
1417 for (i=0; i<(*num_entries); i++) {
1418 centry_put_string(centry, (*info)[i].acct_name);
1419 centry_put_string(centry, (*info)[i].full_name);
1420 centry_put_string(centry, (*info)[i].homedir);
1421 centry_put_string(centry, (*info)[i].shell);
1422 centry_put_sid(centry, &(*info)[i].user_sid);
1423 centry_put_sid(centry, &(*info)[i].group_sid);
1424 if (domain->backend && domain->backend->consistent) {
1425 /* when the backend is consistent we can pre-prime some mappings */
1426 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1427 domain->name,
1428 (*info)[i].acct_name,
1429 &(*info)[i].user_sid,
1430 SID_NAME_USER);
1431 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1432 &(*info)[i].user_sid,
1433 domain->name,
1434 (*info)[i].acct_name,
1435 SID_NAME_USER);
1436 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1439 centry_end(centry, "UL/%s", domain->name);
1440 centry_free(centry);
1442 skip_save:
1443 return status;
1446 /* list all domain groups */
1447 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1448 TALLOC_CTX *mem_ctx,
1449 uint32 *num_entries,
1450 struct acct_info **info)
1452 struct winbind_cache *cache = get_cache(domain);
1453 struct cache_entry *centry = NULL;
1454 NTSTATUS status;
1455 unsigned int i;
1457 if (!cache->tdb)
1458 goto do_query;
1460 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1461 if (!centry)
1462 goto do_query;
1464 *num_entries = centry_uint32(centry);
1466 if (*num_entries == 0)
1467 goto do_cached;
1469 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1470 if (! (*info)) {
1471 smb_panic_fn("enum_dom_groups out of memory");
1473 for (i=0; i<(*num_entries); i++) {
1474 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1475 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1476 (*info)[i].rid = centry_uint32(centry);
1479 do_cached:
1480 status = centry->status;
1482 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1483 domain->name, nt_errstr(status) ));
1485 centry_free(centry);
1486 return status;
1488 do_query:
1489 *num_entries = 0;
1490 *info = NULL;
1492 /* Return status value returned by seq number check */
1494 if (!NT_STATUS_IS_OK(domain->last_status))
1495 return domain->last_status;
1497 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1498 domain->name ));
1500 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1502 /* and save it */
1503 refresh_sequence_number(domain, false);
1504 centry = centry_start(domain, status);
1505 if (!centry)
1506 goto skip_save;
1507 centry_put_uint32(centry, *num_entries);
1508 for (i=0; i<(*num_entries); i++) {
1509 centry_put_string(centry, (*info)[i].acct_name);
1510 centry_put_string(centry, (*info)[i].acct_desc);
1511 centry_put_uint32(centry, (*info)[i].rid);
1513 centry_end(centry, "GL/%s/domain", domain->name);
1514 centry_free(centry);
1516 skip_save:
1517 return status;
1520 /* list all domain groups */
1521 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1522 TALLOC_CTX *mem_ctx,
1523 uint32 *num_entries,
1524 struct acct_info **info)
1526 struct winbind_cache *cache = get_cache(domain);
1527 struct cache_entry *centry = NULL;
1528 NTSTATUS status;
1529 unsigned int i;
1531 if (!cache->tdb)
1532 goto do_query;
1534 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1535 if (!centry)
1536 goto do_query;
1538 *num_entries = centry_uint32(centry);
1540 if (*num_entries == 0)
1541 goto do_cached;
1543 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1544 if (! (*info)) {
1545 smb_panic_fn("enum_dom_groups out of memory");
1547 for (i=0; i<(*num_entries); i++) {
1548 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1549 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1550 (*info)[i].rid = centry_uint32(centry);
1553 do_cached:
1555 /* If we are returning cached data and the domain controller
1556 is down then we don't know whether the data is up to date
1557 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1558 indicate this. */
1560 if (wcache_server_down(domain)) {
1561 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1562 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1563 } else
1564 status = centry->status;
1566 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1567 domain->name, nt_errstr(status) ));
1569 centry_free(centry);
1570 return status;
1572 do_query:
1573 *num_entries = 0;
1574 *info = NULL;
1576 /* Return status value returned by seq number check */
1578 if (!NT_STATUS_IS_OK(domain->last_status))
1579 return domain->last_status;
1581 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1582 domain->name ));
1584 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1586 /* and save it */
1587 refresh_sequence_number(domain, false);
1588 centry = centry_start(domain, status);
1589 if (!centry)
1590 goto skip_save;
1591 centry_put_uint32(centry, *num_entries);
1592 for (i=0; i<(*num_entries); i++) {
1593 centry_put_string(centry, (*info)[i].acct_name);
1594 centry_put_string(centry, (*info)[i].acct_desc);
1595 centry_put_uint32(centry, (*info)[i].rid);
1597 centry_end(centry, "GL/%s/local", domain->name);
1598 centry_free(centry);
1600 skip_save:
1601 return status;
1604 /* convert a single name to a sid in a domain */
1605 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1606 TALLOC_CTX *mem_ctx,
1607 enum winbindd_cmd orig_cmd,
1608 const char *domain_name,
1609 const char *name,
1610 DOM_SID *sid,
1611 enum lsa_SidType *type)
1613 struct winbind_cache *cache = get_cache(domain);
1614 struct cache_entry *centry = NULL;
1615 NTSTATUS status;
1616 fstring uname;
1618 if (!cache->tdb)
1619 goto do_query;
1621 fstrcpy(uname, name);
1622 strupper_m(uname);
1623 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1624 if (!centry)
1625 goto do_query;
1627 status = centry->status;
1628 if (NT_STATUS_IS_OK(status)) {
1629 *type = (enum lsa_SidType)centry_uint32(centry);
1630 centry_sid(centry, mem_ctx, sid);
1633 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1634 domain->name, nt_errstr(status) ));
1636 centry_free(centry);
1637 return status;
1639 do_query:
1640 ZERO_STRUCTP(sid);
1642 /* If the seq number check indicated that there is a problem
1643 * with this DC, then return that status... except for
1644 * access_denied. This is special because the dc may be in
1645 * "restrict anonymous = 1" mode, in which case it will deny
1646 * most unauthenticated operations, but *will* allow the LSA
1647 * name-to-sid that we try as a fallback. */
1649 if (!(NT_STATUS_IS_OK(domain->last_status)
1650 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1651 return domain->last_status;
1653 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1654 domain->name ));
1656 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1657 domain_name, name, sid, type);
1659 /* and save it */
1660 refresh_sequence_number(domain, false);
1662 if (domain->online &&
1663 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1664 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1666 /* Only save the reverse mapping if this was not a UPN */
1667 if (!strchr(name, '@')) {
1668 strupper_m(CONST_DISCARD(char *,domain_name));
1669 strlower_m(CONST_DISCARD(char *,name));
1670 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1674 return status;
1677 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1678 given */
1679 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1680 TALLOC_CTX *mem_ctx,
1681 const DOM_SID *sid,
1682 char **domain_name,
1683 char **name,
1684 enum lsa_SidType *type)
1686 struct winbind_cache *cache = get_cache(domain);
1687 struct cache_entry *centry = NULL;
1688 NTSTATUS status;
1689 fstring sid_string;
1691 if (!cache->tdb)
1692 goto do_query;
1694 centry = wcache_fetch(cache, domain, "SN/%s",
1695 sid_to_fstring(sid_string, sid));
1696 if (!centry)
1697 goto do_query;
1699 status = centry->status;
1700 if (NT_STATUS_IS_OK(status)) {
1701 *type = (enum lsa_SidType)centry_uint32(centry);
1702 *domain_name = centry_string(centry, mem_ctx);
1703 *name = centry_string(centry, mem_ctx);
1706 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1707 domain->name, nt_errstr(status) ));
1709 centry_free(centry);
1710 return status;
1712 do_query:
1713 *name = NULL;
1714 *domain_name = NULL;
1716 /* If the seq number check indicated that there is a problem
1717 * with this DC, then return that status... except for
1718 * access_denied. This is special because the dc may be in
1719 * "restrict anonymous = 1" mode, in which case it will deny
1720 * most unauthenticated operations, but *will* allow the LSA
1721 * sid-to-name that we try as a fallback. */
1723 if (!(NT_STATUS_IS_OK(domain->last_status)
1724 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1725 return domain->last_status;
1727 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1728 domain->name ));
1730 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1732 /* and save it */
1733 refresh_sequence_number(domain, false);
1734 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1736 /* We can't save the name to sid mapping here, as with sid history a
1737 * later name2sid would give the wrong sid. */
1739 return status;
1742 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1743 TALLOC_CTX *mem_ctx,
1744 const DOM_SID *domain_sid,
1745 uint32 *rids,
1746 size_t num_rids,
1747 char **domain_name,
1748 char ***names,
1749 enum lsa_SidType **types)
1751 struct winbind_cache *cache = get_cache(domain);
1752 size_t i;
1753 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1754 bool have_mapped;
1755 bool have_unmapped;
1757 *domain_name = NULL;
1758 *names = NULL;
1759 *types = NULL;
1761 if (!cache->tdb) {
1762 goto do_query;
1765 if (num_rids == 0) {
1766 return NT_STATUS_OK;
1769 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1770 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1772 if ((*names == NULL) || (*types == NULL)) {
1773 result = NT_STATUS_NO_MEMORY;
1774 goto error;
1777 have_mapped = have_unmapped = false;
1779 for (i=0; i<num_rids; i++) {
1780 DOM_SID sid;
1781 struct cache_entry *centry;
1782 fstring tmp;
1784 if (!sid_compose(&sid, domain_sid, rids[i])) {
1785 result = NT_STATUS_INTERNAL_ERROR;
1786 goto error;
1789 centry = wcache_fetch(cache, domain, "SN/%s",
1790 sid_to_fstring(tmp, &sid));
1791 if (!centry) {
1792 goto do_query;
1795 (*types)[i] = SID_NAME_UNKNOWN;
1796 (*names)[i] = talloc_strdup(*names, "");
1798 if (NT_STATUS_IS_OK(centry->status)) {
1799 char *dom;
1800 have_mapped = true;
1801 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1803 dom = centry_string(centry, mem_ctx);
1804 if (*domain_name == NULL) {
1805 *domain_name = dom;
1806 } else {
1807 talloc_free(dom);
1810 (*names)[i] = centry_string(centry, *names);
1812 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1813 have_unmapped = true;
1815 } else {
1816 /* something's definitely wrong */
1817 result = centry->status;
1818 goto error;
1821 centry_free(centry);
1824 if (!have_mapped) {
1825 return NT_STATUS_NONE_MAPPED;
1827 if (!have_unmapped) {
1828 return NT_STATUS_OK;
1830 return STATUS_SOME_UNMAPPED;
1832 do_query:
1834 TALLOC_FREE(*names);
1835 TALLOC_FREE(*types);
1837 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1838 rids, num_rids, domain_name,
1839 names, types);
1842 None of the queried rids has been found so save all negative entries
1844 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1845 for (i = 0; i < num_rids; i++) {
1846 DOM_SID sid;
1847 const char *name = "";
1848 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1849 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1851 if (!sid_compose(&sid, domain_sid, rids[i])) {
1852 return NT_STATUS_INTERNAL_ERROR;
1855 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1856 name, type);
1859 return result;
1863 Some or all of the queried rids have been found.
1865 if (!NT_STATUS_IS_OK(result) &&
1866 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1867 return result;
1870 refresh_sequence_number(domain, false);
1872 for (i=0; i<num_rids; i++) {
1873 DOM_SID sid;
1874 NTSTATUS status;
1876 if (!sid_compose(&sid, domain_sid, rids[i])) {
1877 result = NT_STATUS_INTERNAL_ERROR;
1878 goto error;
1881 status = (*types)[i] == SID_NAME_UNKNOWN ?
1882 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1884 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1885 (*names)[i], (*types)[i]);
1888 return result;
1890 error:
1892 TALLOC_FREE(*names);
1893 TALLOC_FREE(*types);
1894 return result;
1897 /* Lookup user information from a rid */
1898 static NTSTATUS query_user(struct winbindd_domain *domain,
1899 TALLOC_CTX *mem_ctx,
1900 const DOM_SID *user_sid,
1901 WINBIND_USERINFO *info)
1903 struct winbind_cache *cache = get_cache(domain);
1904 struct cache_entry *centry = NULL;
1905 NTSTATUS status;
1906 fstring tmp;
1908 if (!cache->tdb)
1909 goto do_query;
1911 centry = wcache_fetch(cache, domain, "U/%s",
1912 sid_to_fstring(tmp, user_sid));
1914 /* If we have an access denied cache entry and a cached info3 in the
1915 samlogon cache then do a query. This will force the rpc back end
1916 to return the info3 data. */
1918 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1919 netsamlogon_cache_have(user_sid)) {
1920 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1921 domain->last_status = NT_STATUS_OK;
1922 centry_free(centry);
1923 goto do_query;
1926 if (!centry)
1927 goto do_query;
1929 /* if status is not ok then this is a negative hit
1930 and the rest of the data doesn't matter */
1931 status = centry->status;
1932 if (NT_STATUS_IS_OK(status)) {
1933 info->acct_name = centry_string(centry, mem_ctx);
1934 info->full_name = centry_string(centry, mem_ctx);
1935 info->homedir = centry_string(centry, mem_ctx);
1936 info->shell = centry_string(centry, mem_ctx);
1937 info->primary_gid = centry_uint32(centry);
1938 centry_sid(centry, mem_ctx, &info->user_sid);
1939 centry_sid(centry, mem_ctx, &info->group_sid);
1942 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1943 domain->name, nt_errstr(status) ));
1945 centry_free(centry);
1946 return status;
1948 do_query:
1949 ZERO_STRUCTP(info);
1951 /* Return status value returned by seq number check */
1953 if (!NT_STATUS_IS_OK(domain->last_status))
1954 return domain->last_status;
1956 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1957 domain->name ));
1959 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1961 /* and save it */
1962 refresh_sequence_number(domain, false);
1963 wcache_save_user(domain, status, info);
1965 return status;
1969 /* Lookup groups a user is a member of. */
1970 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1971 TALLOC_CTX *mem_ctx,
1972 const DOM_SID *user_sid,
1973 uint32 *num_groups, DOM_SID **user_gids)
1975 struct winbind_cache *cache = get_cache(domain);
1976 struct cache_entry *centry = NULL;
1977 NTSTATUS status;
1978 unsigned int i;
1979 fstring sid_string;
1981 if (!cache->tdb)
1982 goto do_query;
1984 centry = wcache_fetch(cache, domain, "UG/%s",
1985 sid_to_fstring(sid_string, user_sid));
1987 /* If we have an access denied cache entry and a cached info3 in the
1988 samlogon cache then do a query. This will force the rpc back end
1989 to return the info3 data. */
1991 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1992 netsamlogon_cache_have(user_sid)) {
1993 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1994 domain->last_status = NT_STATUS_OK;
1995 centry_free(centry);
1996 goto do_query;
1999 if (!centry)
2000 goto do_query;
2002 *num_groups = centry_uint32(centry);
2004 if (*num_groups == 0)
2005 goto do_cached;
2007 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2008 if (! (*user_gids)) {
2009 smb_panic_fn("lookup_usergroups out of memory");
2011 for (i=0; i<(*num_groups); i++) {
2012 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
2015 do_cached:
2016 status = centry->status;
2018 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2019 domain->name, nt_errstr(status) ));
2021 centry_free(centry);
2022 return status;
2024 do_query:
2025 (*num_groups) = 0;
2026 (*user_gids) = NULL;
2028 /* Return status value returned by seq number check */
2030 if (!NT_STATUS_IS_OK(domain->last_status))
2031 return domain->last_status;
2033 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2034 domain->name ));
2036 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2038 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2039 goto skip_save;
2041 /* and save it */
2042 refresh_sequence_number(domain, false);
2043 centry = centry_start(domain, status);
2044 if (!centry)
2045 goto skip_save;
2047 centry_put_uint32(centry, *num_groups);
2048 for (i=0; i<(*num_groups); i++) {
2049 centry_put_sid(centry, &(*user_gids)[i]);
2052 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2053 centry_free(centry);
2055 skip_save:
2056 return status;
2059 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2060 TALLOC_CTX *mem_ctx,
2061 uint32 num_sids, const DOM_SID *sids,
2062 uint32 *num_aliases, uint32 **alias_rids)
2064 struct winbind_cache *cache = get_cache(domain);
2065 struct cache_entry *centry = NULL;
2066 NTSTATUS status;
2067 char *sidlist = talloc_strdup(mem_ctx, "");
2068 int i;
2070 if (!cache->tdb)
2071 goto do_query;
2073 if (num_sids == 0) {
2074 *num_aliases = 0;
2075 *alias_rids = NULL;
2076 return NT_STATUS_OK;
2079 /* We need to cache indexed by the whole list of SIDs, the aliases
2080 * resulting might come from any of the SIDs. */
2082 for (i=0; i<num_sids; i++) {
2083 fstring tmp;
2084 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2085 sid_to_fstring(tmp, &sids[i]));
2086 if (sidlist == NULL)
2087 return NT_STATUS_NO_MEMORY;
2090 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2092 if (!centry)
2093 goto do_query;
2095 *num_aliases = centry_uint32(centry);
2096 *alias_rids = NULL;
2098 if (*num_aliases) {
2099 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2101 if ((*alias_rids) == NULL) {
2102 centry_free(centry);
2103 return NT_STATUS_NO_MEMORY;
2105 } else {
2106 (*alias_rids) = NULL;
2109 for (i=0; i<(*num_aliases); i++)
2110 (*alias_rids)[i] = centry_uint32(centry);
2112 status = centry->status;
2114 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2115 "status %s\n", domain->name, nt_errstr(status)));
2117 centry_free(centry);
2118 return status;
2120 do_query:
2121 (*num_aliases) = 0;
2122 (*alias_rids) = NULL;
2124 if (!NT_STATUS_IS_OK(domain->last_status))
2125 return domain->last_status;
2127 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2128 "for domain %s\n", domain->name ));
2130 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2131 num_sids, sids,
2132 num_aliases, alias_rids);
2134 /* and save it */
2135 refresh_sequence_number(domain, false);
2136 centry = centry_start(domain, status);
2137 if (!centry)
2138 goto skip_save;
2139 centry_put_uint32(centry, *num_aliases);
2140 for (i=0; i<(*num_aliases); i++)
2141 centry_put_uint32(centry, (*alias_rids)[i]);
2142 centry_end(centry, "UA%s", sidlist);
2143 centry_free(centry);
2145 skip_save:
2146 return status;
2150 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2151 TALLOC_CTX *mem_ctx,
2152 const DOM_SID *group_sid, uint32 *num_names,
2153 DOM_SID **sid_mem, char ***names,
2154 uint32 **name_types)
2156 struct winbind_cache *cache = get_cache(domain);
2157 struct cache_entry *centry = NULL;
2158 NTSTATUS status;
2159 unsigned int i;
2160 fstring sid_string;
2162 if (!cache->tdb)
2163 goto do_query;
2165 centry = wcache_fetch(cache, domain, "GM/%s",
2166 sid_to_fstring(sid_string, group_sid));
2167 if (!centry)
2168 goto do_query;
2170 *num_names = centry_uint32(centry);
2172 if (*num_names == 0)
2173 goto do_cached;
2175 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2176 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2177 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2179 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2180 smb_panic_fn("lookup_groupmem out of memory");
2183 for (i=0; i<(*num_names); i++) {
2184 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
2185 (*names)[i] = centry_string(centry, mem_ctx);
2186 (*name_types)[i] = centry_uint32(centry);
2189 do_cached:
2190 status = centry->status;
2192 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2193 domain->name, nt_errstr(status)));
2195 centry_free(centry);
2196 return status;
2198 do_query:
2199 (*num_names) = 0;
2200 (*sid_mem) = NULL;
2201 (*names) = NULL;
2202 (*name_types) = NULL;
2204 /* Return status value returned by seq number check */
2206 if (!NT_STATUS_IS_OK(domain->last_status))
2207 return domain->last_status;
2209 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2210 domain->name ));
2212 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2213 sid_mem, names, name_types);
2215 /* and save it */
2216 refresh_sequence_number(domain, false);
2217 centry = centry_start(domain, status);
2218 if (!centry)
2219 goto skip_save;
2220 centry_put_uint32(centry, *num_names);
2221 for (i=0; i<(*num_names); i++) {
2222 centry_put_sid(centry, &(*sid_mem)[i]);
2223 centry_put_string(centry, (*names)[i]);
2224 centry_put_uint32(centry, (*name_types)[i]);
2226 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2227 centry_free(centry);
2229 skip_save:
2230 return status;
2233 /* find the sequence number for a domain */
2234 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2236 refresh_sequence_number(domain, false);
2238 *seq = domain->sequence_number;
2240 return NT_STATUS_OK;
2243 /* enumerate trusted domains
2244 * (we need to have the list of trustdoms in the cache when we go offline) -
2245 * Guenther */
2246 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2247 TALLOC_CTX *mem_ctx,
2248 uint32 *num_domains,
2249 char ***names,
2250 char ***alt_names,
2251 DOM_SID **dom_sids)
2253 struct winbind_cache *cache = get_cache(domain);
2254 struct cache_entry *centry = NULL;
2255 NTSTATUS status;
2256 int i;
2258 if (!cache->tdb)
2259 goto do_query;
2261 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2263 if (!centry) {
2264 goto do_query;
2267 *num_domains = centry_uint32(centry);
2269 if (*num_domains) {
2270 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2271 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2272 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2274 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2275 smb_panic_fn("trusted_domains out of memory");
2277 } else {
2278 (*names) = NULL;
2279 (*alt_names) = NULL;
2280 (*dom_sids) = NULL;
2283 for (i=0; i<(*num_domains); i++) {
2284 (*names)[i] = centry_string(centry, mem_ctx);
2285 (*alt_names)[i] = centry_string(centry, mem_ctx);
2286 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2287 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2291 status = centry->status;
2293 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2294 domain->name, *num_domains, nt_errstr(status) ));
2296 centry_free(centry);
2297 return status;
2299 do_query:
2300 (*num_domains) = 0;
2301 (*dom_sids) = NULL;
2302 (*names) = NULL;
2303 (*alt_names) = NULL;
2305 /* Return status value returned by seq number check */
2307 if (!NT_STATUS_IS_OK(domain->last_status))
2308 return domain->last_status;
2310 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2311 domain->name ));
2313 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2314 names, alt_names, dom_sids);
2316 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2317 * so that the generic centry handling still applies correctly -
2318 * Guenther*/
2320 if (!NT_STATUS_IS_ERR(status)) {
2321 status = NT_STATUS_OK;
2325 #if 0 /* Disabled as we want the trust dom list to be managed by
2326 the main parent and always to make the query. --jerry */
2328 /* and save it */
2329 refresh_sequence_number(domain, false);
2331 centry = centry_start(domain, status);
2332 if (!centry)
2333 goto skip_save;
2335 centry_put_uint32(centry, *num_domains);
2337 for (i=0; i<(*num_domains); i++) {
2338 centry_put_string(centry, (*names)[i]);
2339 centry_put_string(centry, (*alt_names)[i]);
2340 centry_put_sid(centry, &(*dom_sids)[i]);
2343 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2345 centry_free(centry);
2347 skip_save:
2348 #endif
2350 return status;
2353 /* get lockout policy */
2354 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2355 TALLOC_CTX *mem_ctx,
2356 struct samr_DomInfo12 *policy)
2358 struct winbind_cache *cache = get_cache(domain);
2359 struct cache_entry *centry = NULL;
2360 NTSTATUS status;
2362 if (!cache->tdb)
2363 goto do_query;
2365 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2367 if (!centry)
2368 goto do_query;
2370 policy->lockout_duration = centry_nttime(centry);
2371 policy->lockout_window = centry_nttime(centry);
2372 policy->lockout_threshold = centry_uint16(centry);
2374 status = centry->status;
2376 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2377 domain->name, nt_errstr(status) ));
2379 centry_free(centry);
2380 return status;
2382 do_query:
2383 ZERO_STRUCTP(policy);
2385 /* Return status value returned by seq number check */
2387 if (!NT_STATUS_IS_OK(domain->last_status))
2388 return domain->last_status;
2390 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2391 domain->name ));
2393 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2395 /* and save it */
2396 refresh_sequence_number(domain, false);
2397 wcache_save_lockout_policy(domain, status, policy);
2399 return status;
2402 /* get password policy */
2403 static NTSTATUS password_policy(struct winbindd_domain *domain,
2404 TALLOC_CTX *mem_ctx,
2405 struct samr_DomInfo1 *policy)
2407 struct winbind_cache *cache = get_cache(domain);
2408 struct cache_entry *centry = NULL;
2409 NTSTATUS status;
2411 if (!cache->tdb)
2412 goto do_query;
2414 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2416 if (!centry)
2417 goto do_query;
2419 policy->min_password_length = centry_uint16(centry);
2420 policy->password_history_length = centry_uint16(centry);
2421 policy->password_properties = centry_uint32(centry);
2422 policy->max_password_age = centry_nttime(centry);
2423 policy->min_password_age = centry_nttime(centry);
2425 status = centry->status;
2427 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2428 domain->name, nt_errstr(status) ));
2430 centry_free(centry);
2431 return status;
2433 do_query:
2434 ZERO_STRUCTP(policy);
2436 /* Return status value returned by seq number check */
2438 if (!NT_STATUS_IS_OK(domain->last_status))
2439 return domain->last_status;
2441 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2442 domain->name ));
2444 status = domain->backend->password_policy(domain, mem_ctx, policy);
2446 /* and save it */
2447 refresh_sequence_number(domain, false);
2448 if (NT_STATUS_IS_OK(status)) {
2449 wcache_save_password_policy(domain, status, policy);
2452 return status;
2456 /* Invalidate cached user and group lists coherently */
2458 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2459 void *state)
2461 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2462 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2463 tdb_delete(the_tdb, kbuf);
2465 return 0;
2468 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2470 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2471 struct netr_SamInfo3 *info3)
2473 DOM_SID sid;
2474 fstring key_str, sid_string;
2475 struct winbind_cache *cache;
2477 /* dont clear cached U/SID and UG/SID entries when we want to logon
2478 * offline - gd */
2480 if (lp_winbind_offline_logon()) {
2481 return;
2484 if (!domain)
2485 return;
2487 cache = get_cache(domain);
2489 if (!cache->tdb) {
2490 return;
2493 sid_copy(&sid, info3->base.domain_sid);
2494 sid_append_rid(&sid, info3->base.rid);
2496 /* Clear U/SID cache entry */
2497 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2498 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2499 tdb_delete(cache->tdb, string_tdb_data(key_str));
2501 /* Clear UG/SID cache entry */
2502 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2503 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2504 tdb_delete(cache->tdb, string_tdb_data(key_str));
2506 /* Samba/winbindd never needs this. */
2507 netsamlogon_clear_cached_user(info3);
2510 bool wcache_invalidate_cache(void)
2512 struct winbindd_domain *domain;
2514 for (domain = domain_list(); domain; domain = domain->next) {
2515 struct winbind_cache *cache = get_cache(domain);
2517 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2518 "entries for %s\n", domain->name));
2519 if (cache) {
2520 if (cache->tdb) {
2521 tdb_traverse(cache->tdb, traverse_fn, NULL);
2522 } else {
2523 return false;
2527 return true;
2530 bool init_wcache(void)
2532 if (wcache == NULL) {
2533 wcache = SMB_XMALLOC_P(struct winbind_cache);
2534 ZERO_STRUCTP(wcache);
2537 if (wcache->tdb != NULL)
2538 return true;
2540 /* when working offline we must not clear the cache on restart */
2541 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2542 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2543 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2544 O_RDWR|O_CREAT, 0600);
2546 if (wcache->tdb == NULL) {
2547 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2548 return false;
2551 return true;
2554 /************************************************************************
2555 This is called by the parent to initialize the cache file.
2556 We don't need sophisticated locking here as we know we're the
2557 only opener.
2558 ************************************************************************/
2560 bool initialize_winbindd_cache(void)
2562 bool cache_bad = true;
2563 uint32 vers;
2565 if (!init_wcache()) {
2566 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2567 return false;
2570 /* Check version number. */
2571 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2572 vers == WINBINDD_CACHE_VERSION) {
2573 cache_bad = false;
2576 if (cache_bad) {
2577 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2578 "and re-creating with version number %d\n",
2579 WINBINDD_CACHE_VERSION ));
2581 tdb_close(wcache->tdb);
2582 wcache->tdb = NULL;
2584 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2585 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2586 cache_path("winbindd_cache.tdb"),
2587 strerror(errno) ));
2588 return false;
2590 if (!init_wcache()) {
2591 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2592 "init_wcache failed.\n"));
2593 return false;
2596 /* Write the version. */
2597 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2598 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2599 tdb_errorstr(wcache->tdb) ));
2600 return false;
2604 tdb_close(wcache->tdb);
2605 wcache->tdb = NULL;
2606 return true;
2609 void close_winbindd_cache(void)
2611 if (!wcache) {
2612 return;
2614 if (wcache->tdb) {
2615 tdb_close(wcache->tdb);
2616 wcache->tdb = NULL;
2620 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2621 char **domain_name, char **name,
2622 enum lsa_SidType *type)
2624 struct winbindd_domain *domain;
2625 struct winbind_cache *cache;
2626 struct cache_entry *centry = NULL;
2627 NTSTATUS status;
2628 fstring tmp;
2630 domain = find_lookup_domain_from_sid(sid);
2631 if (domain == NULL) {
2632 return false;
2635 cache = get_cache(domain);
2637 if (cache->tdb == NULL) {
2638 return false;
2641 centry = wcache_fetch(cache, domain, "SN/%s",
2642 sid_to_fstring(tmp, sid));
2643 if (centry == NULL) {
2644 return false;
2647 if (NT_STATUS_IS_OK(centry->status)) {
2648 *type = (enum lsa_SidType)centry_uint32(centry);
2649 *domain_name = centry_string(centry, mem_ctx);
2650 *name = centry_string(centry, mem_ctx);
2653 status = centry->status;
2654 centry_free(centry);
2655 return NT_STATUS_IS_OK(status);
2658 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2659 const char *domain_name,
2660 const char *name,
2661 DOM_SID *sid,
2662 enum lsa_SidType *type)
2664 struct winbindd_domain *domain;
2665 struct winbind_cache *cache;
2666 struct cache_entry *centry = NULL;
2667 NTSTATUS status;
2668 fstring uname;
2669 bool original_online_state;
2671 domain = find_lookup_domain_from_name(domain_name);
2672 if (domain == NULL) {
2673 return false;
2676 cache = get_cache(domain);
2678 if (cache->tdb == NULL) {
2679 return false;
2682 fstrcpy(uname, name);
2683 strupper_m(uname);
2685 /* If we are doing a cached logon, temporarily set the domain
2686 offline so the cache won't expire the entry */
2688 original_online_state = domain->online;
2689 domain->online = false;
2690 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2691 domain->online = original_online_state;
2693 if (centry == NULL) {
2694 return false;
2697 if (NT_STATUS_IS_OK(centry->status)) {
2698 *type = (enum lsa_SidType)centry_uint32(centry);
2699 centry_sid(centry, mem_ctx, sid);
2702 status = centry->status;
2703 centry_free(centry);
2705 return NT_STATUS_IS_OK(status);
2708 void cache_name2sid(struct winbindd_domain *domain,
2709 const char *domain_name, const char *name,
2710 enum lsa_SidType type, const DOM_SID *sid)
2712 refresh_sequence_number(domain, false);
2713 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2714 sid, type);
2718 * The original idea that this cache only contains centries has
2719 * been blurred - now other stuff gets put in here. Ensure we
2720 * ignore these things on cleanup.
2723 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2724 TDB_DATA dbuf, void *state)
2726 struct cache_entry *centry;
2728 if (is_non_centry_key(kbuf)) {
2729 return 0;
2732 centry = wcache_fetch_raw((char *)kbuf.dptr);
2733 if (!centry) {
2734 return 0;
2737 if (!NT_STATUS_IS_OK(centry->status)) {
2738 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2739 tdb_delete(the_tdb, kbuf);
2742 centry_free(centry);
2743 return 0;
2746 /* flush the cache */
2747 void wcache_flush_cache(void)
2749 if (!wcache)
2750 return;
2751 if (wcache->tdb) {
2752 tdb_close(wcache->tdb);
2753 wcache->tdb = NULL;
2755 if (!winbindd_use_cache()) {
2756 return;
2759 /* when working offline we must not clear the cache on restart */
2760 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2761 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2762 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2763 O_RDWR|O_CREAT, 0600);
2765 if (!wcache->tdb) {
2766 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2767 return;
2770 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2772 DEBUG(10,("wcache_flush_cache success\n"));
2775 /* Count cached creds */
2777 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2778 void *state)
2780 int *cred_count = (int*)state;
2782 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2783 (*cred_count)++;
2785 return 0;
2788 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2790 struct winbind_cache *cache = get_cache(domain);
2792 *count = 0;
2794 if (!cache->tdb) {
2795 return NT_STATUS_INTERNAL_DB_ERROR;
2798 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2800 return NT_STATUS_OK;
2803 struct cred_list {
2804 struct cred_list *prev, *next;
2805 TDB_DATA key;
2806 fstring name;
2807 time_t created;
2809 static struct cred_list *wcache_cred_list;
2811 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2812 void *state)
2814 struct cred_list *cred;
2816 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2818 cred = SMB_MALLOC_P(struct cred_list);
2819 if (cred == NULL) {
2820 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2821 return -1;
2824 ZERO_STRUCTP(cred);
2826 /* save a copy of the key */
2828 fstrcpy(cred->name, (const char *)kbuf.dptr);
2829 DLIST_ADD(wcache_cred_list, cred);
2832 return 0;
2835 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2837 struct winbind_cache *cache = get_cache(domain);
2838 NTSTATUS status;
2839 int ret;
2840 struct cred_list *cred, *oldest = NULL;
2842 if (!cache->tdb) {
2843 return NT_STATUS_INTERNAL_DB_ERROR;
2846 /* we possibly already have an entry */
2847 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2849 fstring key_str, tmp;
2851 DEBUG(11,("we already have an entry, deleting that\n"));
2853 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2855 tdb_delete(cache->tdb, string_tdb_data(key_str));
2857 return NT_STATUS_OK;
2860 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2861 if (ret == 0) {
2862 return NT_STATUS_OK;
2863 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2864 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2867 ZERO_STRUCTP(oldest);
2869 for (cred = wcache_cred_list; cred; cred = cred->next) {
2871 TDB_DATA data;
2872 time_t t;
2874 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2875 if (!data.dptr) {
2876 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2877 cred->name));
2878 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2879 goto done;
2882 t = IVAL(data.dptr, 0);
2883 SAFE_FREE(data.dptr);
2885 if (!oldest) {
2886 oldest = SMB_MALLOC_P(struct cred_list);
2887 if (oldest == NULL) {
2888 status = NT_STATUS_NO_MEMORY;
2889 goto done;
2892 fstrcpy(oldest->name, cred->name);
2893 oldest->created = t;
2894 continue;
2897 if (t < oldest->created) {
2898 fstrcpy(oldest->name, cred->name);
2899 oldest->created = t;
2903 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2904 status = NT_STATUS_OK;
2905 } else {
2906 status = NT_STATUS_UNSUCCESSFUL;
2908 done:
2909 SAFE_FREE(wcache_cred_list);
2910 SAFE_FREE(oldest);
2912 return status;
2915 /* Change the global online/offline state. */
2916 bool set_global_winbindd_state_offline(void)
2918 TDB_DATA data;
2920 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2922 /* Only go offline if someone has created
2923 the key "WINBINDD_OFFLINE" in the cache tdb. */
2925 if (wcache == NULL || wcache->tdb == NULL) {
2926 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2927 return false;
2930 if (!lp_winbind_offline_logon()) {
2931 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2932 return false;
2935 if (global_winbindd_offline_state) {
2936 /* Already offline. */
2937 return true;
2940 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2942 if (!data.dptr || data.dsize != 4) {
2943 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2944 SAFE_FREE(data.dptr);
2945 return false;
2946 } else {
2947 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2948 global_winbindd_offline_state = true;
2949 SAFE_FREE(data.dptr);
2950 return true;
2954 void set_global_winbindd_state_online(void)
2956 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2958 if (!lp_winbind_offline_logon()) {
2959 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2960 return;
2963 if (!global_winbindd_offline_state) {
2964 /* Already online. */
2965 return;
2967 global_winbindd_offline_state = false;
2969 if (!wcache->tdb) {
2970 return;
2973 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2974 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2977 bool get_global_winbindd_state_offline(void)
2979 return global_winbindd_offline_state;
2982 /***********************************************************************
2983 Validate functions for all possible cache tdb keys.
2984 ***********************************************************************/
2986 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2987 struct tdb_validation_status *state)
2989 struct cache_entry *centry;
2991 centry = SMB_XMALLOC_P(struct cache_entry);
2992 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2993 if (!centry->data) {
2994 SAFE_FREE(centry);
2995 return NULL;
2997 centry->len = data.dsize;
2998 centry->ofs = 0;
3000 if (centry->len < 8) {
3001 /* huh? corrupt cache? */
3002 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3003 centry_free(centry);
3004 state->bad_entry = true;
3005 state->success = false;
3006 return NULL;
3009 centry->status = NT_STATUS(centry_uint32(centry));
3010 centry->sequence_number = centry_uint32(centry);
3011 return centry;
3014 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3015 struct tdb_validation_status *state)
3017 if (dbuf.dsize != 8) {
3018 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3019 keystr, (unsigned int)dbuf.dsize ));
3020 state->bad_entry = true;
3021 return 1;
3023 return 0;
3026 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3027 struct tdb_validation_status *state)
3029 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3030 if (!centry) {
3031 return 1;
3034 (void)centry_uint32(centry);
3035 if (NT_STATUS_IS_OK(centry->status)) {
3036 DOM_SID sid;
3037 (void)centry_sid(centry, mem_ctx, &sid);
3040 centry_free(centry);
3042 if (!(state->success)) {
3043 return 1;
3045 DEBUG(10,("validate_ns: %s ok\n", keystr));
3046 return 0;
3049 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3050 struct tdb_validation_status *state)
3052 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3053 if (!centry) {
3054 return 1;
3057 if (NT_STATUS_IS_OK(centry->status)) {
3058 (void)centry_uint32(centry);
3059 (void)centry_string(centry, mem_ctx);
3060 (void)centry_string(centry, mem_ctx);
3063 centry_free(centry);
3065 if (!(state->success)) {
3066 return 1;
3068 DEBUG(10,("validate_sn: %s ok\n", keystr));
3069 return 0;
3072 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3073 struct tdb_validation_status *state)
3075 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3076 DOM_SID sid;
3078 if (!centry) {
3079 return 1;
3082 (void)centry_string(centry, mem_ctx);
3083 (void)centry_string(centry, mem_ctx);
3084 (void)centry_string(centry, mem_ctx);
3085 (void)centry_string(centry, mem_ctx);
3086 (void)centry_uint32(centry);
3087 (void)centry_sid(centry, mem_ctx, &sid);
3088 (void)centry_sid(centry, mem_ctx, &sid);
3090 centry_free(centry);
3092 if (!(state->success)) {
3093 return 1;
3095 DEBUG(10,("validate_u: %s ok\n", keystr));
3096 return 0;
3099 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3100 struct tdb_validation_status *state)
3102 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3104 if (!centry) {
3105 return 1;
3108 (void)centry_nttime(centry);
3109 (void)centry_nttime(centry);
3110 (void)centry_uint16(centry);
3112 centry_free(centry);
3114 if (!(state->success)) {
3115 return 1;
3117 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3118 return 0;
3121 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3122 struct tdb_validation_status *state)
3124 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3126 if (!centry) {
3127 return 1;
3130 (void)centry_uint16(centry);
3131 (void)centry_uint16(centry);
3132 (void)centry_uint32(centry);
3133 (void)centry_nttime(centry);
3134 (void)centry_nttime(centry);
3136 centry_free(centry);
3138 if (!(state->success)) {
3139 return 1;
3141 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3142 return 0;
3145 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3146 struct tdb_validation_status *state)
3148 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3150 if (!centry) {
3151 return 1;
3154 (void)centry_time(centry);
3155 (void)centry_hash16(centry, mem_ctx);
3157 /* We only have 17 bytes more data in the salted cred case. */
3158 if (centry->len - centry->ofs == 17) {
3159 (void)centry_hash16(centry, mem_ctx);
3162 centry_free(centry);
3164 if (!(state->success)) {
3165 return 1;
3167 DEBUG(10,("validate_cred: %s ok\n", keystr));
3168 return 0;
3171 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3172 struct tdb_validation_status *state)
3174 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3175 int32 num_entries, i;
3177 if (!centry) {
3178 return 1;
3181 num_entries = (int32)centry_uint32(centry);
3183 for (i=0; i< num_entries; i++) {
3184 DOM_SID sid;
3185 (void)centry_string(centry, mem_ctx);
3186 (void)centry_string(centry, mem_ctx);
3187 (void)centry_string(centry, mem_ctx);
3188 (void)centry_string(centry, mem_ctx);
3189 (void)centry_sid(centry, mem_ctx, &sid);
3190 (void)centry_sid(centry, mem_ctx, &sid);
3193 centry_free(centry);
3195 if (!(state->success)) {
3196 return 1;
3198 DEBUG(10,("validate_ul: %s ok\n", keystr));
3199 return 0;
3202 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3203 struct tdb_validation_status *state)
3205 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3206 int32 num_entries, i;
3208 if (!centry) {
3209 return 1;
3212 num_entries = centry_uint32(centry);
3214 for (i=0; i< num_entries; i++) {
3215 (void)centry_string(centry, mem_ctx);
3216 (void)centry_string(centry, mem_ctx);
3217 (void)centry_uint32(centry);
3220 centry_free(centry);
3222 if (!(state->success)) {
3223 return 1;
3225 DEBUG(10,("validate_gl: %s ok\n", keystr));
3226 return 0;
3229 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3230 struct tdb_validation_status *state)
3232 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3233 int32 num_groups, i;
3235 if (!centry) {
3236 return 1;
3239 num_groups = centry_uint32(centry);
3241 for (i=0; i< num_groups; i++) {
3242 DOM_SID sid;
3243 centry_sid(centry, mem_ctx, &sid);
3246 centry_free(centry);
3248 if (!(state->success)) {
3249 return 1;
3251 DEBUG(10,("validate_ug: %s ok\n", keystr));
3252 return 0;
3255 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3256 struct tdb_validation_status *state)
3258 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3259 int32 num_aliases, i;
3261 if (!centry) {
3262 return 1;
3265 num_aliases = centry_uint32(centry);
3267 for (i=0; i < num_aliases; i++) {
3268 (void)centry_uint32(centry);
3271 centry_free(centry);
3273 if (!(state->success)) {
3274 return 1;
3276 DEBUG(10,("validate_ua: %s ok\n", keystr));
3277 return 0;
3280 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3281 struct tdb_validation_status *state)
3283 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3284 int32 num_names, i;
3286 if (!centry) {
3287 return 1;
3290 num_names = centry_uint32(centry);
3292 for (i=0; i< num_names; i++) {
3293 DOM_SID sid;
3294 centry_sid(centry, mem_ctx, &sid);
3295 (void)centry_string(centry, mem_ctx);
3296 (void)centry_uint32(centry);
3299 centry_free(centry);
3301 if (!(state->success)) {
3302 return 1;
3304 DEBUG(10,("validate_gm: %s ok\n", keystr));
3305 return 0;
3308 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3309 struct tdb_validation_status *state)
3311 /* Can't say anything about this other than must be nonzero. */
3312 if (dbuf.dsize == 0) {
3313 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3314 keystr));
3315 state->bad_entry = true;
3316 state->success = false;
3317 return 1;
3320 DEBUG(10,("validate_dr: %s ok\n", keystr));
3321 return 0;
3324 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3325 struct tdb_validation_status *state)
3327 /* Can't say anything about this other than must be nonzero. */
3328 if (dbuf.dsize == 0) {
3329 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3330 keystr));
3331 state->bad_entry = true;
3332 state->success = false;
3333 return 1;
3336 DEBUG(10,("validate_de: %s ok\n", keystr));
3337 return 0;
3340 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3341 TDB_DATA dbuf, struct tdb_validation_status *state)
3343 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3345 if (!centry) {
3346 return 1;
3349 (void)centry_string(centry, mem_ctx);
3350 (void)centry_string(centry, mem_ctx);
3351 (void)centry_string(centry, mem_ctx);
3352 (void)centry_uint32(centry);
3354 centry_free(centry);
3356 if (!(state->success)) {
3357 return 1;
3359 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3360 return 0;
3363 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3364 TDB_DATA dbuf,
3365 struct tdb_validation_status *state)
3367 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3369 if (!centry) {
3370 return 1;
3373 (void)centry_string( centry, mem_ctx );
3375 centry_free(centry);
3377 if (!(state->success)) {
3378 return 1;
3380 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3381 return 0;
3384 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3385 TDB_DATA dbuf,
3386 struct tdb_validation_status *state)
3388 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3390 if (!centry) {
3391 return 1;
3394 (void)centry_string( centry, mem_ctx );
3396 centry_free(centry);
3398 if (!(state->success)) {
3399 return 1;
3401 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3402 return 0;
3405 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3406 struct tdb_validation_status *state)
3408 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3409 int32 num_domains, i;
3411 if (!centry) {
3412 return 1;
3415 num_domains = centry_uint32(centry);
3417 for (i=0; i< num_domains; i++) {
3418 DOM_SID sid;
3419 (void)centry_string(centry, mem_ctx);
3420 (void)centry_string(centry, mem_ctx);
3421 (void)centry_sid(centry, mem_ctx, &sid);
3424 centry_free(centry);
3426 if (!(state->success)) {
3427 return 1;
3429 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3430 return 0;
3433 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3434 TDB_DATA dbuf,
3435 struct tdb_validation_status *state)
3437 if (dbuf.dsize == 0) {
3438 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3439 "key %s (len ==0) ?\n", keystr));
3440 state->bad_entry = true;
3441 state->success = false;
3442 return 1;
3445 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3446 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3447 return 0;
3450 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3451 struct tdb_validation_status *state)
3453 if (dbuf.dsize != 4) {
3454 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3455 keystr, (unsigned int)dbuf.dsize ));
3456 state->bad_entry = true;
3457 state->success = false;
3458 return 1;
3460 DEBUG(10,("validate_offline: %s ok\n", keystr));
3461 return 0;
3464 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3465 struct tdb_validation_status *state)
3467 if (dbuf.dsize != 4) {
3468 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3469 "key %s (len %u != 4) ?\n",
3470 keystr, (unsigned int)dbuf.dsize));
3471 state->bad_entry = true;
3472 state->success = false;
3473 return 1;
3476 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3477 return 0;
3480 /***********************************************************************
3481 A list of all possible cache tdb keys with associated validation
3482 functions.
3483 ***********************************************************************/
3485 struct key_val_struct {
3486 const char *keyname;
3487 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3488 } key_val[] = {
3489 {"SEQNUM/", validate_seqnum},
3490 {"NS/", validate_ns},
3491 {"SN/", validate_sn},
3492 {"U/", validate_u},
3493 {"LOC_POL/", validate_loc_pol},
3494 {"PWD_POL/", validate_pwd_pol},
3495 {"CRED/", validate_cred},
3496 {"UL/", validate_ul},
3497 {"GL/", validate_gl},
3498 {"UG/", validate_ug},
3499 {"UA", validate_ua},
3500 {"GM/", validate_gm},
3501 {"DR/", validate_dr},
3502 {"DE/", validate_de},
3503 {"NSS/PWINFO/", validate_pwinfo},
3504 {"TRUSTDOMS/", validate_trustdoms},
3505 {"TRUSTDOMCACHE/", validate_trustdomcache},
3506 {"NSS/NA/", validate_nss_na},
3507 {"NSS/AN/", validate_nss_an},
3508 {"WINBINDD_OFFLINE", validate_offline},
3509 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3510 {NULL, NULL}
3513 /***********************************************************************
3514 Function to look at every entry in the tdb and validate it as far as
3515 possible.
3516 ***********************************************************************/
3518 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3520 int i;
3521 unsigned int max_key_len = 1024;
3522 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3524 /* Paranoia check. */
3525 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3526 max_key_len = 1024 * 1024;
3528 if (kbuf.dsize > max_key_len) {
3529 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3530 "(%u) > (%u)\n\n",
3531 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3532 return 1;
3535 for (i = 0; key_val[i].keyname; i++) {
3536 size_t namelen = strlen(key_val[i].keyname);
3537 if (kbuf.dsize >= namelen && (
3538 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3539 TALLOC_CTX *mem_ctx;
3540 char *keystr;
3541 int ret;
3543 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3544 if (!keystr) {
3545 return 1;
3547 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3548 keystr[kbuf.dsize] = '\0';
3550 mem_ctx = talloc_init("validate_ctx");
3551 if (!mem_ctx) {
3552 SAFE_FREE(keystr);
3553 return 1;
3556 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3557 v_state);
3559 SAFE_FREE(keystr);
3560 talloc_destroy(mem_ctx);
3561 return ret;
3565 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3566 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3567 DEBUG(0,("data :\n"));
3568 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3569 v_state->unknown_key = true;
3570 v_state->success = false;
3571 return 1; /* terminate. */
3574 static void validate_panic(const char *const why)
3576 DEBUG(0,("validating cache: would panic %s\n", why ));
3577 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3578 exit(47);
3581 /***********************************************************************
3582 Try and validate every entry in the winbindd cache. If we fail here,
3583 delete the cache tdb and return non-zero.
3584 ***********************************************************************/
3586 int winbindd_validate_cache(void)
3588 int ret = -1;
3589 const char *tdb_path = cache_path("winbindd_cache.tdb");
3590 TDB_CONTEXT *tdb = NULL;
3592 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3593 smb_panic_fn = validate_panic;
3596 tdb = tdb_open_log(tdb_path,
3597 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3598 ( lp_winbind_offline_logon()
3599 ? TDB_DEFAULT
3600 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3601 O_RDWR|O_CREAT,
3602 0600);
3603 if (!tdb) {
3604 DEBUG(0, ("winbindd_validate_cache: "
3605 "error opening/initializing tdb\n"));
3606 goto done;
3608 tdb_close(tdb);
3610 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3612 if (ret != 0) {
3613 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3614 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3615 unlink(tdb_path);
3618 done:
3619 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3620 smb_panic_fn = smb_panic;
3621 return ret;
3624 /***********************************************************************
3625 Try and validate every entry in the winbindd cache.
3626 ***********************************************************************/
3628 int winbindd_validate_cache_nobackup(void)
3630 int ret = -1;
3631 const char *tdb_path = cache_path("winbindd_cache.tdb");
3633 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3634 smb_panic_fn = validate_panic;
3637 if (wcache == NULL || wcache->tdb == NULL) {
3638 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3639 } else {
3640 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3643 if (ret != 0) {
3644 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3645 "successful.\n"));
3648 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3649 "function\n"));
3650 smb_panic_fn = smb_panic;
3651 return ret;
3654 bool winbindd_cache_validate_and_initialize(void)
3656 close_winbindd_cache();
3658 if (lp_winbind_offline_logon()) {
3659 if (winbindd_validate_cache() < 0) {
3660 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3661 "could be restored.\n"));
3665 return initialize_winbindd_cache();
3668 /*********************************************************************
3669 ********************************************************************/
3671 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3672 struct winbindd_tdc_domain **domains,
3673 size_t *num_domains )
3675 struct winbindd_tdc_domain *list = NULL;
3676 size_t idx;
3677 int i;
3678 bool set_only = false;
3680 /* don't allow duplicates */
3682 idx = *num_domains;
3683 list = *domains;
3685 for ( i=0; i< (*num_domains); i++ ) {
3686 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3687 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3688 new_dom->name));
3689 idx = i;
3690 set_only = true;
3692 break;
3696 if ( !set_only ) {
3697 if ( !*domains ) {
3698 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3699 idx = 0;
3700 } else {
3701 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3702 struct winbindd_tdc_domain,
3703 (*num_domains)+1);
3704 idx = *num_domains;
3707 ZERO_STRUCT( list[idx] );
3710 if ( !list )
3711 return false;
3713 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3714 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3716 if ( !is_null_sid( &new_dom->sid ) ) {
3717 sid_copy( &list[idx].sid, &new_dom->sid );
3718 } else {
3719 sid_copy(&list[idx].sid, &global_sid_NULL);
3722 if ( new_dom->domain_flags != 0x0 )
3723 list[idx].trust_flags = new_dom->domain_flags;
3725 if ( new_dom->domain_type != 0x0 )
3726 list[idx].trust_type = new_dom->domain_type;
3728 if ( new_dom->domain_trust_attribs != 0x0 )
3729 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3731 if ( !set_only ) {
3732 *domains = list;
3733 *num_domains = idx + 1;
3736 return true;
3739 /*********************************************************************
3740 ********************************************************************/
3742 static TDB_DATA make_tdc_key( const char *domain_name )
3744 char *keystr = NULL;
3745 TDB_DATA key = { NULL, 0 };
3747 if ( !domain_name ) {
3748 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3749 return key;
3753 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3754 return key;
3756 key = string_term_tdb_data(keystr);
3758 return key;
3761 /*********************************************************************
3762 ********************************************************************/
3764 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3765 size_t num_domains,
3766 unsigned char **buf )
3768 unsigned char *buffer = NULL;
3769 int len = 0;
3770 int buflen = 0;
3771 int i = 0;
3773 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3774 (int)num_domains));
3776 buflen = 0;
3778 again:
3779 len = 0;
3781 /* Store the number of array items first */
3782 len += tdb_pack( buffer+len, buflen-len, "d",
3783 num_domains );
3785 /* now pack each domain trust record */
3786 for ( i=0; i<num_domains; i++ ) {
3788 fstring tmp;
3790 if ( buflen > 0 ) {
3791 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3792 domains[i].domain_name,
3793 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3796 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3797 domains[i].domain_name,
3798 domains[i].dns_name,
3799 sid_to_fstring(tmp, &domains[i].sid),
3800 domains[i].trust_flags,
3801 domains[i].trust_attribs,
3802 domains[i].trust_type );
3805 if ( buflen < len ) {
3806 SAFE_FREE(buffer);
3807 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3808 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3809 buflen = -1;
3810 goto done;
3812 buflen = len;
3813 goto again;
3816 *buf = buffer;
3818 done:
3819 return buflen;
3822 /*********************************************************************
3823 ********************************************************************/
3825 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3826 struct winbindd_tdc_domain **domains )
3828 fstring domain_name, dns_name, sid_string;
3829 uint32 type, attribs, flags;
3830 int num_domains;
3831 int len = 0;
3832 int i;
3833 struct winbindd_tdc_domain *list = NULL;
3835 /* get the number of domains */
3836 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3837 if ( len == -1 ) {
3838 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3839 return 0;
3842 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3843 if ( !list ) {
3844 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3845 return 0;
3848 for ( i=0; i<num_domains; i++ ) {
3849 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3850 domain_name,
3851 dns_name,
3852 sid_string,
3853 &flags,
3854 &attribs,
3855 &type );
3857 if ( len == -1 ) {
3858 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3859 TALLOC_FREE( list );
3860 return 0;
3863 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3864 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3865 domain_name, dns_name, sid_string,
3866 flags, attribs, type));
3868 list[i].domain_name = talloc_strdup( list, domain_name );
3869 list[i].dns_name = talloc_strdup( list, dns_name );
3870 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3871 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3872 domain_name));
3874 list[i].trust_flags = flags;
3875 list[i].trust_attribs = attribs;
3876 list[i].trust_type = type;
3879 *domains = list;
3881 return num_domains;
3884 /*********************************************************************
3885 ********************************************************************/
3887 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3889 TDB_DATA key = make_tdc_key( lp_workgroup() );
3890 TDB_DATA data = { NULL, 0 };
3891 int ret;
3893 if ( !key.dptr )
3894 return false;
3896 /* See if we were asked to delete the cache entry */
3898 if ( !domains ) {
3899 ret = tdb_delete( wcache->tdb, key );
3900 goto done;
3903 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3905 if ( !data.dptr ) {
3906 ret = -1;
3907 goto done;
3910 ret = tdb_store( wcache->tdb, key, data, 0 );
3912 done:
3913 SAFE_FREE( data.dptr );
3914 SAFE_FREE( key.dptr );
3916 return ( ret != -1 );
3919 /*********************************************************************
3920 ********************************************************************/
3922 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3924 TDB_DATA key = make_tdc_key( lp_workgroup() );
3925 TDB_DATA data = { NULL, 0 };
3927 *domains = NULL;
3928 *num_domains = 0;
3930 if ( !key.dptr )
3931 return false;
3933 data = tdb_fetch( wcache->tdb, key );
3935 SAFE_FREE( key.dptr );
3937 if ( !data.dptr )
3938 return false;
3940 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3942 SAFE_FREE( data.dptr );
3944 if ( !*domains )
3945 return false;
3947 return true;
3950 /*********************************************************************
3951 ********************************************************************/
3953 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3955 struct winbindd_tdc_domain *dom_list = NULL;
3956 size_t num_domains = 0;
3957 bool ret = false;
3959 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3960 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3961 domain->name, domain->alt_name,
3962 sid_string_dbg(&domain->sid),
3963 domain->domain_flags,
3964 domain->domain_trust_attribs,
3965 domain->domain_type));
3967 if ( !init_wcache() ) {
3968 return false;
3971 /* fetch the list */
3973 wcache_tdc_fetch_list( &dom_list, &num_domains );
3975 /* add the new domain */
3977 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3978 goto done;
3981 /* pack the domain */
3983 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3984 goto done;
3987 /* Success */
3989 ret = true;
3990 done:
3991 TALLOC_FREE( dom_list );
3993 return ret;
3996 /*********************************************************************
3997 ********************************************************************/
3999 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4001 struct winbindd_tdc_domain *dom_list = NULL;
4002 size_t num_domains = 0;
4003 int i;
4004 struct winbindd_tdc_domain *d = NULL;
4006 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4008 if ( !init_wcache() ) {
4009 return false;
4012 /* fetch the list */
4014 wcache_tdc_fetch_list( &dom_list, &num_domains );
4016 for ( i=0; i<num_domains; i++ ) {
4017 if ( strequal(name, dom_list[i].domain_name) ||
4018 strequal(name, dom_list[i].dns_name) )
4020 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4021 name));
4023 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4024 if ( !d )
4025 break;
4027 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4028 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4029 sid_copy( &d->sid, &dom_list[i].sid );
4030 d->trust_flags = dom_list[i].trust_flags;
4031 d->trust_type = dom_list[i].trust_type;
4032 d->trust_attribs = dom_list[i].trust_attribs;
4034 break;
4038 TALLOC_FREE( dom_list );
4040 return d;
4044 /*********************************************************************
4045 ********************************************************************/
4047 void wcache_tdc_clear( void )
4049 if ( !init_wcache() )
4050 return;
4052 wcache_tdc_store_list( NULL, 0 );
4054 return;
4058 /*********************************************************************
4059 ********************************************************************/
4061 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4062 NTSTATUS status,
4063 const DOM_SID *user_sid,
4064 const char *homedir,
4065 const char *shell,
4066 const char *gecos,
4067 uint32 gid)
4069 struct cache_entry *centry;
4070 fstring tmp;
4072 if ( (centry = centry_start(domain, status)) == NULL )
4073 return;
4075 centry_put_string( centry, homedir );
4076 centry_put_string( centry, shell );
4077 centry_put_string( centry, gecos );
4078 centry_put_uint32( centry, gid );
4080 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4082 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4084 centry_free(centry);
4087 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4088 const DOM_SID *user_sid,
4089 TALLOC_CTX *ctx,
4090 ADS_STRUCT *ads, LDAPMessage *msg,
4091 char **homedir, char **shell, char **gecos,
4092 gid_t *p_gid)
4094 struct winbind_cache *cache = get_cache(domain);
4095 struct cache_entry *centry = NULL;
4096 NTSTATUS nt_status;
4097 fstring tmp;
4099 if (!cache->tdb)
4100 goto do_query;
4102 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4103 sid_to_fstring(tmp, user_sid));
4105 if (!centry)
4106 goto do_query;
4108 *homedir = centry_string( centry, ctx );
4109 *shell = centry_string( centry, ctx );
4110 *gecos = centry_string( centry, ctx );
4111 *p_gid = centry_uint32( centry );
4113 centry_free(centry);
4115 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4116 sid_string_dbg(user_sid)));
4118 return NT_STATUS_OK;
4120 do_query:
4122 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4123 homedir, shell, gecos, p_gid );
4125 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4127 if ( NT_STATUS_IS_OK(nt_status) ) {
4128 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4129 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4130 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4131 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4133 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4134 *homedir, *shell, *gecos, *p_gid );
4137 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4138 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4139 domain->name ));
4140 set_domain_offline( domain );
4143 return nt_status;
4147 /* the cache backend methods are exposed via this structure */
4148 struct winbindd_methods cache_methods = {
4149 true,
4150 query_user_list,
4151 enum_dom_groups,
4152 enum_local_groups,
4153 name_to_sid,
4154 sid_to_name,
4155 rids_to_names,
4156 query_user,
4157 lookup_usergroups,
4158 lookup_useraliases,
4159 lookup_groupmem,
4160 sequence_number,
4161 lockout_policy,
4162 password_policy,
4163 trusted_domains