After technical consultation, add Steven Danneman's <steven.danneman@isilon.com>...
[Samba.git] / source / winbindd / winbindd_cache.c
blobdda8b03d5f9d28fd5ddaefdd74a243ca453186db
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "winbindd.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods;
36 extern bool opt_nocache;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40 extern struct winbindd_methods builtin_passdb_methods;
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys[] = {
49 "SEQNUM/",
50 "DR/",
51 "DE/",
52 "WINBINDD_OFFLINE",
53 WINBINDD_CACHE_VERSION_KEYSTR,
54 NULL
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static bool is_non_centry_key(TDB_DATA kbuf)
63 int i;
65 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66 return false;
68 for (i = 0; non_centry_keys[i] != NULL; i++) {
69 size_t namelen = strlen(non_centry_keys[i]);
70 if (kbuf.dsize < namelen) {
71 continue;
73 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74 return true;
77 return false;
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static bool global_winbindd_offline_state;
86 struct winbind_cache {
87 TDB_CONTEXT *tdb;
90 struct cache_entry {
91 NTSTATUS status;
92 uint32 sequence_number;
93 uint8 *data;
94 uint32 len, ofs;
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache *wcache;
103 void winbindd_check_cache_size(time_t t)
105 static time_t last_check_time;
106 struct stat st;
108 if (last_check_time == (time_t)0)
109 last_check_time = t;
111 if (t - last_check_time < 60 && t - last_check_time > 0)
112 return;
114 if (wcache == NULL || wcache->tdb == NULL) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116 return;
119 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
121 return;
124 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st.st_size,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
135 struct winbind_cache *ret = wcache;
137 /* We have to know what type of domain we are dealing with first. */
139 if (domain->internal) {
140 domain->backend = &builtin_passdb_methods;
141 domain->initialized = True;
143 if ( !domain->initialized ) {
144 init_dc_connection( domain );
148 OK. listen up becasue I'm only going to say this once.
149 We have the following scenarios to consider
150 (a) trusted AD domains on a Samba DC,
151 (b) trusted AD domains and we are joined to a non-kerberos domain
152 (c) trusted AD domains and we are joined to a kerberos (AD) domain
154 For (a) we can always contact the trusted domain using krb5
155 since we have the domain trust account password
157 For (b) we can only use RPC since we have no way of
158 getting a krb5 ticket in our own domain
160 For (c) we can always use krb5 since we have a kerberos trust
162 --jerry
165 if (!domain->backend) {
166 #ifdef HAVE_ADS
167 struct winbindd_domain *our_domain = domain;
169 /* find our domain first so we can figure out if we
170 are joined to a kerberized domain */
172 if ( !domain->primary )
173 our_domain = find_our_domain();
175 if ((our_domain->active_directory || IS_DC)
176 && domain->active_directory
177 && !lp_winbind_rpc_only()) {
178 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
179 domain->backend = &ads_methods;
180 } else {
181 #endif /* HAVE_ADS */
182 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
183 domain->backend = &reconnect_methods;
184 #ifdef HAVE_ADS
186 #endif /* HAVE_ADS */
189 if (ret)
190 return ret;
192 ret = SMB_XMALLOC_P(struct winbind_cache);
193 ZERO_STRUCTP(ret);
195 wcache = ret;
196 wcache_flush_cache();
198 return ret;
202 free a centry structure
204 static void centry_free(struct cache_entry *centry)
206 if (!centry)
207 return;
208 SAFE_FREE(centry->data);
209 free(centry);
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
214 if (centry->len - centry->ofs < nbytes) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes,
217 centry->len - centry->ofs));
218 return false;
220 return true;
224 pull a uint32 from a cache entry
226 static uint32 centry_uint32(struct cache_entry *centry)
228 uint32 ret;
230 if (!centry_check_bytes(centry, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret = IVAL(centry->data, centry->ofs);
234 centry->ofs += 4;
235 return ret;
239 pull a uint16 from a cache entry
241 static uint16 centry_uint16(struct cache_entry *centry)
243 uint16 ret;
244 if (!centry_check_bytes(centry, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 2;
249 return ret;
253 pull a uint8 from a cache entry
255 static uint8 centry_uint8(struct cache_entry *centry)
257 uint8 ret;
258 if (!centry_check_bytes(centry, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret = CVAL(centry->data, centry->ofs);
262 centry->ofs += 1;
263 return ret;
267 pull a NTTIME from a cache entry
269 static NTTIME centry_nttime(struct cache_entry *centry)
271 NTTIME ret;
272 if (!centry_check_bytes(centry, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret = IVAL(centry->data, centry->ofs);
276 centry->ofs += 4;
277 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
278 centry->ofs += 4;
279 return ret;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry *centry)
287 return (time_t)centry_nttime(centry);
290 /* pull a string from a cache entry, using the supplied
291 talloc context
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
295 uint32 len;
296 char *ret;
298 len = centry_uint8(centry);
300 if (len == 0xFF) {
301 /* a deliberate NULL string */
302 return NULL;
305 if (!centry_check_bytes(centry, (size_t)len)) {
306 smb_panic_fn("centry_string");
309 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310 if (!ret) {
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret,centry->data + centry->ofs, len);
314 ret[len] = 0;
315 centry->ofs += len;
316 return ret;
319 /* pull a hash16 from a cache entry, using the supplied
320 talloc context
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
324 uint32 len;
325 char *ret;
327 len = centry_uint8(centry);
329 if (len != 16) {
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
331 len ));
332 return NULL;
335 if (!centry_check_bytes(centry, 16)) {
336 return NULL;
339 ret = TALLOC_ARRAY(mem_ctx, char, 16);
340 if (!ret) {
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret,centry->data + centry->ofs, 16);
344 centry->ofs += 16;
345 return ret;
348 /* pull a sid from a cache entry, using the supplied
349 talloc context
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
353 char *sid_string;
354 sid_string = centry_string(centry, mem_ctx);
355 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
356 return false;
358 return true;
363 pull a NTSTATUS from a cache entry
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
367 NTSTATUS status;
369 status = NT_STATUS(centry_uint32(centry));
370 return status;
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain *domain)
377 bool ret;
379 if (!wcache->tdb)
380 return false;
382 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
384 if (ret)
385 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
386 domain->name ));
387 return ret;
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
392 TDB_DATA data;
393 fstring key;
394 uint32 time_diff;
396 if (!wcache->tdb) {
397 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398 return NT_STATUS_UNSUCCESSFUL;
401 fstr_sprintf( key, "SEQNUM/%s", domain->name );
403 data = tdb_fetch_bystring( wcache->tdb, key );
404 if ( !data.dptr || data.dsize!=8 ) {
405 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
406 return NT_STATUS_UNSUCCESSFUL;
409 domain->sequence_number = IVAL(data.dptr, 0);
410 domain->last_seq_check = IVAL(data.dptr, 4);
412 SAFE_FREE(data.dptr);
414 /* have we expired? */
416 time_diff = now - domain->last_seq_check;
417 if ( time_diff > lp_winbind_cache_time() ) {
418 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419 domain->name, domain->sequence_number,
420 (uint32)domain->last_seq_check));
421 return NT_STATUS_UNSUCCESSFUL;
424 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
425 domain->name, domain->sequence_number,
426 (uint32)domain->last_seq_check));
428 return NT_STATUS_OK;
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
433 TDB_DATA data;
434 fstring key_str;
435 uint8 buf[8];
437 if (!wcache->tdb) {
438 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439 return NT_STATUS_UNSUCCESSFUL;
442 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
444 SIVAL(buf, 0, domain->sequence_number);
445 SIVAL(buf, 4, domain->last_seq_check);
446 data.dptr = buf;
447 data.dsize = 8;
449 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
450 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
451 return NT_STATUS_UNSUCCESSFUL;
454 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
455 domain->name, domain->sequence_number,
456 (uint32)domain->last_seq_check));
458 return NT_STATUS_OK;
462 refresh the domain sequence number. If force is true
463 then always refresh it, no matter how recently we fetched it
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
468 NTSTATUS status;
469 unsigned time_diff;
470 time_t t = time(NULL);
471 unsigned cache_time = lp_winbind_cache_time();
473 if ( IS_DOMAIN_OFFLINE(domain) ) {
474 return;
477 get_cache( domain );
479 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
480 /* trying to reconnect is expensive, don't do it too often */
481 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
482 cache_time *= 8;
484 #endif
486 time_diff = t - domain->last_seq_check;
488 /* see if we have to refetch the domain sequence number */
489 if (!force && (time_diff < cache_time)) {
490 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
491 goto done;
494 /* try to get the sequence number from the tdb cache first */
495 /* this will update the timestamp as well */
497 status = fetch_cache_seqnum( domain, t );
498 if ( NT_STATUS_IS_OK(status) )
499 goto done;
501 /* important! make sure that we know if this is a native
502 mode domain or not. And that we can contact it. */
504 if ( winbindd_can_contact_domain( domain ) ) {
505 struct winbindd_methods *orig_backend = domain->backend;
506 status = domain->backend->sequence_number(domain,
507 &domain->sequence_number);
508 if (domain->backend != orig_backend) {
509 /* Try again. */
510 status = domain->backend->sequence_number(domain,
511 &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 (opt_nocache) {
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 (opt_nocache) {
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);
943 static void wcache_save_password_policy(struct winbindd_domain *domain,
944 NTSTATUS status,
945 struct samr_DomInfo1 *policy)
947 struct cache_entry *centry;
949 centry = centry_start(domain, status);
950 if (!centry)
951 return;
953 centry_put_uint16(centry, policy->min_password_length);
954 centry_put_uint16(centry, policy->password_history_length);
955 centry_put_uint32(centry, policy->password_properties);
956 centry_put_nttime(centry, policy->max_password_age);
957 centry_put_nttime(centry, policy->min_password_age);
959 centry_end(centry, "PWD_POL/%s", domain->name);
961 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
963 centry_free(centry);
966 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
968 struct winbind_cache *cache = get_cache(domain);
969 TDB_DATA data;
970 fstring key_str, tmp;
971 uint32 rid;
973 if (!cache->tdb) {
974 return NT_STATUS_INTERNAL_DB_ERROR;
977 if (is_null_sid(sid)) {
978 return NT_STATUS_INVALID_SID;
981 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
982 return NT_STATUS_INVALID_SID;
985 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
987 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
988 if (!data.dptr) {
989 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
992 SAFE_FREE(data.dptr);
993 return NT_STATUS_OK;
996 /* Lookup creds for a SID - copes with old (unsalted) creds as well
997 as new salted ones. */
999 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1000 TALLOC_CTX *mem_ctx,
1001 const DOM_SID *sid,
1002 const uint8 **cached_nt_pass,
1003 const uint8 **cached_salt)
1005 struct winbind_cache *cache = get_cache(domain);
1006 struct cache_entry *centry = NULL;
1007 NTSTATUS status;
1008 time_t t;
1009 uint32 rid;
1010 fstring tmp;
1012 if (!cache->tdb) {
1013 return NT_STATUS_INTERNAL_DB_ERROR;
1016 if (is_null_sid(sid)) {
1017 return NT_STATUS_INVALID_SID;
1020 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1021 return NT_STATUS_INVALID_SID;
1024 /* Try and get a salted cred first. If we can't
1025 fall back to an unsalted cred. */
1027 centry = wcache_fetch(cache, domain, "CRED/%s",
1028 sid_to_fstring(tmp, sid));
1029 if (!centry) {
1030 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1031 sid_string_dbg(sid)));
1032 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1035 t = centry_time(centry);
1037 /* In the salted case this isn't actually the nt_hash itself,
1038 but the MD5 of the salt + nt_hash. Let the caller
1039 sort this out. It can tell as we only return the cached_salt
1040 if we are returning a salted cred. */
1042 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1043 if (*cached_nt_pass == NULL) {
1044 fstring sidstr;
1046 sid_to_fstring(sidstr, sid);
1048 /* Bad (old) cred cache. Delete and pretend we
1049 don't have it. */
1050 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1051 sidstr));
1052 wcache_delete("CRED/%s", sidstr);
1053 centry_free(centry);
1054 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1057 /* We only have 17 bytes more data in the salted cred case. */
1058 if (centry->len - centry->ofs == 17) {
1059 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1060 } else {
1061 *cached_salt = NULL;
1064 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1065 if (*cached_salt) {
1066 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1069 status = centry->status;
1071 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1072 sid_string_dbg(sid), nt_errstr(status) ));
1074 centry_free(centry);
1075 return status;
1078 /* Store creds for a SID - only writes out new salted ones. */
1080 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1081 TALLOC_CTX *mem_ctx,
1082 const DOM_SID *sid,
1083 const uint8 nt_pass[NT_HASH_LEN])
1085 struct cache_entry *centry;
1086 fstring sid_string;
1087 uint32 rid;
1088 uint8 cred_salt[NT_HASH_LEN];
1089 uint8 salted_hash[NT_HASH_LEN];
1091 if (is_null_sid(sid)) {
1092 return NT_STATUS_INVALID_SID;
1095 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1096 return NT_STATUS_INVALID_SID;
1099 centry = centry_start(domain, NT_STATUS_OK);
1100 if (!centry) {
1101 return NT_STATUS_INTERNAL_DB_ERROR;
1104 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1106 centry_put_time(centry, time(NULL));
1108 /* Create a salt and then salt the hash. */
1109 generate_random_buffer(cred_salt, NT_HASH_LEN);
1110 E_md5hash(cred_salt, nt_pass, salted_hash);
1112 centry_put_hash16(centry, salted_hash);
1113 centry_put_hash16(centry, cred_salt);
1114 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1116 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1118 centry_free(centry);
1120 return NT_STATUS_OK;
1124 /* Query display info. This is the basic user list fn */
1125 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1126 TALLOC_CTX *mem_ctx,
1127 uint32 *num_entries,
1128 WINBIND_USERINFO **info)
1130 struct winbind_cache *cache = get_cache(domain);
1131 struct cache_entry *centry = NULL;
1132 NTSTATUS status;
1133 unsigned int i, retry;
1135 if (!cache->tdb)
1136 goto do_query;
1138 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1139 if (!centry)
1140 goto do_query;
1142 *num_entries = centry_uint32(centry);
1144 if (*num_entries == 0)
1145 goto do_cached;
1147 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1148 if (! (*info)) {
1149 smb_panic_fn("query_user_list out of memory");
1151 for (i=0; i<(*num_entries); i++) {
1152 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1153 (*info)[i].full_name = centry_string(centry, mem_ctx);
1154 (*info)[i].homedir = centry_string(centry, mem_ctx);
1155 (*info)[i].shell = centry_string(centry, mem_ctx);
1156 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1157 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1160 do_cached:
1161 status = centry->status;
1163 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1164 domain->name, nt_errstr(status) ));
1166 centry_free(centry);
1167 return status;
1169 do_query:
1170 *num_entries = 0;
1171 *info = NULL;
1173 /* Return status value returned by seq number check */
1175 if (!NT_STATUS_IS_OK(domain->last_status))
1176 return domain->last_status;
1178 /* Put the query_user_list() in a retry loop. There appears to be
1179 * some bug either with Windows 2000 or Samba's handling of large
1180 * rpc replies. This manifests itself as sudden disconnection
1181 * at a random point in the enumeration of a large (60k) user list.
1182 * The retry loop simply tries the operation again. )-: It's not
1183 * pretty but an acceptable workaround until we work out what the
1184 * real problem is. */
1186 retry = 0;
1187 do {
1189 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1190 domain->name ));
1192 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1193 if (!NT_STATUS_IS_OK(status)) {
1194 DEBUG(3, ("query_user_list: returned 0x%08x, "
1195 "retrying\n", NT_STATUS_V(status)));
1197 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1198 DEBUG(3, ("query_user_list: flushing "
1199 "connection cache\n"));
1200 invalidate_cm_connection(&domain->conn);
1203 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1204 (retry++ < 5));
1206 /* and save it */
1207 refresh_sequence_number(domain, false);
1208 centry = centry_start(domain, status);
1209 if (!centry)
1210 goto skip_save;
1211 centry_put_uint32(centry, *num_entries);
1212 for (i=0; i<(*num_entries); i++) {
1213 centry_put_string(centry, (*info)[i].acct_name);
1214 centry_put_string(centry, (*info)[i].full_name);
1215 centry_put_string(centry, (*info)[i].homedir);
1216 centry_put_string(centry, (*info)[i].shell);
1217 centry_put_sid(centry, &(*info)[i].user_sid);
1218 centry_put_sid(centry, &(*info)[i].group_sid);
1219 if (domain->backend && domain->backend->consistent) {
1220 /* when the backend is consistent we can pre-prime some mappings */
1221 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1222 domain->name,
1223 (*info)[i].acct_name,
1224 &(*info)[i].user_sid,
1225 SID_NAME_USER);
1226 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1227 &(*info)[i].user_sid,
1228 domain->name,
1229 (*info)[i].acct_name,
1230 SID_NAME_USER);
1231 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1234 centry_end(centry, "UL/%s", domain->name);
1235 centry_free(centry);
1237 skip_save:
1238 return status;
1241 /* list all domain groups */
1242 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1243 TALLOC_CTX *mem_ctx,
1244 uint32 *num_entries,
1245 struct acct_info **info)
1247 struct winbind_cache *cache = get_cache(domain);
1248 struct cache_entry *centry = NULL;
1249 NTSTATUS status;
1250 unsigned int i;
1252 if (!cache->tdb)
1253 goto do_query;
1255 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1256 if (!centry)
1257 goto do_query;
1259 *num_entries = centry_uint32(centry);
1261 if (*num_entries == 0)
1262 goto do_cached;
1264 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1265 if (! (*info)) {
1266 smb_panic_fn("enum_dom_groups out of memory");
1268 for (i=0; i<(*num_entries); i++) {
1269 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1270 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1271 (*info)[i].rid = centry_uint32(centry);
1274 do_cached:
1275 status = centry->status;
1277 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1278 domain->name, nt_errstr(status) ));
1280 centry_free(centry);
1281 return status;
1283 do_query:
1284 *num_entries = 0;
1285 *info = NULL;
1287 /* Return status value returned by seq number check */
1289 if (!NT_STATUS_IS_OK(domain->last_status))
1290 return domain->last_status;
1292 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1293 domain->name ));
1295 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1297 /* and save it */
1298 refresh_sequence_number(domain, false);
1299 centry = centry_start(domain, status);
1300 if (!centry)
1301 goto skip_save;
1302 centry_put_uint32(centry, *num_entries);
1303 for (i=0; i<(*num_entries); i++) {
1304 centry_put_string(centry, (*info)[i].acct_name);
1305 centry_put_string(centry, (*info)[i].acct_desc);
1306 centry_put_uint32(centry, (*info)[i].rid);
1308 centry_end(centry, "GL/%s/domain", domain->name);
1309 centry_free(centry);
1311 skip_save:
1312 return status;
1315 /* list all domain groups */
1316 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1317 TALLOC_CTX *mem_ctx,
1318 uint32 *num_entries,
1319 struct acct_info **info)
1321 struct winbind_cache *cache = get_cache(domain);
1322 struct cache_entry *centry = NULL;
1323 NTSTATUS status;
1324 unsigned int i;
1326 if (!cache->tdb)
1327 goto do_query;
1329 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1330 if (!centry)
1331 goto do_query;
1333 *num_entries = centry_uint32(centry);
1335 if (*num_entries == 0)
1336 goto do_cached;
1338 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1339 if (! (*info)) {
1340 smb_panic_fn("enum_dom_groups out of memory");
1342 for (i=0; i<(*num_entries); i++) {
1343 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1344 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1345 (*info)[i].rid = centry_uint32(centry);
1348 do_cached:
1350 /* If we are returning cached data and the domain controller
1351 is down then we don't know whether the data is up to date
1352 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1353 indicate this. */
1355 if (wcache_server_down(domain)) {
1356 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1357 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1358 } else
1359 status = centry->status;
1361 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1362 domain->name, nt_errstr(status) ));
1364 centry_free(centry);
1365 return status;
1367 do_query:
1368 *num_entries = 0;
1369 *info = NULL;
1371 /* Return status value returned by seq number check */
1373 if (!NT_STATUS_IS_OK(domain->last_status))
1374 return domain->last_status;
1376 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1377 domain->name ));
1379 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1381 /* and save it */
1382 refresh_sequence_number(domain, false);
1383 centry = centry_start(domain, status);
1384 if (!centry)
1385 goto skip_save;
1386 centry_put_uint32(centry, *num_entries);
1387 for (i=0; i<(*num_entries); i++) {
1388 centry_put_string(centry, (*info)[i].acct_name);
1389 centry_put_string(centry, (*info)[i].acct_desc);
1390 centry_put_uint32(centry, (*info)[i].rid);
1392 centry_end(centry, "GL/%s/local", domain->name);
1393 centry_free(centry);
1395 skip_save:
1396 return status;
1399 /* convert a single name to a sid in a domain */
1400 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1401 TALLOC_CTX *mem_ctx,
1402 enum winbindd_cmd orig_cmd,
1403 const char *domain_name,
1404 const char *name,
1405 DOM_SID *sid,
1406 enum lsa_SidType *type)
1408 struct winbind_cache *cache = get_cache(domain);
1409 struct cache_entry *centry = NULL;
1410 NTSTATUS status;
1411 fstring uname;
1413 if (!cache->tdb)
1414 goto do_query;
1416 fstrcpy(uname, name);
1417 strupper_m(uname);
1418 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1419 if (!centry)
1420 goto do_query;
1422 status = centry->status;
1423 if (NT_STATUS_IS_OK(status)) {
1424 *type = (enum lsa_SidType)centry_uint32(centry);
1425 centry_sid(centry, mem_ctx, sid);
1428 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1429 domain->name, nt_errstr(status) ));
1431 centry_free(centry);
1432 return status;
1434 do_query:
1435 ZERO_STRUCTP(sid);
1437 /* If the seq number check indicated that there is a problem
1438 * with this DC, then return that status... except for
1439 * access_denied. This is special because the dc may be in
1440 * "restrict anonymous = 1" mode, in which case it will deny
1441 * most unauthenticated operations, but *will* allow the LSA
1442 * name-to-sid that we try as a fallback. */
1444 if (!(NT_STATUS_IS_OK(domain->last_status)
1445 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1446 return domain->last_status;
1448 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1449 domain->name ));
1451 status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd,
1452 domain_name, name, sid, type);
1454 /* and save it */
1455 refresh_sequence_number(domain, false);
1457 if (domain->online &&
1458 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1459 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1461 /* Only save the reverse mapping if this was not a UPN */
1462 if (!strchr(name, '@')) {
1463 strupper_m(CONST_DISCARD(char *,domain_name));
1464 strlower_m(CONST_DISCARD(char *,name));
1465 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1469 return status;
1472 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1473 given */
1474 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1475 TALLOC_CTX *mem_ctx,
1476 const DOM_SID *sid,
1477 char **domain_name,
1478 char **name,
1479 enum lsa_SidType *type)
1481 struct winbind_cache *cache = get_cache(domain);
1482 struct cache_entry *centry = NULL;
1483 NTSTATUS status;
1484 fstring sid_string;
1486 if (!cache->tdb)
1487 goto do_query;
1489 centry = wcache_fetch(cache, domain, "SN/%s",
1490 sid_to_fstring(sid_string, sid));
1491 if (!centry)
1492 goto do_query;
1494 status = centry->status;
1495 if (NT_STATUS_IS_OK(status)) {
1496 *type = (enum lsa_SidType)centry_uint32(centry);
1497 *domain_name = centry_string(centry, mem_ctx);
1498 *name = centry_string(centry, mem_ctx);
1501 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1502 domain->name, nt_errstr(status) ));
1504 centry_free(centry);
1505 return status;
1507 do_query:
1508 *name = NULL;
1509 *domain_name = NULL;
1511 /* If the seq number check indicated that there is a problem
1512 * with this DC, then return that status... except for
1513 * access_denied. This is special because the dc may be in
1514 * "restrict anonymous = 1" mode, in which case it will deny
1515 * most unauthenticated operations, but *will* allow the LSA
1516 * sid-to-name that we try as a fallback. */
1518 if (!(NT_STATUS_IS_OK(domain->last_status)
1519 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1520 return domain->last_status;
1522 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1523 domain->name ));
1525 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1527 /* and save it */
1528 refresh_sequence_number(domain, false);
1529 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1531 /* We can't save the name to sid mapping here, as with sid history a
1532 * later name2sid would give the wrong sid. */
1534 return status;
1537 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1538 TALLOC_CTX *mem_ctx,
1539 const DOM_SID *domain_sid,
1540 uint32 *rids,
1541 size_t num_rids,
1542 char **domain_name,
1543 char ***names,
1544 enum lsa_SidType **types)
1546 struct winbind_cache *cache = get_cache(domain);
1547 size_t i;
1548 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1549 bool have_mapped;
1550 bool have_unmapped;
1552 *domain_name = NULL;
1553 *names = NULL;
1554 *types = NULL;
1556 if (!cache->tdb) {
1557 goto do_query;
1560 if (num_rids == 0) {
1561 return NT_STATUS_OK;
1564 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1565 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1567 if ((*names == NULL) || (*types == NULL)) {
1568 result = NT_STATUS_NO_MEMORY;
1569 goto error;
1572 have_mapped = have_unmapped = false;
1574 for (i=0; i<num_rids; i++) {
1575 DOM_SID sid;
1576 struct cache_entry *centry;
1577 fstring tmp;
1579 if (!sid_compose(&sid, domain_sid, rids[i])) {
1580 result = NT_STATUS_INTERNAL_ERROR;
1581 goto error;
1584 centry = wcache_fetch(cache, domain, "SN/%s",
1585 sid_to_fstring(tmp, &sid));
1586 if (!centry) {
1587 goto do_query;
1590 (*types)[i] = SID_NAME_UNKNOWN;
1591 (*names)[i] = talloc_strdup(*names, "");
1593 if (NT_STATUS_IS_OK(centry->status)) {
1594 char *dom;
1595 have_mapped = true;
1596 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1598 dom = centry_string(centry, mem_ctx);
1599 if (*domain_name == NULL) {
1600 *domain_name = dom;
1601 } else {
1602 talloc_free(dom);
1605 (*names)[i] = centry_string(centry, *names);
1607 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1608 have_unmapped = true;
1610 } else {
1611 /* something's definitely wrong */
1612 result = centry->status;
1613 goto error;
1616 centry_free(centry);
1619 if (!have_mapped) {
1620 return NT_STATUS_NONE_MAPPED;
1622 if (!have_unmapped) {
1623 return NT_STATUS_OK;
1625 return STATUS_SOME_UNMAPPED;
1627 do_query:
1629 TALLOC_FREE(*names);
1630 TALLOC_FREE(*types);
1632 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1633 rids, num_rids, domain_name,
1634 names, types);
1637 None of the queried rids has been found so save all negative entries
1639 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1640 for (i = 0; i < num_rids; i++) {
1641 DOM_SID sid;
1642 const char *name = "";
1643 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1644 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1646 if (!sid_compose(&sid, domain_sid, rids[i])) {
1647 return NT_STATUS_INTERNAL_ERROR;
1650 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1651 name, type);
1654 return result;
1658 Some or all of the queried rids have been found.
1660 if (!NT_STATUS_IS_OK(result) &&
1661 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1662 return result;
1665 refresh_sequence_number(domain, false);
1667 for (i=0; i<num_rids; i++) {
1668 DOM_SID sid;
1669 NTSTATUS status;
1671 if (!sid_compose(&sid, domain_sid, rids[i])) {
1672 result = NT_STATUS_INTERNAL_ERROR;
1673 goto error;
1676 status = (*types)[i] == SID_NAME_UNKNOWN ?
1677 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1679 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1680 (*names)[i], (*types)[i]);
1683 return result;
1685 error:
1687 TALLOC_FREE(*names);
1688 TALLOC_FREE(*types);
1689 return result;
1692 /* Lookup user information from a rid */
1693 static NTSTATUS query_user(struct winbindd_domain *domain,
1694 TALLOC_CTX *mem_ctx,
1695 const DOM_SID *user_sid,
1696 WINBIND_USERINFO *info)
1698 struct winbind_cache *cache = get_cache(domain);
1699 struct cache_entry *centry = NULL;
1700 NTSTATUS status;
1701 fstring tmp;
1703 if (!cache->tdb)
1704 goto do_query;
1706 centry = wcache_fetch(cache, domain, "U/%s",
1707 sid_to_fstring(tmp, user_sid));
1709 /* If we have an access denied cache entry and a cached info3 in the
1710 samlogon cache then do a query. This will force the rpc back end
1711 to return the info3 data. */
1713 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1714 netsamlogon_cache_have(user_sid)) {
1715 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1716 domain->last_status = NT_STATUS_OK;
1717 centry_free(centry);
1718 goto do_query;
1721 if (!centry)
1722 goto do_query;
1724 /* if status is not ok then this is a negative hit
1725 and the rest of the data doesn't matter */
1726 status = centry->status;
1727 if (NT_STATUS_IS_OK(status)) {
1728 info->acct_name = centry_string(centry, mem_ctx);
1729 info->full_name = centry_string(centry, mem_ctx);
1730 info->homedir = centry_string(centry, mem_ctx);
1731 info->shell = centry_string(centry, mem_ctx);
1732 info->primary_gid = centry_uint32(centry);
1733 centry_sid(centry, mem_ctx, &info->user_sid);
1734 centry_sid(centry, mem_ctx, &info->group_sid);
1737 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1738 domain->name, nt_errstr(status) ));
1740 centry_free(centry);
1741 return status;
1743 do_query:
1744 ZERO_STRUCTP(info);
1746 /* Return status value returned by seq number check */
1748 if (!NT_STATUS_IS_OK(domain->last_status))
1749 return domain->last_status;
1751 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1752 domain->name ));
1754 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1756 /* and save it */
1757 refresh_sequence_number(domain, false);
1758 wcache_save_user(domain, status, info);
1760 return status;
1764 /* Lookup groups a user is a member of. */
1765 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1766 TALLOC_CTX *mem_ctx,
1767 const DOM_SID *user_sid,
1768 uint32 *num_groups, DOM_SID **user_gids)
1770 struct winbind_cache *cache = get_cache(domain);
1771 struct cache_entry *centry = NULL;
1772 NTSTATUS status;
1773 unsigned int i;
1774 fstring sid_string;
1776 if (!cache->tdb)
1777 goto do_query;
1779 centry = wcache_fetch(cache, domain, "UG/%s",
1780 sid_to_fstring(sid_string, user_sid));
1782 /* If we have an access denied cache entry and a cached info3 in the
1783 samlogon cache then do a query. This will force the rpc back end
1784 to return the info3 data. */
1786 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1787 netsamlogon_cache_have(user_sid)) {
1788 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1789 domain->last_status = NT_STATUS_OK;
1790 centry_free(centry);
1791 goto do_query;
1794 if (!centry)
1795 goto do_query;
1797 *num_groups = centry_uint32(centry);
1799 if (*num_groups == 0)
1800 goto do_cached;
1802 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1803 if (! (*user_gids)) {
1804 smb_panic_fn("lookup_usergroups out of memory");
1806 for (i=0; i<(*num_groups); i++) {
1807 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1810 do_cached:
1811 status = centry->status;
1813 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1814 domain->name, nt_errstr(status) ));
1816 centry_free(centry);
1817 return status;
1819 do_query:
1820 (*num_groups) = 0;
1821 (*user_gids) = NULL;
1823 /* Return status value returned by seq number check */
1825 if (!NT_STATUS_IS_OK(domain->last_status))
1826 return domain->last_status;
1828 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1829 domain->name ));
1831 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1833 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
1834 goto skip_save;
1836 /* and save it */
1837 refresh_sequence_number(domain, false);
1838 centry = centry_start(domain, status);
1839 if (!centry)
1840 goto skip_save;
1842 centry_put_uint32(centry, *num_groups);
1843 for (i=0; i<(*num_groups); i++) {
1844 centry_put_sid(centry, &(*user_gids)[i]);
1847 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
1848 centry_free(centry);
1850 skip_save:
1851 return status;
1854 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1855 TALLOC_CTX *mem_ctx,
1856 uint32 num_sids, const DOM_SID *sids,
1857 uint32 *num_aliases, uint32 **alias_rids)
1859 struct winbind_cache *cache = get_cache(domain);
1860 struct cache_entry *centry = NULL;
1861 NTSTATUS status;
1862 char *sidlist = talloc_strdup(mem_ctx, "");
1863 int i;
1865 if (!cache->tdb)
1866 goto do_query;
1868 if (num_sids == 0) {
1869 *num_aliases = 0;
1870 *alias_rids = NULL;
1871 return NT_STATUS_OK;
1874 /* We need to cache indexed by the whole list of SIDs, the aliases
1875 * resulting might come from any of the SIDs. */
1877 for (i=0; i<num_sids; i++) {
1878 fstring tmp;
1879 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1880 sid_to_fstring(tmp, &sids[i]));
1881 if (sidlist == NULL)
1882 return NT_STATUS_NO_MEMORY;
1885 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1887 if (!centry)
1888 goto do_query;
1890 *num_aliases = centry_uint32(centry);
1891 *alias_rids = NULL;
1893 if (*num_aliases) {
1894 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1896 if ((*alias_rids) == NULL) {
1897 centry_free(centry);
1898 return NT_STATUS_NO_MEMORY;
1900 } else {
1901 (*alias_rids) = NULL;
1904 for (i=0; i<(*num_aliases); i++)
1905 (*alias_rids)[i] = centry_uint32(centry);
1907 status = centry->status;
1909 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1910 "status %s\n", domain->name, nt_errstr(status)));
1912 centry_free(centry);
1913 return status;
1915 do_query:
1916 (*num_aliases) = 0;
1917 (*alias_rids) = NULL;
1919 if (!NT_STATUS_IS_OK(domain->last_status))
1920 return domain->last_status;
1922 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1923 "for domain %s\n", domain->name ));
1925 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1926 num_sids, sids,
1927 num_aliases, alias_rids);
1929 /* and save it */
1930 refresh_sequence_number(domain, false);
1931 centry = centry_start(domain, status);
1932 if (!centry)
1933 goto skip_save;
1934 centry_put_uint32(centry, *num_aliases);
1935 for (i=0; i<(*num_aliases); i++)
1936 centry_put_uint32(centry, (*alias_rids)[i]);
1937 centry_end(centry, "UA%s", sidlist);
1938 centry_free(centry);
1940 skip_save:
1941 return status;
1945 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1946 TALLOC_CTX *mem_ctx,
1947 const DOM_SID *group_sid, uint32 *num_names,
1948 DOM_SID **sid_mem, char ***names,
1949 uint32 **name_types)
1951 struct winbind_cache *cache = get_cache(domain);
1952 struct cache_entry *centry = NULL;
1953 NTSTATUS status;
1954 unsigned int i;
1955 fstring sid_string;
1957 if (!cache->tdb)
1958 goto do_query;
1960 centry = wcache_fetch(cache, domain, "GM/%s",
1961 sid_to_fstring(sid_string, group_sid));
1962 if (!centry)
1963 goto do_query;
1965 *num_names = centry_uint32(centry);
1967 if (*num_names == 0)
1968 goto do_cached;
1970 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1971 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1972 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1974 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1975 smb_panic_fn("lookup_groupmem out of memory");
1978 for (i=0; i<(*num_names); i++) {
1979 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1980 (*names)[i] = centry_string(centry, mem_ctx);
1981 (*name_types)[i] = centry_uint32(centry);
1984 do_cached:
1985 status = centry->status;
1987 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1988 domain->name, nt_errstr(status)));
1990 centry_free(centry);
1991 return status;
1993 do_query:
1994 (*num_names) = 0;
1995 (*sid_mem) = NULL;
1996 (*names) = NULL;
1997 (*name_types) = NULL;
1999 /* Return status value returned by seq number check */
2001 if (!NT_STATUS_IS_OK(domain->last_status))
2002 return domain->last_status;
2004 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2005 domain->name ));
2007 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2008 sid_mem, names, name_types);
2010 /* and save it */
2011 refresh_sequence_number(domain, false);
2012 centry = centry_start(domain, status);
2013 if (!centry)
2014 goto skip_save;
2015 centry_put_uint32(centry, *num_names);
2016 for (i=0; i<(*num_names); i++) {
2017 centry_put_sid(centry, &(*sid_mem)[i]);
2018 centry_put_string(centry, (*names)[i]);
2019 centry_put_uint32(centry, (*name_types)[i]);
2021 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2022 centry_free(centry);
2024 skip_save:
2025 return status;
2028 /* find the sequence number for a domain */
2029 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2031 refresh_sequence_number(domain, false);
2033 *seq = domain->sequence_number;
2035 return NT_STATUS_OK;
2038 /* enumerate trusted domains
2039 * (we need to have the list of trustdoms in the cache when we go offline) -
2040 * Guenther */
2041 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2042 TALLOC_CTX *mem_ctx,
2043 uint32 *num_domains,
2044 char ***names,
2045 char ***alt_names,
2046 DOM_SID **dom_sids)
2048 struct winbind_cache *cache = get_cache(domain);
2049 struct cache_entry *centry = NULL;
2050 NTSTATUS status;
2051 int i;
2053 if (!cache->tdb)
2054 goto do_query;
2056 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2058 if (!centry) {
2059 goto do_query;
2062 *num_domains = centry_uint32(centry);
2064 if (*num_domains) {
2065 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2066 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2067 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2069 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2070 smb_panic_fn("trusted_domains out of memory");
2072 } else {
2073 (*names) = NULL;
2074 (*alt_names) = NULL;
2075 (*dom_sids) = NULL;
2078 for (i=0; i<(*num_domains); i++) {
2079 (*names)[i] = centry_string(centry, mem_ctx);
2080 (*alt_names)[i] = centry_string(centry, mem_ctx);
2081 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
2084 status = centry->status;
2086 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2087 domain->name, *num_domains, nt_errstr(status) ));
2089 centry_free(centry);
2090 return status;
2092 do_query:
2093 (*num_domains) = 0;
2094 (*dom_sids) = NULL;
2095 (*names) = NULL;
2096 (*alt_names) = NULL;
2098 /* Return status value returned by seq number check */
2100 if (!NT_STATUS_IS_OK(domain->last_status))
2101 return domain->last_status;
2103 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2104 domain->name ));
2106 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2107 names, alt_names, dom_sids);
2109 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2110 * so that the generic centry handling still applies correctly -
2111 * Guenther*/
2113 if (!NT_STATUS_IS_ERR(status)) {
2114 status = NT_STATUS_OK;
2118 #if 0 /* Disabled as we want the trust dom list to be managed by
2119 the main parent and always to make the query. --jerry */
2121 /* and save it */
2122 refresh_sequence_number(domain, false);
2124 centry = centry_start(domain, status);
2125 if (!centry)
2126 goto skip_save;
2128 centry_put_uint32(centry, *num_domains);
2130 for (i=0; i<(*num_domains); i++) {
2131 centry_put_string(centry, (*names)[i]);
2132 centry_put_string(centry, (*alt_names)[i]);
2133 centry_put_sid(centry, &(*dom_sids)[i]);
2136 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2138 centry_free(centry);
2140 skip_save:
2141 #endif
2143 return status;
2146 /* get lockout policy */
2147 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2148 TALLOC_CTX *mem_ctx,
2149 struct samr_DomInfo12 *policy)
2151 struct winbind_cache *cache = get_cache(domain);
2152 struct cache_entry *centry = NULL;
2153 NTSTATUS status;
2155 if (!cache->tdb)
2156 goto do_query;
2158 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2160 if (!centry)
2161 goto do_query;
2163 policy->lockout_duration = centry_nttime(centry);
2164 policy->lockout_window = centry_nttime(centry);
2165 policy->lockout_threshold = centry_uint16(centry);
2167 status = centry->status;
2169 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2170 domain->name, nt_errstr(status) ));
2172 centry_free(centry);
2173 return status;
2175 do_query:
2176 ZERO_STRUCTP(policy);
2178 /* Return status value returned by seq number check */
2180 if (!NT_STATUS_IS_OK(domain->last_status))
2181 return domain->last_status;
2183 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2184 domain->name ));
2186 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2188 /* and save it */
2189 refresh_sequence_number(domain, false);
2190 wcache_save_lockout_policy(domain, status, policy);
2192 return status;
2195 /* get password policy */
2196 static NTSTATUS password_policy(struct winbindd_domain *domain,
2197 TALLOC_CTX *mem_ctx,
2198 struct samr_DomInfo1 *policy)
2200 struct winbind_cache *cache = get_cache(domain);
2201 struct cache_entry *centry = NULL;
2202 NTSTATUS status;
2204 if (!cache->tdb)
2205 goto do_query;
2207 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2209 if (!centry)
2210 goto do_query;
2212 policy->min_password_length = centry_uint16(centry);
2213 policy->password_history_length = centry_uint16(centry);
2214 policy->password_properties = centry_uint32(centry);
2215 policy->max_password_age = centry_nttime(centry);
2216 policy->min_password_age = centry_nttime(centry);
2218 status = centry->status;
2220 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2221 domain->name, nt_errstr(status) ));
2223 centry_free(centry);
2224 return status;
2226 do_query:
2227 ZERO_STRUCTP(policy);
2229 /* Return status value returned by seq number check */
2231 if (!NT_STATUS_IS_OK(domain->last_status))
2232 return domain->last_status;
2234 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2235 domain->name ));
2237 status = domain->backend->password_policy(domain, mem_ctx, policy);
2239 /* and save it */
2240 refresh_sequence_number(domain, false);
2241 if (NT_STATUS_IS_OK(status)) {
2242 wcache_save_password_policy(domain, status, policy);
2245 return status;
2249 /* Invalidate cached user and group lists coherently */
2251 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2252 void *state)
2254 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2255 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2256 tdb_delete(the_tdb, kbuf);
2258 return 0;
2261 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2263 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2264 struct netr_SamInfo3 *info3)
2266 struct winbind_cache *cache;
2268 /* dont clear cached U/SID and UG/SID entries when we want to logon
2269 * offline - gd */
2271 if (lp_winbind_offline_logon()) {
2272 return;
2275 if (!domain)
2276 return;
2278 cache = get_cache(domain);
2279 netsamlogon_clear_cached_user(cache->tdb, info3);
2282 bool wcache_invalidate_cache(void)
2284 struct winbindd_domain *domain;
2286 for (domain = domain_list(); domain; domain = domain->next) {
2287 struct winbind_cache *cache = get_cache(domain);
2289 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2290 "entries for %s\n", domain->name));
2291 if (cache) {
2292 if (cache->tdb) {
2293 tdb_traverse(cache->tdb, traverse_fn, NULL);
2294 } else {
2295 return false;
2299 return true;
2302 bool init_wcache(void)
2304 if (wcache == NULL) {
2305 wcache = SMB_XMALLOC_P(struct winbind_cache);
2306 ZERO_STRUCTP(wcache);
2309 if (wcache->tdb != NULL)
2310 return true;
2312 /* when working offline we must not clear the cache on restart */
2313 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2314 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2315 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2316 O_RDWR|O_CREAT, 0600);
2318 if (wcache->tdb == NULL) {
2319 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2320 return false;
2323 return true;
2326 /************************************************************************
2327 This is called by the parent to initialize the cache file.
2328 We don't need sophisticated locking here as we know we're the
2329 only opener.
2330 ************************************************************************/
2332 bool initialize_winbindd_cache(void)
2334 bool cache_bad = true;
2335 uint32 vers;
2337 if (!init_wcache()) {
2338 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2339 return false;
2342 /* Check version number. */
2343 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2344 vers == WINBINDD_CACHE_VERSION) {
2345 cache_bad = false;
2348 if (cache_bad) {
2349 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2350 "and re-creating with version number %d\n",
2351 WINBINDD_CACHE_VERSION ));
2353 tdb_close(wcache->tdb);
2354 wcache->tdb = NULL;
2356 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2357 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2358 lock_path("winbindd_cache.tdb"),
2359 strerror(errno) ));
2360 return false;
2362 if (!init_wcache()) {
2363 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2364 "init_wcache failed.\n"));
2365 return false;
2368 /* Write the version. */
2369 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2370 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2371 tdb_errorstr(wcache->tdb) ));
2372 return false;
2376 tdb_close(wcache->tdb);
2377 wcache->tdb = NULL;
2378 return true;
2381 void close_winbindd_cache(void)
2383 if (!wcache) {
2384 return;
2386 if (wcache->tdb) {
2387 tdb_close(wcache->tdb);
2388 wcache->tdb = NULL;
2392 void cache_store_response(pid_t pid, struct winbindd_response *response)
2394 fstring key_str;
2396 if (!init_wcache())
2397 return;
2399 DEBUG(10, ("Storing response for pid %d, len %d\n",
2400 pid, response->length));
2402 fstr_sprintf(key_str, "DR/%d", pid);
2403 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2404 make_tdb_data((uint8 *)response, sizeof(*response)),
2405 TDB_REPLACE) == -1)
2406 return;
2408 if (response->length == sizeof(*response))
2409 return;
2411 /* There's extra data */
2413 DEBUG(10, ("Storing extra data: len=%d\n",
2414 (int)(response->length - sizeof(*response))));
2416 fstr_sprintf(key_str, "DE/%d", pid);
2417 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2418 make_tdb_data((uint8 *)response->extra_data.data,
2419 response->length - sizeof(*response)),
2420 TDB_REPLACE) == 0)
2421 return;
2423 /* We could not store the extra data, make sure the tdb does not
2424 * contain a main record with wrong dangling extra data */
2426 fstr_sprintf(key_str, "DR/%d", pid);
2427 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2429 return;
2432 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2434 TDB_DATA data;
2435 fstring key_str;
2437 if (!init_wcache())
2438 return false;
2440 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2442 fstr_sprintf(key_str, "DR/%d", pid);
2443 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2445 if (data.dptr == NULL)
2446 return false;
2448 if (data.dsize != sizeof(*response))
2449 return false;
2451 memcpy(response, data.dptr, data.dsize);
2452 SAFE_FREE(data.dptr);
2454 if (response->length == sizeof(*response)) {
2455 response->extra_data.data = NULL;
2456 return true;
2459 /* There's extra data */
2461 DEBUG(10, ("Retrieving extra data length=%d\n",
2462 (int)(response->length - sizeof(*response))));
2464 fstr_sprintf(key_str, "DE/%d", pid);
2465 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2467 if (data.dptr == NULL) {
2468 DEBUG(0, ("Did not find extra data\n"));
2469 return false;
2472 if (data.dsize != (response->length - sizeof(*response))) {
2473 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2474 SAFE_FREE(data.dptr);
2475 return false;
2478 dump_data(11, (uint8 *)data.dptr, data.dsize);
2480 response->extra_data.data = data.dptr;
2481 return true;
2484 void cache_cleanup_response(pid_t pid)
2486 fstring key_str;
2488 if (!init_wcache())
2489 return;
2491 fstr_sprintf(key_str, "DR/%d", pid);
2492 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2494 fstr_sprintf(key_str, "DE/%d", pid);
2495 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2497 return;
2501 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2502 char **domain_name, char **name,
2503 enum lsa_SidType *type)
2505 struct winbindd_domain *domain;
2506 struct winbind_cache *cache;
2507 struct cache_entry *centry = NULL;
2508 NTSTATUS status;
2509 fstring tmp;
2511 domain = find_lookup_domain_from_sid(sid);
2512 if (domain == NULL) {
2513 return false;
2516 cache = get_cache(domain);
2518 if (cache->tdb == NULL) {
2519 return false;
2522 centry = wcache_fetch(cache, domain, "SN/%s",
2523 sid_to_fstring(tmp, sid));
2524 if (centry == NULL) {
2525 return false;
2528 if (NT_STATUS_IS_OK(centry->status)) {
2529 *type = (enum lsa_SidType)centry_uint32(centry);
2530 *domain_name = centry_string(centry, mem_ctx);
2531 *name = centry_string(centry, mem_ctx);
2534 status = centry->status;
2535 centry_free(centry);
2536 return NT_STATUS_IS_OK(status);
2539 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2540 const char *domain_name,
2541 const char *name,
2542 DOM_SID *sid,
2543 enum lsa_SidType *type)
2545 struct winbindd_domain *domain;
2546 struct winbind_cache *cache;
2547 struct cache_entry *centry = NULL;
2548 NTSTATUS status;
2549 fstring uname;
2550 bool original_online_state;
2552 domain = find_lookup_domain_from_name(domain_name);
2553 if (domain == NULL) {
2554 return false;
2557 cache = get_cache(domain);
2559 if (cache->tdb == NULL) {
2560 return false;
2563 fstrcpy(uname, name);
2564 strupper_m(uname);
2566 /* If we are doing a cached logon, temporarily set the domain
2567 offline so the cache won't expire the entry */
2569 original_online_state = domain->online;
2570 domain->online = false;
2571 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2572 domain->online = original_online_state;
2574 if (centry == NULL) {
2575 return false;
2578 if (NT_STATUS_IS_OK(centry->status)) {
2579 *type = (enum lsa_SidType)centry_uint32(centry);
2580 centry_sid(centry, mem_ctx, sid);
2583 status = centry->status;
2584 centry_free(centry);
2586 return NT_STATUS_IS_OK(status);
2589 void cache_name2sid(struct winbindd_domain *domain,
2590 const char *domain_name, const char *name,
2591 enum lsa_SidType type, const DOM_SID *sid)
2593 refresh_sequence_number(domain, false);
2594 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2595 sid, type);
2599 * The original idea that this cache only contains centries has
2600 * been blurred - now other stuff gets put in here. Ensure we
2601 * ignore these things on cleanup.
2604 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2605 TDB_DATA dbuf, void *state)
2607 struct cache_entry *centry;
2609 if (is_non_centry_key(kbuf)) {
2610 return 0;
2613 centry = wcache_fetch_raw((char *)kbuf.dptr);
2614 if (!centry) {
2615 return 0;
2618 if (!NT_STATUS_IS_OK(centry->status)) {
2619 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2620 tdb_delete(the_tdb, kbuf);
2623 centry_free(centry);
2624 return 0;
2627 /* flush the cache */
2628 void wcache_flush_cache(void)
2630 if (!wcache)
2631 return;
2632 if (wcache->tdb) {
2633 tdb_close(wcache->tdb);
2634 wcache->tdb = NULL;
2636 if (opt_nocache)
2637 return;
2639 /* when working offline we must not clear the cache on restart */
2640 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2641 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2642 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2643 O_RDWR|O_CREAT, 0600);
2645 if (!wcache->tdb) {
2646 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2647 return;
2650 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2652 DEBUG(10,("wcache_flush_cache success\n"));
2655 /* Count cached creds */
2657 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2658 void *state)
2660 int *cred_count = (int*)state;
2662 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2663 (*cred_count)++;
2665 return 0;
2668 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2670 struct winbind_cache *cache = get_cache(domain);
2672 *count = 0;
2674 if (!cache->tdb) {
2675 return NT_STATUS_INTERNAL_DB_ERROR;
2678 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2680 return NT_STATUS_OK;
2683 struct cred_list {
2684 struct cred_list *prev, *next;
2685 TDB_DATA key;
2686 fstring name;
2687 time_t created;
2689 static struct cred_list *wcache_cred_list;
2691 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2692 void *state)
2694 struct cred_list *cred;
2696 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2698 cred = SMB_MALLOC_P(struct cred_list);
2699 if (cred == NULL) {
2700 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2701 return -1;
2704 ZERO_STRUCTP(cred);
2706 /* save a copy of the key */
2708 fstrcpy(cred->name, (const char *)kbuf.dptr);
2709 DLIST_ADD(wcache_cred_list, cred);
2712 return 0;
2715 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2717 struct winbind_cache *cache = get_cache(domain);
2718 NTSTATUS status;
2719 int ret;
2720 struct cred_list *cred, *oldest = NULL;
2722 if (!cache->tdb) {
2723 return NT_STATUS_INTERNAL_DB_ERROR;
2726 /* we possibly already have an entry */
2727 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2729 fstring key_str, tmp;
2731 DEBUG(11,("we already have an entry, deleting that\n"));
2733 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2735 tdb_delete(cache->tdb, string_tdb_data(key_str));
2737 return NT_STATUS_OK;
2740 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2741 if (ret == 0) {
2742 return NT_STATUS_OK;
2743 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2744 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2747 ZERO_STRUCTP(oldest);
2749 for (cred = wcache_cred_list; cred; cred = cred->next) {
2751 TDB_DATA data;
2752 time_t t;
2754 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2755 if (!data.dptr) {
2756 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2757 cred->name));
2758 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2759 goto done;
2762 t = IVAL(data.dptr, 0);
2763 SAFE_FREE(data.dptr);
2765 if (!oldest) {
2766 oldest = SMB_MALLOC_P(struct cred_list);
2767 if (oldest == NULL) {
2768 status = NT_STATUS_NO_MEMORY;
2769 goto done;
2772 fstrcpy(oldest->name, cred->name);
2773 oldest->created = t;
2774 continue;
2777 if (t < oldest->created) {
2778 fstrcpy(oldest->name, cred->name);
2779 oldest->created = t;
2783 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2784 status = NT_STATUS_OK;
2785 } else {
2786 status = NT_STATUS_UNSUCCESSFUL;
2788 done:
2789 SAFE_FREE(wcache_cred_list);
2790 SAFE_FREE(oldest);
2792 return status;
2795 /* Change the global online/offline state. */
2796 bool set_global_winbindd_state_offline(void)
2798 TDB_DATA data;
2800 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2802 /* Only go offline if someone has created
2803 the key "WINBINDD_OFFLINE" in the cache tdb. */
2805 if (wcache == NULL || wcache->tdb == NULL) {
2806 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2807 return false;
2810 if (!lp_winbind_offline_logon()) {
2811 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2812 return false;
2815 if (global_winbindd_offline_state) {
2816 /* Already offline. */
2817 return true;
2820 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2822 if (!data.dptr || data.dsize != 4) {
2823 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2824 SAFE_FREE(data.dptr);
2825 return false;
2826 } else {
2827 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2828 global_winbindd_offline_state = true;
2829 SAFE_FREE(data.dptr);
2830 return true;
2834 void set_global_winbindd_state_online(void)
2836 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2838 if (!lp_winbind_offline_logon()) {
2839 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2840 return;
2843 if (!global_winbindd_offline_state) {
2844 /* Already online. */
2845 return;
2847 global_winbindd_offline_state = false;
2849 if (!wcache->tdb) {
2850 return;
2853 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2854 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2857 bool get_global_winbindd_state_offline(void)
2859 return global_winbindd_offline_state;
2862 /***********************************************************************
2863 Validate functions for all possible cache tdb keys.
2864 ***********************************************************************/
2866 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
2867 struct tdb_validation_status *state)
2869 struct cache_entry *centry;
2871 centry = SMB_XMALLOC_P(struct cache_entry);
2872 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
2873 if (!centry->data) {
2874 SAFE_FREE(centry);
2875 return NULL;
2877 centry->len = data.dsize;
2878 centry->ofs = 0;
2880 if (centry->len < 8) {
2881 /* huh? corrupt cache? */
2882 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
2883 centry_free(centry);
2884 state->bad_entry = true;
2885 state->success = false;
2886 return NULL;
2889 centry->status = NT_STATUS(centry_uint32(centry));
2890 centry->sequence_number = centry_uint32(centry);
2891 return centry;
2894 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2895 struct tdb_validation_status *state)
2897 if (dbuf.dsize != 8) {
2898 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
2899 keystr, (unsigned int)dbuf.dsize ));
2900 state->bad_entry = true;
2901 return 1;
2903 return 0;
2906 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2907 struct tdb_validation_status *state)
2909 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2910 if (!centry) {
2911 return 1;
2914 (void)centry_uint32(centry);
2915 if (NT_STATUS_IS_OK(centry->status)) {
2916 DOM_SID sid;
2917 (void)centry_sid(centry, mem_ctx, &sid);
2920 centry_free(centry);
2922 if (!(state->success)) {
2923 return 1;
2925 DEBUG(10,("validate_ns: %s ok\n", keystr));
2926 return 0;
2929 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2930 struct tdb_validation_status *state)
2932 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2933 if (!centry) {
2934 return 1;
2937 if (NT_STATUS_IS_OK(centry->status)) {
2938 (void)centry_uint32(centry);
2939 (void)centry_string(centry, mem_ctx);
2940 (void)centry_string(centry, mem_ctx);
2943 centry_free(centry);
2945 if (!(state->success)) {
2946 return 1;
2948 DEBUG(10,("validate_sn: %s ok\n", keystr));
2949 return 0;
2952 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2953 struct tdb_validation_status *state)
2955 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2956 DOM_SID sid;
2958 if (!centry) {
2959 return 1;
2962 (void)centry_string(centry, mem_ctx);
2963 (void)centry_string(centry, mem_ctx);
2964 (void)centry_string(centry, mem_ctx);
2965 (void)centry_string(centry, mem_ctx);
2966 (void)centry_uint32(centry);
2967 (void)centry_sid(centry, mem_ctx, &sid);
2968 (void)centry_sid(centry, mem_ctx, &sid);
2970 centry_free(centry);
2972 if (!(state->success)) {
2973 return 1;
2975 DEBUG(10,("validate_u: %s ok\n", keystr));
2976 return 0;
2979 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
2980 struct tdb_validation_status *state)
2982 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
2984 if (!centry) {
2985 return 1;
2988 (void)centry_nttime(centry);
2989 (void)centry_nttime(centry);
2990 (void)centry_uint16(centry);
2992 centry_free(centry);
2994 if (!(state->success)) {
2995 return 1;
2997 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
2998 return 0;
3001 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3002 struct tdb_validation_status *state)
3004 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3006 if (!centry) {
3007 return 1;
3010 (void)centry_uint16(centry);
3011 (void)centry_uint16(centry);
3012 (void)centry_uint32(centry);
3013 (void)centry_nttime(centry);
3014 (void)centry_nttime(centry);
3016 centry_free(centry);
3018 if (!(state->success)) {
3019 return 1;
3021 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3022 return 0;
3025 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3026 struct tdb_validation_status *state)
3028 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3030 if (!centry) {
3031 return 1;
3034 (void)centry_time(centry);
3035 (void)centry_hash16(centry, mem_ctx);
3037 /* We only have 17 bytes more data in the salted cred case. */
3038 if (centry->len - centry->ofs == 17) {
3039 (void)centry_hash16(centry, mem_ctx);
3042 centry_free(centry);
3044 if (!(state->success)) {
3045 return 1;
3047 DEBUG(10,("validate_cred: %s ok\n", keystr));
3048 return 0;
3051 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3052 struct tdb_validation_status *state)
3054 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3055 int32 num_entries, i;
3057 if (!centry) {
3058 return 1;
3061 num_entries = (int32)centry_uint32(centry);
3063 for (i=0; i< num_entries; i++) {
3064 DOM_SID sid;
3065 (void)centry_string(centry, mem_ctx);
3066 (void)centry_string(centry, mem_ctx);
3067 (void)centry_string(centry, mem_ctx);
3068 (void)centry_string(centry, mem_ctx);
3069 (void)centry_sid(centry, mem_ctx, &sid);
3070 (void)centry_sid(centry, mem_ctx, &sid);
3073 centry_free(centry);
3075 if (!(state->success)) {
3076 return 1;
3078 DEBUG(10,("validate_ul: %s ok\n", keystr));
3079 return 0;
3082 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3083 struct tdb_validation_status *state)
3085 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3086 int32 num_entries, i;
3088 if (!centry) {
3089 return 1;
3092 num_entries = centry_uint32(centry);
3094 for (i=0; i< num_entries; i++) {
3095 (void)centry_string(centry, mem_ctx);
3096 (void)centry_string(centry, mem_ctx);
3097 (void)centry_uint32(centry);
3100 centry_free(centry);
3102 if (!(state->success)) {
3103 return 1;
3105 DEBUG(10,("validate_gl: %s ok\n", keystr));
3106 return 0;
3109 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3110 struct tdb_validation_status *state)
3112 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3113 int32 num_groups, i;
3115 if (!centry) {
3116 return 1;
3119 num_groups = centry_uint32(centry);
3121 for (i=0; i< num_groups; i++) {
3122 DOM_SID sid;
3123 centry_sid(centry, mem_ctx, &sid);
3126 centry_free(centry);
3128 if (!(state->success)) {
3129 return 1;
3131 DEBUG(10,("validate_ug: %s ok\n", keystr));
3132 return 0;
3135 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3136 struct tdb_validation_status *state)
3138 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3139 int32 num_aliases, i;
3141 if (!centry) {
3142 return 1;
3145 num_aliases = centry_uint32(centry);
3147 for (i=0; i < num_aliases; i++) {
3148 (void)centry_uint32(centry);
3151 centry_free(centry);
3153 if (!(state->success)) {
3154 return 1;
3156 DEBUG(10,("validate_ua: %s ok\n", keystr));
3157 return 0;
3160 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3161 struct tdb_validation_status *state)
3163 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3164 int32 num_names, i;
3166 if (!centry) {
3167 return 1;
3170 num_names = centry_uint32(centry);
3172 for (i=0; i< num_names; i++) {
3173 DOM_SID sid;
3174 centry_sid(centry, mem_ctx, &sid);
3175 (void)centry_string(centry, mem_ctx);
3176 (void)centry_uint32(centry);
3179 centry_free(centry);
3181 if (!(state->success)) {
3182 return 1;
3184 DEBUG(10,("validate_gm: %s ok\n", keystr));
3185 return 0;
3188 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3189 struct tdb_validation_status *state)
3191 /* Can't say anything about this other than must be nonzero. */
3192 if (dbuf.dsize == 0) {
3193 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3194 keystr));
3195 state->bad_entry = true;
3196 state->success = false;
3197 return 1;
3200 DEBUG(10,("validate_dr: %s ok\n", keystr));
3201 return 0;
3204 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3205 struct tdb_validation_status *state)
3207 /* Can't say anything about this other than must be nonzero. */
3208 if (dbuf.dsize == 0) {
3209 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3210 keystr));
3211 state->bad_entry = true;
3212 state->success = false;
3213 return 1;
3216 DEBUG(10,("validate_de: %s ok\n", keystr));
3217 return 0;
3220 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3221 TDB_DATA dbuf, struct tdb_validation_status *state)
3223 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3225 if (!centry) {
3226 return 1;
3229 (void)centry_string(centry, mem_ctx);
3230 (void)centry_string(centry, mem_ctx);
3231 (void)centry_string(centry, mem_ctx);
3232 (void)centry_uint32(centry);
3234 centry_free(centry);
3236 if (!(state->success)) {
3237 return 1;
3239 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3240 return 0;
3243 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3244 struct tdb_validation_status *state)
3246 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3247 int32 num_domains, i;
3249 if (!centry) {
3250 return 1;
3253 num_domains = centry_uint32(centry);
3255 for (i=0; i< num_domains; i++) {
3256 DOM_SID sid;
3257 (void)centry_string(centry, mem_ctx);
3258 (void)centry_string(centry, mem_ctx);
3259 (void)centry_sid(centry, mem_ctx, &sid);
3262 centry_free(centry);
3264 if (!(state->success)) {
3265 return 1;
3267 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3268 return 0;
3271 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3272 TDB_DATA dbuf,
3273 struct tdb_validation_status *state)
3275 if (dbuf.dsize == 0) {
3276 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3277 "key %s (len ==0) ?\n", keystr));
3278 state->bad_entry = true;
3279 state->success = false;
3280 return 1;
3283 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3284 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3285 return 0;
3288 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3289 struct tdb_validation_status *state)
3291 if (dbuf.dsize != 4) {
3292 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3293 keystr, (unsigned int)dbuf.dsize ));
3294 state->bad_entry = true;
3295 state->success = false;
3296 return 1;
3298 DEBUG(10,("validate_offline: %s ok\n", keystr));
3299 return 0;
3302 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3303 struct tdb_validation_status *state)
3305 if (dbuf.dsize != 4) {
3306 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3307 "key %s (len %u != 4) ?\n",
3308 keystr, (unsigned int)dbuf.dsize));
3309 state->bad_entry = true;
3310 state->success = false;
3311 return 1;
3314 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3315 return 0;
3318 /***********************************************************************
3319 A list of all possible cache tdb keys with associated validation
3320 functions.
3321 ***********************************************************************/
3323 struct key_val_struct {
3324 const char *keyname;
3325 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3326 } key_val[] = {
3327 {"SEQNUM/", validate_seqnum},
3328 {"NS/", validate_ns},
3329 {"SN/", validate_sn},
3330 {"U/", validate_u},
3331 {"LOC_POL/", validate_loc_pol},
3332 {"PWD_POL/", validate_pwd_pol},
3333 {"CRED/", validate_cred},
3334 {"UL/", validate_ul},
3335 {"GL/", validate_gl},
3336 {"UG/", validate_ug},
3337 {"UA", validate_ua},
3338 {"GM/", validate_gm},
3339 {"DR/", validate_dr},
3340 {"DE/", validate_de},
3341 {"NSS/PWINFO/", validate_pwinfo},
3342 {"TRUSTDOMS/", validate_trustdoms},
3343 {"TRUSTDOMCACHE/", validate_trustdomcache},
3344 {"WINBINDD_OFFLINE", validate_offline},
3345 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3346 {NULL, NULL}
3349 /***********************************************************************
3350 Function to look at every entry in the tdb and validate it as far as
3351 possible.
3352 ***********************************************************************/
3354 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3356 int i;
3357 unsigned int max_key_len = 1024;
3358 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3360 /* Paranoia check. */
3361 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3362 max_key_len = 1024 * 1024;
3364 if (kbuf.dsize > max_key_len) {
3365 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3366 "(%u) > (%u)\n\n",
3367 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3368 return 1;
3371 for (i = 0; key_val[i].keyname; i++) {
3372 size_t namelen = strlen(key_val[i].keyname);
3373 if (kbuf.dsize >= namelen && (
3374 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3375 TALLOC_CTX *mem_ctx;
3376 char *keystr;
3377 int ret;
3379 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3380 if (!keystr) {
3381 return 1;
3383 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3384 keystr[kbuf.dsize] = '\0';
3386 mem_ctx = talloc_init("validate_ctx");
3387 if (!mem_ctx) {
3388 SAFE_FREE(keystr);
3389 return 1;
3392 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3393 v_state);
3395 SAFE_FREE(keystr);
3396 talloc_destroy(mem_ctx);
3397 return ret;
3401 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3402 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3403 DEBUG(0,("data :\n"));
3404 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3405 v_state->unknown_key = true;
3406 v_state->success = false;
3407 return 1; /* terminate. */
3410 static void validate_panic(const char *const why)
3412 DEBUG(0,("validating cache: would panic %s\n", why ));
3413 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3414 exit(47);
3417 /***********************************************************************
3418 Try and validate every entry in the winbindd cache. If we fail here,
3419 delete the cache tdb and return non-zero.
3420 ***********************************************************************/
3422 int winbindd_validate_cache(void)
3424 int ret = -1;
3425 const char *tdb_path = lock_path("winbindd_cache.tdb");
3426 TDB_CONTEXT *tdb = NULL;
3428 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3429 smb_panic_fn = validate_panic;
3432 tdb = tdb_open_log(tdb_path,
3433 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3434 ( lp_winbind_offline_logon()
3435 ? TDB_DEFAULT
3436 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3437 O_RDWR|O_CREAT,
3438 0600);
3439 if (!tdb) {
3440 DEBUG(0, ("winbindd_validate_cache: "
3441 "error opening/initializing tdb\n"));
3442 goto done;
3444 tdb_close(tdb);
3446 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3448 if (ret != 0) {
3449 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3450 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3451 unlink(tdb_path);
3454 done:
3455 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3456 smb_panic_fn = smb_panic;
3457 return ret;
3460 /***********************************************************************
3461 Try and validate every entry in the winbindd cache.
3462 ***********************************************************************/
3464 int winbindd_validate_cache_nobackup(void)
3466 int ret = -1;
3467 const char *tdb_path = lock_path("winbindd_cache.tdb");
3469 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3470 smb_panic_fn = validate_panic;
3473 if (wcache == NULL || wcache->tdb == NULL) {
3474 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3475 } else {
3476 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3479 if (ret != 0) {
3480 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3481 "successful.\n"));
3484 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3485 "function\n"));
3486 smb_panic_fn = smb_panic;
3487 return ret;
3490 bool winbindd_cache_validate_and_initialize(void)
3492 close_winbindd_cache();
3494 if (lp_winbind_offline_logon()) {
3495 if (winbindd_validate_cache() < 0) {
3496 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3497 "could be restored.\n"));
3501 return initialize_winbindd_cache();
3504 /*********************************************************************
3505 ********************************************************************/
3507 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3508 struct winbindd_tdc_domain **domains,
3509 size_t *num_domains )
3511 struct winbindd_tdc_domain *list = NULL;
3512 size_t idx;
3513 int i;
3514 bool set_only = false;
3516 /* don't allow duplicates */
3518 idx = *num_domains;
3519 list = *domains;
3521 for ( i=0; i< (*num_domains); i++ ) {
3522 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3523 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3524 new_dom->name));
3525 idx = i;
3526 set_only = true;
3528 break;
3532 if ( !set_only ) {
3533 if ( !*domains ) {
3534 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3535 idx = 0;
3536 } else {
3537 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3538 struct winbindd_tdc_domain,
3539 (*num_domains)+1);
3540 idx = *num_domains;
3543 ZERO_STRUCT( list[idx] );
3546 if ( !list )
3547 return false;
3549 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3550 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3552 if ( !is_null_sid( &new_dom->sid ) )
3553 sid_copy( &list[idx].sid, &new_dom->sid );
3555 if ( new_dom->domain_flags != 0x0 )
3556 list[idx].trust_flags = new_dom->domain_flags;
3558 if ( new_dom->domain_type != 0x0 )
3559 list[idx].trust_type = new_dom->domain_type;
3561 if ( new_dom->domain_trust_attribs != 0x0 )
3562 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3564 if ( !set_only ) {
3565 *domains = list;
3566 *num_domains = idx + 1;
3569 return true;
3572 /*********************************************************************
3573 ********************************************************************/
3575 static TDB_DATA make_tdc_key( const char *domain_name )
3577 char *keystr = NULL;
3578 TDB_DATA key = { NULL, 0 };
3580 if ( !domain_name ) {
3581 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3582 return key;
3586 asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
3587 key = string_term_tdb_data(keystr);
3589 return key;
3592 /*********************************************************************
3593 ********************************************************************/
3595 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3596 size_t num_domains,
3597 unsigned char **buf )
3599 unsigned char *buffer = NULL;
3600 int len = 0;
3601 int buflen = 0;
3602 int i = 0;
3604 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3605 (int)num_domains));
3607 buflen = 0;
3609 again:
3610 len = 0;
3612 /* Store the number of array items first */
3613 len += tdb_pack( buffer+len, buflen-len, "d",
3614 num_domains );
3616 /* now pack each domain trust record */
3617 for ( i=0; i<num_domains; i++ ) {
3619 fstring tmp;
3621 if ( buflen > 0 ) {
3622 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3623 domains[i].domain_name,
3624 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3627 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3628 domains[i].domain_name,
3629 domains[i].dns_name,
3630 sid_to_fstring(tmp, &domains[i].sid),
3631 domains[i].trust_flags,
3632 domains[i].trust_attribs,
3633 domains[i].trust_type );
3636 if ( buflen < len ) {
3637 SAFE_FREE(buffer);
3638 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3639 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3640 buflen = -1;
3641 goto done;
3643 buflen = len;
3644 goto again;
3647 *buf = buffer;
3649 done:
3650 return buflen;
3653 /*********************************************************************
3654 ********************************************************************/
3656 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3657 struct winbindd_tdc_domain **domains )
3659 fstring domain_name, dns_name, sid_string;
3660 uint32 type, attribs, flags;
3661 int num_domains;
3662 int len = 0;
3663 int i;
3664 struct winbindd_tdc_domain *list = NULL;
3666 /* get the number of domains */
3667 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3668 if ( len == -1 ) {
3669 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3670 return 0;
3673 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3674 if ( !list ) {
3675 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3676 return 0;
3679 for ( i=0; i<num_domains; i++ ) {
3680 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3681 domain_name,
3682 dns_name,
3683 sid_string,
3684 &flags,
3685 &attribs,
3686 &type );
3688 if ( len == -1 ) {
3689 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3690 TALLOC_FREE( list );
3691 return 0;
3694 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3695 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3696 domain_name, dns_name, sid_string,
3697 flags, attribs, type));
3699 list[i].domain_name = talloc_strdup( list, domain_name );
3700 list[i].dns_name = talloc_strdup( list, dns_name );
3701 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
3702 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3703 domain_name));
3705 list[i].trust_flags = flags;
3706 list[i].trust_attribs = attribs;
3707 list[i].trust_type = type;
3710 *domains = list;
3712 return num_domains;
3715 /*********************************************************************
3716 ********************************************************************/
3718 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
3720 TDB_DATA key = make_tdc_key( lp_workgroup() );
3721 TDB_DATA data = { NULL, 0 };
3722 int ret;
3724 if ( !key.dptr )
3725 return false;
3727 /* See if we were asked to delete the cache entry */
3729 if ( !domains ) {
3730 ret = tdb_delete( wcache->tdb, key );
3731 goto done;
3734 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
3736 if ( !data.dptr ) {
3737 ret = -1;
3738 goto done;
3741 ret = tdb_store( wcache->tdb, key, data, 0 );
3743 done:
3744 SAFE_FREE( data.dptr );
3745 SAFE_FREE( key.dptr );
3747 return ( ret != -1 );
3750 /*********************************************************************
3751 ********************************************************************/
3753 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
3755 TDB_DATA key = make_tdc_key( lp_workgroup() );
3756 TDB_DATA data = { NULL, 0 };
3758 *domains = NULL;
3759 *num_domains = 0;
3761 if ( !key.dptr )
3762 return false;
3764 data = tdb_fetch( wcache->tdb, key );
3766 SAFE_FREE( key.dptr );
3768 if ( !data.dptr )
3769 return false;
3771 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
3773 SAFE_FREE( data.dptr );
3775 if ( !*domains )
3776 return false;
3778 return true;
3781 /*********************************************************************
3782 ********************************************************************/
3784 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
3786 struct winbindd_tdc_domain *dom_list = NULL;
3787 size_t num_domains = 0;
3788 bool ret = false;
3790 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
3791 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
3792 domain->name, domain->alt_name,
3793 sid_string_dbg(&domain->sid),
3794 domain->domain_flags,
3795 domain->domain_trust_attribs,
3796 domain->domain_type));
3798 if ( !init_wcache() ) {
3799 return false;
3802 /* fetch the list */
3804 wcache_tdc_fetch_list( &dom_list, &num_domains );
3806 /* add the new domain */
3808 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
3809 goto done;
3812 /* pack the domain */
3814 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
3815 goto done;
3818 /* Success */
3820 ret = true;
3821 done:
3822 TALLOC_FREE( dom_list );
3824 return ret;
3827 /*********************************************************************
3828 ********************************************************************/
3830 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
3832 struct winbindd_tdc_domain *dom_list = NULL;
3833 size_t num_domains = 0;
3834 int i;
3835 struct winbindd_tdc_domain *d = NULL;
3837 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
3839 if ( !init_wcache() ) {
3840 return false;
3843 /* fetch the list */
3845 wcache_tdc_fetch_list( &dom_list, &num_domains );
3847 for ( i=0; i<num_domains; i++ ) {
3848 if ( strequal(name, dom_list[i].domain_name) ||
3849 strequal(name, dom_list[i].dns_name) )
3851 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
3852 name));
3854 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
3855 if ( !d )
3856 break;
3858 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
3859 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
3860 sid_copy( &d->sid, &dom_list[i].sid );
3861 d->trust_flags = dom_list[i].trust_flags;
3862 d->trust_type = dom_list[i].trust_type;
3863 d->trust_attribs = dom_list[i].trust_attribs;
3865 break;
3869 TALLOC_FREE( dom_list );
3871 return d;
3875 /*********************************************************************
3876 ********************************************************************/
3878 void wcache_tdc_clear( void )
3880 if ( !init_wcache() )
3881 return;
3883 wcache_tdc_store_list( NULL, 0 );
3885 return;
3889 /*********************************************************************
3890 ********************************************************************/
3892 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
3893 NTSTATUS status,
3894 const DOM_SID *user_sid,
3895 const char *homedir,
3896 const char *shell,
3897 const char *gecos,
3898 uint32 gid)
3900 struct cache_entry *centry;
3901 fstring tmp;
3903 if ( (centry = centry_start(domain, status)) == NULL )
3904 return;
3906 centry_put_string( centry, homedir );
3907 centry_put_string( centry, shell );
3908 centry_put_string( centry, gecos );
3909 centry_put_uint32( centry, gid );
3911 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
3913 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
3915 centry_free(centry);
3918 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
3919 const DOM_SID *user_sid,
3920 TALLOC_CTX *ctx,
3921 ADS_STRUCT *ads, LDAPMessage *msg,
3922 char **homedir, char **shell, char **gecos,
3923 gid_t *p_gid)
3925 struct winbind_cache *cache = get_cache(domain);
3926 struct cache_entry *centry = NULL;
3927 NTSTATUS nt_status;
3928 fstring tmp;
3930 if (!cache->tdb)
3931 goto do_query;
3933 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
3934 sid_to_fstring(tmp, user_sid));
3936 if (!centry)
3937 goto do_query;
3939 *homedir = centry_string( centry, ctx );
3940 *shell = centry_string( centry, ctx );
3941 *gecos = centry_string( centry, ctx );
3942 *p_gid = centry_uint32( centry );
3944 centry_free(centry);
3946 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
3947 sid_string_dbg(user_sid)));
3949 return NT_STATUS_OK;
3951 do_query:
3953 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
3954 homedir, shell, gecos, p_gid );
3956 if ( NT_STATUS_IS_OK(nt_status) ) {
3957 wcache_save_user_pwinfo( domain, nt_status, user_sid,
3958 *homedir, *shell, *gecos, *p_gid );
3961 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
3962 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
3963 domain->name ));
3964 set_domain_offline( domain );
3967 return nt_status;
3971 /* the cache backend methods are exposed via this structure */
3972 struct winbindd_methods cache_methods = {
3973 true,
3974 query_user_list,
3975 enum_dom_groups,
3976 enum_local_groups,
3977 name_to_sid,
3978 sid_to_name,
3979 rids_to_names,
3980 query_user,
3981 lookup_usergroups,
3982 lookup_useraliases,
3983 lookup_groupmem,
3984 sequence_number,
3985 lockout_policy,
3986 password_policy,
3987 trusted_domains